sipp-3.6.1/0000775000175000017500000000000013730472333012061 5ustar walterwaltersipp-3.6.1/version.h0000644000175000017500000000006613730472205013715 0ustar walterwalter#define SIPP_VERSION VERSION #define VERSION "v3.6.1" sipp-3.6.1/CMakeLists.txt0000664000175000017500000001225513730472040014621 0ustar walterwaltercmake_minimum_required(VERSION 2.8) # set the project name project(SIPp) # specify the C++ standard set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) if(EXISTS ./gmock/CMakeLists.txt) add_subdirectory(gmock EXCLUDE_FROM_ALL) endif() include_directories( ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/gtest/include ${PROJECT_SOURCE_DIR}/gmock/include ${PROJECT_BINARY_DIR} "/usr/local/include") option(BUILD_STATIC "Build a statically-linked binary" OFF) option(USE_SSL "Build with SIPS support" OFF) option(USE_SCTP "Build with SCTP support" OFF) option(USE_PCAP "Build with PCAP playback support" OFF) option(USE_GSL "Build with improved statistical support" ON) file(GLOB all_SRCS "${PROJECT_SOURCE_DIR}/src/*.cpp" "${PROJECT_SOURCE_DIR}/src/*.c" ) include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake) include(${CMAKE_ROOT}/Modules/CheckSymbolExists.cmake) include(${CMAKE_ROOT}/Modules/CheckStructHasMember.cmake) CHECK_INCLUDE_FILE("endian.h" HAVE_ENDIAN_H) CHECK_INCLUDE_FILE("sys/endian.h" HAVE_SYS_ENDIAN_H) CHECK_INCLUDE_FILE("sys/epoll.h" HAVE_EPOLL) CHECK_STRUCT_HAS_MEMBER("struct udphdr" uh_sport "sys/types.h;netinet/udp.h" HAVE_UDP_UH_PREFIX) CHECK_SYMBOL_EXISTS(le16toh "endian.h" HAVE_DECL_LE16TOH) CHECK_SYMBOL_EXISTS(le16toh "sys/endian.h" HAVE_DECL_LE16TOH_BSD) configure_file("${PROJECT_SOURCE_DIR}/include/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ) list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/sipp_unittest.cpp") list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/sipp.cpp") if(NOT USE_SSL) list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/sslsocket.cpp") endif(NOT USE_SSL) if(NOT USE_PCAP) list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/prepare_pcap.c") list(REMOVE_ITEM all_SRCS "${PROJECT_SOURCE_DIR}/src/send_packets.c") endif(NOT USE_PCAP) if(USE_SSL) add_definitions("-DUSE_TLS -DUSE_OPENSSL") endif() if(USE_PCAP) add_definitions("-DPCAPPLAY") endif(USE_PCAP) if(USE_GSL) find_library(GSL_LIBRARY gsl) if(GSL_LIBRARY) add_definitions("-DHAVE_GSL") endif(GSL_LIBRARY) endif(USE_GSL) if(USE_SCTP) add_definitions("-DUSE_SCTP") endif(USE_SCTP) add_definitions("-DRTP_STREAM") # add the executable link_directories("/usr/local/lib") add_executable(sipp ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp.cpp") add_executable(sipp_unittest EXCLUDE_FROM_ALL ${all_SRCS} "${PROJECT_SOURCE_DIR}/src/sipp_unittest.cpp") target_compile_definitions(sipp_unittest PUBLIC "-DGTEST") # add version find_package(Git) file(WRITE ${PROJECT_SOURCE_DIR}/include/version.cmake "execute_process( COMMAND ${GIT_EXECUTABLE} describe --tags --always --first-parent WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} OUTPUT_VARIABLE VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) configure_file(\${SRC} \${DST} @ONLY) ") add_custom_target( version if test -d .git \; then ${CMAKE_COMMAND} -D SRC=${PROJECT_SOURCE_DIR}/include/version.h.in -D DST=${PROJECT_BINARY_DIR}/version.h -P ${PROJECT_SOURCE_DIR}/include/version.cmake \; fi ) add_dependencies(sipp version) add_dependencies(sipp_unittest version) if(BUILD_STATIC) set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static") endif(BUILD_STATIC) find_package(PkgConfig QUIET) # import pkg_check_modules() and friends if(PKG_CONFIG_FOUND) pkg_search_module(CURSES_LIBRARY ncursesw cursesw ncurses curses) if(CURSES_LIBRARY_FOUND) set(CURSES_LIBRARY ${CURSES_LIBRARY_LIBRARIES}) endif() pkg_search_module(TINFO_LIBRARY tinfo) if(TINFO_LIBRARY_FOUND) set(TINFO_LIBRARY ${TINFO_LIBRARY_LIBRARIES}) endif() endif() if(NOT CURSES_LIBRARY) find_library(CURSES_LIBRARY NAMES ncursesw cursesw ncurses curses) endif() if(NOT TINFO_LIBRARY) find_library(TINFO_LIBRARY NAMES tinfo) endif() if(CURSES_LIBRARY) target_link_libraries(sipp dl ${CURSES_LIBRARY} pthread) target_link_libraries(sipp_unittest dl ${CURSES_LIBRARY} pthread gtest gmock) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(NOT BUILD_STATIC) if(TINFO_LIBRARY) target_link_libraries(sipp dl ${TINFO_LIBRARY}) target_link_libraries(sipp_unittest dl ${TINFO_LIBRARY}) else() message(FATAL_ERROR "libtinfo was not found -- please install package") endif(TINFO_LIBRARY) endif(NOT BUILD_STATIC) endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") else() message(FATAL_ERROR "libcurses / libncurses was not found; please install devel package") endif() find_library(RT_LIBRARY NAMES rt) if(RT_LIBRARY) target_link_libraries(sipp ${RT_LIBRARY}) target_link_libraries(sipp_unittest ${RT_LIBRARY}) endif() if(USE_GSL AND GSL_LIBRARY) target_link_libraries(sipp gsl gslcblas) target_link_libraries(sipp_unittest gsl gslcblas) endif(USE_GSL AND GSL_LIBRARY) if(USE_SSL) target_link_libraries(sipp crypto ssl) target_link_libraries(sipp_unittest crypto ssl) endif(USE_SSL) if(USE_PCAP) target_link_libraries(sipp pcap) target_link_libraries(sipp_unittest pcap) endif(USE_PCAP) if(USE_SCTP) find_library(SCTP_LIBRARY sctp) if(SCTP_LIBRARY) target_link_libraries(sipp ${SCTP_LIBRARY}) target_link_libraries(sipp_unittest ${SCTP_LIBRARY}) endif() endif() install(TARGETS sipp DESTINATION bin) sipp-3.6.1/docker/0000775000175000017500000000000013730472040013323 5ustar walterwaltersipp-3.6.1/docker/Dockerfile0000664000175000017500000000032713730472040015317 0ustar walterwalterFROM alpine:3.10 RUN apk add --no-cache binutils make cmake gcc g++ ncurses-static libpcap-dev ncurses-dev gsl-dev CMD cd /src && rm -f CMakeCache.txt && cmake . -DBUILD_STATIC=1 -DUSE_PCAP=1 -DUSE_GSL=1 && make sipp-3.6.1/build.sh0000755000175000017500000000167313730472040013517 0ustar walterwalter#!/bin/sh set -e # abort on error MAKE=`which gmake make 2>/dev/null | head -n1` # prefer GNU make test -z "$MAKE" && echo "No (g)make found" >&2 && exit 1 CPUCOUNT=$(nproc --all 2>/dev/null || echo 1) MAKEFLAGS="-j$CPUCOUNT" if test -z "$*"; then echo "build.sh: Please specify configure options," \ "--none for defaults, or --full for all" >&2 exit 1 elif test "$*" = "--help" || test "$*" = "-h"; then echo "build.sh: Please specify configure options," \ "--none for defaults, or --full for all" >&2 exit 1 fi if test "$*" = "--none"; then cmake . -DUSE_GSL= elif test "$*" = "--common"; then cmake . -DUSE_GSL=1 -DUSE_PCAP=1 -DUSE_SSL= -DUSE_SCTP= elif test "$*" = "--full"; then cmake . -DUSE_GSL=1 -DUSE_PCAP=1 -DUSE_SSL=1 -DUSE_SCTP=1 else cmake . "$@" fi # For git checkout, run unit tests. if test -e gtest/.git; then "$MAKE" $MAKEFLAGS sipp_unittest ./sipp_unittest fi "$MAKE" $MAKEFLAGS sipp-3.6.1/cpplint.py0000664000175000017500000046622113730472040014112 0ustar walterwalter#!/usr/bin/python # # Copyright (c) 2009 Google 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: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * 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. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # 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 # OWNER 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. # Here are some issues that I've had people identify in my code during reviews, # that I think are possible to flag automatically in a lint tool. If these were # caught by lint, it would save time both for myself and that of my reviewers. # Most likely, some of these are beyond the scope of the current lint framework, # but I think it is valuable to retain these wish-list items even if they cannot # be immediately implemented. # # Suggestions # ----------- # - Check for no 'explicit' for multi-arg ctor # - Check for boolean assign RHS in parens # - Check for ctor initializer-list colon position and spacing # - Check that if there's a ctor, there should be a dtor # - Check accessors that return non-pointer member variables are # declared const # - Check accessors that return non-const pointer member vars are # *not* declared const # - Check for using public includes for testing # - Check for spaces between brackets in one-line inline method # - Check for no assert() # - Check for spaces surrounding operators # - Check for 0 in pointer context (should be NULL) # - Check for 0 in char context (should be '\0') # - Check for camel-case method name conventions for methods # that are not simple inline getters and setters # - Do not indent namespace contents # - Avoid inlining non-trivial constructors in header files # - Check for old-school (void) cast for call-sites of functions # ignored return value # - Check gUnit usage of anonymous namespace # - Check for class declaration order (typedefs, consts, enums, # ctor(s?), dtor, friend declarations, methods, member vars) # """Does google-lint on c++ files. The goal of this script is to identify places in the code that *may* be in non-compliance with google style. It does not attempt to fix up these problems -- the point is to educate. It does also not attempt to find all problems, or to ensure that everything it does find is legitimately a problem. In particular, we can get very confused by /* and // inside strings! We do a small hack, which is to ignore //'s with "'s after them on the same line, but it is far from perfect (in either direction). """ import codecs import copy import getopt import math # for log import os import re import sre_compile import string import sys import unicodedata _USAGE = """ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [file] ... The style guidelines this tries to follow are those in http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml Every problem is given a confidence score from 1-5, with 5 meaning we are certain of the problem, and 1 meaning it could be a legitimate construct. This will miss some errors, and is not a substitute for a code review. To suppress false-positive errors of a certain category, add a 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) suppresses errors of all categories on that line. The files passed in will be linted; at least one file must be provided. Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. Flags: output=vs7 By default, the output is formatted to ease emacs parsing. Visual Studio compatible output (vs7) may also be used. Other formats are unsupported. verbose=# Specify a number 0-5 to restrict errors to certain verbosity levels. filter=-x,+y,... Specify a comma-separated list of category-filters to apply: only error messages whose category names pass the filters will be printed. (Category names are printed with the message and look like "[whitespace/indent]".) Filters are evaluated left to right. "-FOO" and "FOO" means "do not print categories that start with FOO". "+FOO" means "do print categories that start with FOO". Examples: --filter=-whitespace,+whitespace/braces --filter=whitespace,runtime/printf,+runtime/printf_format --filter=-,+build/include_what_you_use To see a list of all the categories used in cpplint, pass no arg: --filter= counting=total|toplevel|detailed The total number of errors found is always printed. If 'toplevel' is provided, then the count of errors in each of the top-level categories like 'build' and 'whitespace' will also be printed. If 'detailed' is provided, then a count is provided for each category like 'build/class'. root=subdir The root directory used for deriving header guard CPP variable. By default, the header guard CPP variable is calculated as the relative path to the directory that contains .git, .hg, or .svn. When this flag is specified, the relative path is calculated from the specified directory. If the specified directory does not exist, this flag is ignored. Examples: Assuing that src/.git exists, the header guard CPP variables for src/chrome/browser/ui/browser.h are: No flag => CHROME_BROWSER_UI_BROWSER_H_ --root=chrome => BROWSER_UI_BROWSER_H_ --root=chrome/browser => UI_BROWSER_H_ """ # We categorize each error message we print. Here are the categories. # We want an explicit list so we can list them all in cpplint --filter=. # If you add a new error message with a new category, add it to the list # here! cpplint_unittest.py should tell you if you forget to do this. # \ used for clearer layout -- pylint: disable-msg=C6013 _ERROR_CATEGORIES = [ 'build/class', 'build/deprecated', 'build/endif_comment', 'build/explicit_make_pair', 'build/forward_decl', 'build/header_guard', 'build/include', 'build/include_alpha', 'build/include_order', 'build/include_what_you_use', 'build/namespaces', 'build/printf_format', 'build/storage_class', 'legal/copyright', 'readability/alt_tokens', 'readability/braces', 'readability/casting', 'readability/check', 'readability/constructors', 'readability/fn_size', 'readability/function', 'readability/multiline_comment', 'readability/multiline_string', 'readability/namespace', 'readability/nolint', 'readability/streams', 'readability/todo', 'readability/utf8', 'runtime/arrays', 'runtime/casting', 'runtime/explicit', 'runtime/int', 'runtime/init', 'runtime/invalid_increment', 'runtime/member_string_references', 'runtime/memset', 'runtime/operator', 'runtime/printf', 'runtime/printf_format', 'runtime/references', 'runtime/rtti', 'runtime/sizeof', 'runtime/string', 'runtime/threadsafe_fn', 'whitespace/blank_line', 'whitespace/braces', 'whitespace/comma', 'whitespace/comments', 'whitespace/empty_loop_body', 'whitespace/end_of_line', 'whitespace/ending_newline', 'whitespace/forcolon', 'whitespace/indent', 'whitespace/labels', 'whitespace/line_length', 'whitespace/newline', 'whitespace/operators', 'whitespace/parens', 'whitespace/semicolon', 'whitespace/tab', 'whitespace/todo' ] # The default state of the category filter. This is overrided by the --filter= # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. _DEFAULT_FILTERS = ['-build/include_alpha'] # We used to check for high-bit characters, but after much discussion we # decided those were OK, as long as they were in UTF-8 and didn't represent # hard-coded international strings, which belong in a separate i18n file. # Headers that we consider STL headers. _STL_HEADERS = frozenset([ 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', 'utility', 'vector', 'vector.h', ]) # Non-STL C++ system headers. _CPP_HEADERS = frozenset([ 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', ]) # Assertion macros. These are defined in base/logging.h and # testing/base/gunit.h. Note that the _M versions need to come first # for substring matching to work. _CHECK_MACROS = [ 'DCHECK', 'CHECK', 'EXPECT_TRUE_M', 'EXPECT_TRUE', 'ASSERT_TRUE_M', 'ASSERT_TRUE', 'EXPECT_FALSE_M', 'EXPECT_FALSE', 'ASSERT_FALSE_M', 'ASSERT_FALSE', ] # Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE _CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) for op, replacement in [('==', 'EQ'), ('!=', 'NE'), ('>=', 'GE'), ('>', 'GT'), ('<=', 'LE'), ('<', 'LT')]: _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), ('>=', 'LT'), ('>', 'LE'), ('<=', 'GT'), ('<', 'GE')]: _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement # Alternative tokens and their replacements. For full list, see section 2.5 # Alternative tokens [lex.digraph] in the C++ standard. # # Digraphs (such as '%:') are not included here since it's a mess to # match those on a word boundary. _ALT_TOKEN_REPLACEMENT = { 'and': '&&', 'bitor': '|', 'or': '||', 'xor': '^', 'compl': '~', 'bitand': '&', 'and_eq': '&=', 'or_eq': '|=', 'xor_eq': '^=', 'not': '!', 'not_eq': '!=' } # Compile regular expression that matches all the above keywords. The "[ =()]" # bit is meant to avoid matching these keywords outside of boolean expressions. # # False positives include C-style multi-line comments (http://go/nsiut ) # and multi-line strings (http://go/beujw ), but those have always been # troublesome for cpplint. _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') # These constants define types of headers for use with # _IncludeState.CheckNextIncludeOrder(). _C_SYS_HEADER = 1 _CPP_SYS_HEADER = 2 _LIKELY_MY_HEADER = 3 _POSSIBLE_MY_HEADER = 4 _OTHER_HEADER = 5 # These constants define the current inline assembly state _NO_ASM = 0 # Outside of inline assembly block _INSIDE_ASM = 1 # Inside inline assembly block _END_ASM = 2 # Last line of inline assembly block _BLOCK_ASM = 3 # The whole block is an inline assembly block # Match start of assembly blocks _MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' r'(?:\s+(volatile|__volatile__))?' r'\s*[{(]') _regexp_compile_cache = {} # Finds occurrences of NOLINT or NOLINT(...). _RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') # {str, set(int)}: a map from error categories to sets of linenumbers # on which those errors are expected and should be suppressed. _error_suppressions = {} # The root directory used for deriving header guard CPP variable. # This is set by --root flag. _root = None def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of error-suppressions. Parses any NOLINT comments on the current line, updating the global error_suppressions store. Reports an error if the NOLINT comment was malformed. Args: filename: str, the name of the input file. raw_line: str, the line of input text, with comments. linenum: int, the number of the current line. error: function, an error handler. """ # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). matched = _RE_SUPPRESSION.search(raw_line) if matched: category = matched.group(1) if category in (None, '(*)'): # => "suppress all" _error_suppressions.setdefault(None, set()).add(linenum) else: if category.startswith('(') and category.endswith(')'): category = category[1:-1] if category in _ERROR_CATEGORIES: _error_suppressions.setdefault(category, set()).add(linenum) else: error(filename, linenum, 'readability/nolint', 5, 'Unknown NOLINT error category: %s' % category) def ResetNolintSuppressions(): "Resets the set of NOLINT suppressions to empty." _error_suppressions.clear() def IsErrorSuppressedByNolint(category, linenum): """Returns true if the specified error category is suppressed on this line. Consults the global error_suppressions map populated by ParseNolintSuppressions/ResetNolintSuppressions. Args: category: str, the category of the error. linenum: int, the current line number. Returns: bool, True iff the error should be suppressed due to a NOLINT comment. """ return (linenum in _error_suppressions.get(category, set()) or linenum in _error_suppressions.get(None, set())) def Match(pattern, s): """Matches the string with the pattern, caching the compiled regexp.""" # The regexp compilation caching is inlined in both Match and Search for # performance reasons; factoring it out into a separate function turns out # to be noticeably expensive. if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].match(s) def Search(pattern, s): """Searches the string for the pattern, caching the compiled regexp.""" if not pattern in _regexp_compile_cache: _regexp_compile_cache[pattern] = sre_compile.compile(pattern) return _regexp_compile_cache[pattern].search(s) class _IncludeState(dict): """Tracks line numbers for includes, and the order in which includes appear. As a dict, an _IncludeState object serves as a mapping between include filename and line number on which that file was included. Call CheckNextIncludeOrder() once for each header in the file, passing in the type constants defined above. Calls in an illegal order will raise an _IncludeError with an appropriate error message. """ # self._section will move monotonically through this set. If it ever # needs to move backwards, CheckNextIncludeOrder will raise an error. _INITIAL_SECTION = 0 _MY_H_SECTION = 1 _C_SECTION = 2 _CPP_SECTION = 3 _OTHER_H_SECTION = 4 _TYPE_NAMES = { _C_SYS_HEADER: 'C system header', _CPP_SYS_HEADER: 'C++ system header', _LIKELY_MY_HEADER: 'header this file implements', _POSSIBLE_MY_HEADER: 'header this file may implement', _OTHER_HEADER: 'other header', } _SECTION_NAMES = { _INITIAL_SECTION: "... nothing. (This can't be an error.)", _MY_H_SECTION: 'a header this file implements', _C_SECTION: 'C system header', _CPP_SECTION: 'C++ system header', _OTHER_H_SECTION: 'other header', } def __init__(self): dict.__init__(self) # The name of the current section. self._section = self._INITIAL_SECTION # The path of last found header. self._last_header = '' def CanonicalizeAlphabeticalOrder(self, header_path): """Returns a path canonicalized for alphabetical comparison. - replaces "-" with "_" so they both cmp the same. - removes '-inl' since we don't require them to be after the main header. - lowercase everything, just in case. Args: header_path: Path to be canonicalized. Returns: Canonicalized path. """ return header_path.replace('-inl.h', '.h').replace('-', '_').lower() def IsInAlphabeticalOrder(self, header_path): """Check if a header is in alphabetical order with the previous header. Args: header_path: Header to be checked. Returns: Returns true if the header is in alphabetical order. """ canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) if self._last_header > canonical_header: return False self._last_header = canonical_header return True def CheckNextIncludeOrder(self, header_type): """Returns a non-empty error message if the next header is out of order. This function also updates the internal state to be ready to check the next include. Args: header_type: One of the _XXX_HEADER constants defined above. Returns: The empty string if the header is in the right order, or an error message describing what's wrong. """ error_message = ('Found %s after %s' % (self._TYPE_NAMES[header_type], self._SECTION_NAMES[self._section])) last_section = self._section if header_type == _C_SYS_HEADER: if self._section <= self._C_SECTION: self._section = self._C_SECTION else: self._last_header = '' return error_message elif header_type == _CPP_SYS_HEADER: if self._section <= self._CPP_SECTION: self._section = self._CPP_SECTION else: self._last_header = '' return error_message elif header_type == _LIKELY_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: self._section = self._OTHER_H_SECTION elif header_type == _POSSIBLE_MY_HEADER: if self._section <= self._MY_H_SECTION: self._section = self._MY_H_SECTION else: # This will always be the fallback because we're not sure # enough that the header is associated with this file. self._section = self._OTHER_H_SECTION else: assert header_type == _OTHER_HEADER self._section = self._OTHER_H_SECTION if last_section != self._section: self._last_header = '' return '' class _CppLintState(object): """Maintains module-wide state..""" def __init__(self): self.verbose_level = 1 # global setting. self.error_count = 0 # global count of reported errors # filters to apply when emitting error messages self.filters = _DEFAULT_FILTERS[:] self.counting = 'total' # In what way are we counting errors? self.errors_by_category = {} # string to int dict storing error counts # output format: # "emacs" - format that emacs can parse (default) # "vs7" - format that Microsoft Visual Studio 7 can parse self.output_format = 'emacs' def SetOutputFormat(self, output_format): """Sets the output format for errors.""" self.output_format = output_format def SetVerboseLevel(self, level): """Sets the module's verbosity, and returns the previous setting.""" last_verbose_level = self.verbose_level self.verbose_level = level return last_verbose_level def SetCountingStyle(self, counting_style): """Sets the module's counting options.""" self.counting = counting_style def SetFilters(self, filters): """Sets the error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "+whitespace/indent"). Each filter should start with + or -; else we die. Raises: ValueError: The comma-separated filters did not all start with '+' or '-'. E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" """ # Default filters always have less priority than the flag ones. self.filters = _DEFAULT_FILTERS[:] for filt in filters.split(','): clean_filt = filt.strip() if clean_filt: self.filters.append(clean_filt) for filt in self.filters: if not (filt.startswith('+') or filt.startswith('-')): raise ValueError('Every filter in --filters must start with + or -' ' (%s does not)' % filt) def ResetErrorCounts(self): """Sets the module's error statistic back to zero.""" self.error_count = 0 self.errors_by_category = {} def IncrementErrorCount(self, category): """Bumps the module's error statistic.""" self.error_count += 1 if self.counting in ('toplevel', 'detailed'): if self.counting != 'detailed': category = category.split('/')[0] if category not in self.errors_by_category: self.errors_by_category[category] = 0 self.errors_by_category[category] += 1 def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" for category, count in self.errors_by_category.iteritems(): sys.stderr.write('Category \'%s\' errors found: %d\n' % (category, count)) sys.stderr.write('Total errors found: %d\n' % self.error_count) _cpplint_state = _CppLintState() def _OutputFormat(): """Gets the module's output format.""" return _cpplint_state.output_format def _SetOutputFormat(output_format): """Sets the module's output format.""" _cpplint_state.SetOutputFormat(output_format) def _VerboseLevel(): """Returns the module's verbosity setting.""" return _cpplint_state.verbose_level def _SetVerboseLevel(level): """Sets the module's verbosity, and returns the previous setting.""" return _cpplint_state.SetVerboseLevel(level) def _SetCountingStyle(level): """Sets the module's counting options.""" _cpplint_state.SetCountingStyle(level) def _Filters(): """Returns the module's list of output filters, as a list.""" return _cpplint_state.filters def _SetFilters(filters): """Sets the module's error-message filters. These filters are applied when deciding whether to emit a given error message. Args: filters: A string of comma-separated filters (eg "whitespace/indent"). Each filter should start with + or -; else we die. """ _cpplint_state.SetFilters(filters) class _FunctionState(object): """Tracks current function name and the number of lines in its body.""" _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. def __init__(self): self.in_a_function = False self.lines_in_function = 0 self.current_function = '' def Begin(self, function_name): """Start analyzing function body. Args: function_name: The name of the function being tracked. """ self.in_a_function = True self.lines_in_function = 0 self.current_function = function_name def Count(self): """Count line in current function body.""" if self.in_a_function: self.lines_in_function += 1 def Check(self, error, filename, linenum): """Report if too many lines in function body. Args: error: The function to call with any errors found. filename: The name of the current file. linenum: The number of the line to check. """ if Match(r'T(EST|est)', self.current_function): base_trigger = self._TEST_TRIGGER else: base_trigger = self._NORMAL_TRIGGER trigger = base_trigger * 2**_VerboseLevel() if self.lines_in_function > trigger: error_level = int(math.log(self.lines_in_function / base_trigger, 2)) # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... if error_level > 5: error_level = 5 error(filename, linenum, 'readability/fn_size', error_level, 'Small and focused functions are preferred:' ' %s has %d non-comment lines' ' (error triggered by exceeding %d lines).' % ( self.current_function, self.lines_in_function, trigger)) def End(self): """Stop analyzing function body.""" self.in_a_function = False class _IncludeError(Exception): """Indicates a problem with the include order in a file.""" pass class FileInfo: """Provides utility functions for filenames. FileInfo provides easy access to the components of a file's path relative to the project root. """ def __init__(self, filename): self._filename = filename def FullName(self): """Make Windows paths like Unix.""" return os.path.abspath(self._filename).replace('\\', '/') def RepositoryName(self): """FullName after removing the local path to the repository. If we have a real absolute path name here we can try to do something smart: detecting the root of the checkout and truncating /path/to/checkout from the name so that we get header guards that don't include things like "C:\Documents and Settings\..." or "/home/username/..." in them and thus people on different computers who have checked the source out to different locations won't see bogus errors. """ fullname = self.FullName() if os.path.exists(fullname): project_dir = os.path.dirname(fullname) if os.path.exists(os.path.join(project_dir, ".svn")): # If there's a .svn file in the current directory, we recursively look # up the directory tree for the top of the SVN checkout root_dir = project_dir one_up_dir = os.path.dirname(root_dir) while os.path.exists(os.path.join(one_up_dir, ".svn")): root_dir = os.path.dirname(root_dir) one_up_dir = os.path.dirname(one_up_dir) prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by # searching up from the current path. root_dir = os.path.dirname(fullname) while (root_dir != os.path.dirname(root_dir) and not os.path.exists(os.path.join(root_dir, ".git")) and not os.path.exists(os.path.join(root_dir, ".hg")) and not os.path.exists(os.path.join(root_dir, ".svn"))): root_dir = os.path.dirname(root_dir) if (os.path.exists(os.path.join(root_dir, ".git")) or os.path.exists(os.path.join(root_dir, ".hg")) or os.path.exists(os.path.join(root_dir, ".svn"))): prefix = os.path.commonprefix([root_dir, project_dir]) return fullname[len(prefix) + 1:] # Don't know what to do; header guard warnings may be wrong... return fullname def Split(self): """Splits the file into the directory, basename, and extension. For 'chrome/browser/browser.cc', Split() would return ('chrome/browser', 'browser', '.cc') Returns: A tuple of (directory, basename, extension). """ googlename = self.RepositoryName() project, rest = os.path.split(googlename) return (project,) + os.path.splitext(rest) def BaseName(self): """File base name - text after the final slash, before the final period.""" return self.Split()[1] def Extension(self): """File extension - text following the final period.""" return self.Split()[2] def NoExtension(self): """File has no source file extension.""" return '/'.join(self.Split()[0:2]) def IsSource(self): """File has a source file extension.""" return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') def _ShouldPrintError(category, confidence, linenum): """If confidence >= verbose, category passes filter and is not suppressed.""" # There are three ways we might decide not to print an error message: # a "NOLINT(category)" comment appears in the source, # the verbosity level isn't high enough, or the filters filter it out. if IsErrorSuppressedByNolint(category, linenum): return False if confidence < _cpplint_state.verbose_level: return False is_filtered = False for one_filter in _Filters(): if one_filter.startswith('-'): if category.startswith(one_filter[1:]): is_filtered = True elif one_filter.startswith('+'): if category.startswith(one_filter[1:]): is_filtered = False else: assert False # should have been checked for in SetFilter. if is_filtered: return False return True def Error(filename, linenum, category, confidence, message): """Logs the fact we've found a lint error. We log where the error was found, and also our confidence in the error, that is, how certain we are this is a legitimate style regression, and not a misidentification or a use that's sometimes justified. False positives can be suppressed by the use of "cpplint(category)" comments on the offending line. These are parsed into _error_suppressions. Args: filename: The name of the file containing the error. linenum: The number of the line containing the error. category: A string used to describe the "category" this bug falls under: "whitespace", say, or "runtime". Categories may have a hierarchy separated by slashes: "whitespace/indent". confidence: A number from 1-5 representing a confidence score for the error, with 5 meaning that we are certain of the problem, and 1 meaning that it could be a legitimate construct. message: The error message. """ if _ShouldPrintError(category, confidence, linenum): _cpplint_state.IncrementErrorCount(category) if _cpplint_state.output_format == 'vs7': sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) elif _cpplint_state.output_format == 'eclipse': sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) else: sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) # Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. _RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') # Matches strings. Escape codes should already be removed by ESCAPES. _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') # Matches characters. Escape codes should already be removed by ESCAPES. _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") # Matches multi-line C++ comments. # This RE is a little bit more complicated than one might expect, because we # have to take care of space removals tools so we can handle comments inside # statements better. # The current rule is: We only clear spaces from both sides when we're at the # end of the line. Otherwise, we try to remove spaces from the right side, # if this doesn't work we try on left side but only if there's a non-character # on the right. _RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( r"""(\s*/\*.*\*/\s*$| /\*.*\*/\s+| \s+/\*.*\*/(?=\W)| /\*.*\*/)""", re.VERBOSE) def IsCppString(line): """Does line terminate so, that the next symbol is in string constant. This function does not consider single-line nor multi-line comments. Args: line: is a partial line of code starting from the 0..n. Returns: True, if next character appended to 'line' is inside a string constant. """ line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 def FindNextMultiLineCommentStart(lines, lineix): """Find the beginning marker for a multiline comment.""" while lineix < len(lines): if lines[lineix].strip().startswith('/*'): # Only return this marker if the comment goes beyond this line if lines[lineix].strip().find('*/', 2) < 0: return lineix lineix += 1 return len(lines) def FindNextMultiLineCommentEnd(lines, lineix): """We are inside a comment, find the end marker.""" while lineix < len(lines): if lines[lineix].strip().endswith('*/'): return lineix lineix += 1 return len(lines) def RemoveMultiLineCommentsFromRange(lines, begin, end): """Clears a range of lines for multi-line comments.""" # Having // dummy comments makes the lines non-empty, so we will not get # unnecessary blank line warnings later in the code. for i in range(begin, end): lines[i] = '// dummy' def RemoveMultiLineComments(filename, lines, error): """Removes multiline (c-style) comments from lines.""" lineix = 0 while lineix < len(lines): lineix_begin = FindNextMultiLineCommentStart(lines, lineix) if lineix_begin >= len(lines): return lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) if lineix_end >= len(lines): error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, 'Could not find end of multi-line comment') return RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) lineix = lineix_end + 1 def CleanseComments(line): """Removes //-comments and single-line C-style /* */ comments. Args: line: A line of C++ source. Returns: The line with single-line comments removed. """ commentpos = line.find('//') if commentpos != -1 and not IsCppString(line[:commentpos]): line = line[:commentpos].rstrip() # get rid of /* ... */ return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) class CleansedLines(object): """Holds 3 copies of all lines with different preprocessing applied to them. 1) elided member contains lines without strings and comments, 2) lines member contains lines without comments, and 3) raw_lines member contains all the lines without processing. All these three members are of , and of the same length. """ def __init__(self, lines): self.elided = [] self.lines = [] self.raw_lines = lines self.num_lines = len(lines) for linenum in range(len(lines)): self.lines.append(CleanseComments(lines[linenum])) elided = self._CollapseStrings(lines[linenum]) self.elided.append(CleanseComments(elided)) def NumLines(self): """Returns the number of lines represented.""" return self.num_lines @staticmethod def _CollapseStrings(elided): """Collapses strings and chars on a line to simple "" or '' blocks. We nix strings first so we're not fooled by text like '"http://"' Args: elided: The line being processed. Returns: The line with collapsed strings. """ if not _RE_PATTERN_INCLUDE.match(elided): # Remove escaped characters first to make quote/single quote collapsing # basic. Things that look like escaped characters shouldn't occur # outside of strings and chars. elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) return elided def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): """Find the position just after the matching endchar. Args: line: a CleansedLines line. startpos: start searching at this position. depth: nesting level at startpos. startchar: expression opening character. endchar: expression closing character. Returns: Index just after endchar. """ for i in xrange(startpos, len(line)): if line[i] == startchar: depth += 1 elif line[i] == endchar: depth -= 1 if depth == 0: return i + 1 return -1 def CloseExpression(clean_lines, linenum, pos): """If input points to ( or { or [, finds the position that closes it. If lines[linenum][pos] points to a '(' or '{' or '[', finds the linenum/pos that correspond to the closing of the expression. Args: clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. pos: A position on the line. Returns: A tuple (line, linenum, pos) pointer *past* the closing brace, or (line, len(lines), -1) if we never find a close. Note we ignore strings and comments when matching; and the line we return is the 'cleansed' line at linenum. """ line = clean_lines.elided[linenum] startchar = line[pos] if startchar not in '({[': return (line, clean_lines.NumLines(), -1) if startchar == '(': endchar = ')' if startchar == '[': endchar = ']' if startchar == '{': endchar = '}' # Check first line end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) if end_pos > -1: return (line, linenum, end_pos) tail = line[pos:] num_open = tail.count(startchar) - tail.count(endchar) while linenum < clean_lines.NumLines() - 1: linenum += 1 line = clean_lines.elided[linenum] delta = line.count(startchar) - line.count(endchar) if num_open + delta <= 0: return (line, linenum, FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) num_open += delta # Did not find endchar before end of file, give up return (line, clean_lines.NumLines(), -1) def CheckForCopyright(filename, lines, error): """Logs an error if no Copyright message appears at the top of the file.""" # We'll say it should occur by line 10. Don't forget there's a # dummy line at the front. for line in xrange(1, min(len(lines), 11)): if re.search(r'Copyright', lines[line], re.I): break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, 'No copyright message found. ' 'You should have a line: "Copyright [year] "') def GetHeaderGuardCPPVariable(filename): """Returns the CPP variable that should be used as a header guard. Args: filename: The name of a C++ header file. Returns: The CPP variable that should be used as a header guard in the named file. """ # Restores original filename in case that cpplint is invoked from Emacs's # flymake. filename = re.sub(r'_flymake\.h$', '.h', filename) filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) fileinfo = FileInfo(filename) file_path_from_root = fileinfo.RepositoryName() if _root: file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' def CheckForHeaderGuard(filename, lines, error): """Checks that the file contains a header guard. Logs an error if no #ifndef header guard is present. For other headers, checks that the full pathname is used. Args: filename: The name of the C++ header file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ cppvar = GetHeaderGuardCPPVariable(filename) ifndef = None ifndef_linenum = 0 define = None endif = None endif_linenum = 0 for linenum, line in enumerate(lines): linesplit = line.split() if len(linesplit) >= 2: # find the first occurrence of #ifndef and #define, save arg if not ifndef and linesplit[0] == '#ifndef': # set ifndef to the header guard presented on the #ifndef line. ifndef = linesplit[1] ifndef_linenum = linenum if not define and linesplit[0] == '#define': define = linesplit[1] # find the last occurrence of #endif, save entire line if line.startswith('#endif'): endif = line endif_linenum = linenum if not ifndef: error(filename, 0, 'build/header_guard', 5, 'No #ifndef header guard found, suggested CPP variable is: %s' % cppvar) return if not define: error(filename, 0, 'build/header_guard', 5, 'No #define header guard found, suggested CPP variable is: %s' % cppvar) return # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ # for backward compatibility. if ifndef != cppvar: error_level = 0 if ifndef != cppvar + '_': error_level = 5 ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, error) error(filename, ifndef_linenum, 'build/header_guard', error_level, '#ifndef header guard has wrong style, please use: %s' % cppvar) if define != ifndef: error(filename, 0, 'build/header_guard', 5, '#ifndef and #define don\'t match, suggested CPP variable is: %s' % cppvar) return if endif != ('#endif // %s' % cppvar): error_level = 0 if endif != ('#endif // %s' % (cppvar + '_')): error_level = 5 ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, error) error(filename, endif_linenum, 'build/header_guard', error_level, '#endif line should be "#endif // %s"' % cppvar) def CheckForUnicodeReplacementCharacters(filename, lines, error): """Logs an error for each line containing Unicode replacement characters. These indicate that either the file contained invalid UTF-8 (likely) or Unicode replacement characters (which it shouldn't). Note that it's possible for this to throw off line numbering if the invalid UTF-8 occurred adjacent to a newline. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ for linenum, line in enumerate(lines): if u'\ufffd' in line: error(filename, linenum, 'readability/utf8', 5, 'Line contains invalid UTF-8 (or Unicode replacement character).') def CheckForNewlineAtEOF(filename, lines, error): """Logs an error if there is no newline char at the end of the file. Args: filename: The name of the current file. lines: An array of strings, each representing a line of the file. error: The function to call with any errors found. """ # The array lines() was created by adding two newlines to the # original file (go figure), then splitting on \n. # To verify that the file ends in \n, we just have to make sure the # last-but-two element of lines() exists and is empty. if len(lines) < 3 or lines[-2]: error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, 'Could not find a newline character at the end of the file.') def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): """Logs an error if we see /* ... */ or "..." that extend past one line. /* ... */ comments are legit inside macros, for one line. Otherwise, we prefer // comments, so it's ok to warn about the other. Likewise, it's ok for strings to extend across multiple lines, as long as a line continuation character (backslash) terminates each line. Although not currently prohibited by the C++ style guide, it's ugly and unnecessary. We don't do well with either in this lint program, so we warn about both. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Remove all \\ (escaped backslashes) from the line. They are OK, and the # second (escaped) slash may trigger later \" detection erroneously. line = line.replace('\\\\', '') if line.count('/*') > line.count('*/'): error(filename, linenum, 'readability/multiline_comment', 5, 'Complex multi-line /*...*/-style comment found. ' 'Lint may give bogus warnings. ' 'Consider replacing these with //-style comments, ' 'with #if 0...#endif, ' 'or with more clearly structured multi-line comments.') if (line.count('"') - line.count('\\"')) % 2: error(filename, linenum, 'readability/multiline_string', 5, 'Multi-line string ("...") found. This lint script doesn\'t ' 'do well with such strings, and may give bogus warnings. They\'re ' 'ugly and unnecessary, and you should use concatenation instead".') threading_list = ( ('asctime(', 'asctime_r('), ('ctime(', 'ctime_r('), ('getgrgid(', 'getgrgid_r('), ('getgrnam(', 'getgrnam_r('), ('getlogin(', 'getlogin_r('), ('getpwnam(', 'getpwnam_r('), ('getpwuid(', 'getpwuid_r('), ('gmtime(', 'gmtime_r('), ('localtime(', 'localtime_r('), ('rand(', 'rand_r('), ('readdir(', 'readdir_r('), ('strtok(', 'strtok_r('), ('ttyname(', 'ttyname_r('), ) def CheckPosixThreading(filename, clean_lines, linenum, error): """Checks for calls to thread-unsafe functions. Much code has been originally written without consideration of multi-threading. Also, engineers are relying on their old experience; they have learned posix before threading extensions were added. These tests guide the engineers to use thread-safe functions (when using posix directly). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] for single_thread_function, multithread_safe_function in threading_list: ix = line.find(single_thread_function) # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and line[ix - 1] not in ('_', '.', '>'))): error(filename, linenum, 'runtime/threadsafe_fn', 2, 'Consider using ' + multithread_safe_function + '...) instead of ' + single_thread_function + '...) for improved thread safety.') # Matches invalid increment: *count++, which moves pointer instead of # incrementing a value. _RE_PATTERN_INVALID_INCREMENT = re.compile( r'^\s*\*\w+(\+\+|--);') def CheckInvalidIncrement(filename, clean_lines, linenum, error): """Checks for invalid increment *count++. For example following function: void increment_counter(int* count) { *count++; } is invalid, because it effectively does count++, moving pointer, and should be replaced with ++*count, (*count)++ or *count += 1. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] if _RE_PATTERN_INVALID_INCREMENT.match(line): error(filename, linenum, 'runtime/invalid_increment', 5, 'Changing pointer instead of value (or unused value of operator*).') class _BlockInfo(object): """Stores information about a generic block of code.""" def __init__(self, seen_open_brace): self.seen_open_brace = seen_open_brace self.open_parentheses = 0 self.inline_asm = _NO_ASM def CheckBegin(self, filename, clean_lines, linenum, error): """Run checks that applies to text up to the opening brace. This is mostly for checking the text after the class identifier and the "{", usually where the base class is specified. For other blocks, there isn't much to check, so we always pass. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ pass def CheckEnd(self, filename, clean_lines, linenum, error): """Run checks that applies to text after the closing brace. This is mostly used for checking end of namespace comments. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ pass class _ClassInfo(_BlockInfo): """Stores information about a class.""" def __init__(self, name, class_or_struct, clean_lines, linenum): _BlockInfo.__init__(self, False) self.name = name self.starting_linenum = linenum self.is_derived = False if class_or_struct == 'struct': self.access = 'public' else: self.access = 'private' # Try to find the end of the class. This will be confused by things like: # class A { # } *x = { ... # # But it's still good enough for CheckSectionSpacing. self.last_line = 0 depth = 0 for i in range(linenum, clean_lines.NumLines()): line = clean_lines.elided[i] depth += line.count('{') - line.count('}') if not depth: self.last_line = i break def CheckBegin(self, filename, clean_lines, linenum, error): # Look for a bare ':' if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): self.is_derived = True class _NamespaceInfo(_BlockInfo): """Stores information about a namespace.""" def __init__(self, name, linenum): _BlockInfo.__init__(self, False) self.name = name or '' self.starting_linenum = linenum def CheckEnd(self, filename, clean_lines, linenum, error): """Check end of namespace comments.""" line = clean_lines.raw_lines[linenum] # Check how many lines is enclosed in this namespace. Don't issue # warning for missing namespace comments if there aren't enough # lines. However, do apply checks if there is already an end of # namespace comment and it's incorrect. # # TODO(unknown): We always want to check end of namespace comments # if a namespace is large, but sometimes we also want to apply the # check if a short namespace contained nontrivial things (something # other than forward declarations). There is currently no logic on # deciding what these nontrivial things are, so this check is # triggered by namespace size only, which works most of the time. if (linenum - self.starting_linenum < 10 and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): return # Look for matching comment at end of namespace. # # Note that we accept C style "/* */" comments for terminating # namespaces, so that code that terminate namespaces inside # preprocessor macros can be cpplint clean. Example: http://go/nxpiz # # We also accept stuff like "// end of namespace ." with the # period at the end. # # Besides these, we don't accept anything else, otherwise we might # get false negatives when existing comment is a substring of the # expected namespace. Example: http://go/ldkdc, http://cl/23548205 if self.name: # Named namespace if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + r'[\*/\.\\\s]*$'), line): error(filename, linenum, 'readability/namespace', 5, 'Namespace should be terminated with "// namespace %s"' % self.name) else: # Anonymous namespace if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): error(filename, linenum, 'readability/namespace', 5, 'Namespace should be terminated with "// namespace"') class _PreprocessorInfo(object): """Stores checkpoints of nesting stacks when #if/#else is seen.""" def __init__(self, stack_before_if): # The entire nesting stack before #if self.stack_before_if = stack_before_if # The entire nesting stack up to #else self.stack_before_else = [] # Whether we have already seen #else or #elif self.seen_else = False class _NestingState(object): """Holds states related to parsing braces.""" def __init__(self): # Stack for tracking all braces. An object is pushed whenever we # see a "{", and popped when we see a "}". Only 3 types of # objects are possible: # - _ClassInfo: a class or struct. # - _NamespaceInfo: a namespace. # - _BlockInfo: some other type of block. self.stack = [] # Stack of _PreprocessorInfo objects. self.pp_stack = [] def SeenOpenBrace(self): """Check if we have seen the opening brace for the innermost block. Returns: True if we have seen the opening brace, False if the innermost block is still expecting an opening brace. """ return (not self.stack) or self.stack[-1].seen_open_brace def InNamespaceBody(self): """Check if we are currently one level inside a namespace body. Returns: True if top of the stack is a namespace block, False otherwise. """ return self.stack and isinstance(self.stack[-1], _NamespaceInfo) def UpdatePreprocessor(self, line): """Update preprocessor stack. We need to handle preprocessors due to classes like this: #ifdef SWIG struct ResultDetailsPageElementExtensionPoint { #else struct ResultDetailsPageElementExtensionPoint : public Extension { #endif (see http://go/qwddn for original example) We make the following assumptions (good enough for most files): - Preprocessor condition evaluates to true from #if up to first #else/#elif/#endif. - Preprocessor condition evaluates to false from #else/#elif up to #endif. We still perform lint checks on these lines, but these do not affect nesting stack. Args: line: current line to check. """ if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): # Beginning of #if block, save the nesting stack here. The saved # stack will allow us to restore the parsing state in the #else case. self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) elif Match(r'^\s*#\s*(else|elif)\b', line): # Beginning of #else block if self.pp_stack: if not self.pp_stack[-1].seen_else: # This is the first #else or #elif block. Remember the # whole nesting stack up to this point. This is what we # keep after the #endif. self.pp_stack[-1].seen_else = True self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) # Restore the stack to how it was before the #if self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) else: # TODO(unknown): unexpected #else, issue warning? pass elif Match(r'^\s*#\s*endif\b', line): # End of #if or #else blocks. if self.pp_stack: # If we saw an #else, we will need to restore the nesting # stack to its former state before the #else, otherwise we # will just continue from where we left off. if self.pp_stack[-1].seen_else: # Here we can just use a shallow copy since we are the last # reference to it. self.stack = self.pp_stack[-1].stack_before_else # Drop the corresponding #if self.pp_stack.pop() else: # TODO(unknown): unexpected #endif, issue warning? pass def Update(self, filename, clean_lines, linenum, error): """Update nesting state with current line. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Update pp_stack first self.UpdatePreprocessor(line) # Count parentheses. This is to avoid adding struct arguments to # the nesting stack. if self.stack: inner_block = self.stack[-1] depth_change = line.count('(') - line.count(')') inner_block.open_parentheses += depth_change # Also check if we are starting or ending an inline assembly block. if inner_block.inline_asm in (_NO_ASM, _END_ASM): if (depth_change != 0 and inner_block.open_parentheses == 1 and _MATCH_ASM.match(line)): # Enter assembly block inner_block.inline_asm = _INSIDE_ASM else: # Not entering assembly block. If previous line was _END_ASM, # we will now shift to _NO_ASM state. inner_block.inline_asm = _NO_ASM elif (inner_block.inline_asm == _INSIDE_ASM and inner_block.open_parentheses == 0): # Exit assembly block inner_block.inline_asm = _END_ASM # Consume namespace declaration at the beginning of the line. Do # this in a loop so that we catch same line declarations like this: # namespace proto2 { namespace bridge { class MessageSet; } } while True: # Match start of namespace. The "\b\s*" below catches namespace # declarations even if it weren't followed by a whitespace, this # is so that we don't confuse our namespace checker. The # missing spaces will be flagged by CheckSpacing. namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) if not namespace_decl_match: break new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) self.stack.append(new_namespace) line = namespace_decl_match.group(2) if line.find('{') != -1: new_namespace.seen_open_brace = True line = line[line.find('{') + 1:] # Look for a class declaration in whatever is left of the line # after parsing namespaces. The regexp accounts for decorated classes # such as in: # class LOCKABLE API Object { # }; # # Templates with class arguments may confuse the parser, for example: # template , # class Vector = vector > # class HeapQueue { # # Because this parser has no nesting state about templates, by the # time it saw "class Comparator", it may think that it's a new class. # Nested templates have a similar problem: # template < # typename ExportedType, # typename TupleType, # template class ImplTemplate> # # To avoid these cases, we ignore classes that are followed by '=' or '>' class_decl_match = Match( r'\s*(template\s*<[\w\s<>,:]*>\s*)?' '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' '(([^=>]|<[^<>]*>)*)$', line) if (class_decl_match and (not self.stack or self.stack[-1].open_parentheses == 0)): self.stack.append(_ClassInfo( class_decl_match.group(4), class_decl_match.group(2), clean_lines, linenum)) line = class_decl_match.group(5) # If we have not yet seen the opening brace for the innermost block, # run checks here. if not self.SeenOpenBrace(): self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) # Update access control if we are inside a class/struct if self.stack and isinstance(self.stack[-1], _ClassInfo): access_match = Match(r'\s*(public|private|protected)\s*:', line) if access_match: self.stack[-1].access = access_match.group(1) # Consume braces or semicolons from what's left of the line while True: # Match first brace, semicolon, or closed parenthesis. matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) if not matched: break token = matched.group(1) if token == '{': # If namespace or class hasn't seen a opening brace yet, mark # namespace/class head as complete. Push a new block onto the # stack otherwise. if not self.SeenOpenBrace(): self.stack[-1].seen_open_brace = True else: self.stack.append(_BlockInfo(True)) if _MATCH_ASM.match(line): self.stack[-1].inline_asm = _BLOCK_ASM elif token == ';' or token == ')': # If we haven't seen an opening brace yet, but we already saw # a semicolon, this is probably a forward declaration. Pop # the stack for these. # # Similarly, if we haven't seen an opening brace yet, but we # already saw a closing parenthesis, then these are probably # function arguments with extra "class" or "struct" keywords. # Also pop these stack for these. if not self.SeenOpenBrace(): self.stack.pop() else: # token == '}' # Perform end of block checks and pop the stack. if self.stack: self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) self.stack.pop() line = matched.group(2) def InnermostClass(self): """Get class info on the top of the stack. Returns: A _ClassInfo object if we are inside a class, or None otherwise. """ for i in range(len(self.stack), 0, -1): classinfo = self.stack[i - 1] if isinstance(classinfo, _ClassInfo): return classinfo return None def CheckClassFinished(self, filename, error): """Checks that all classes have been completely parsed. Call this when all lines in a file have been processed. Args: filename: The name of the current file. error: The function to call with any errors found. """ # Note: This test can result in false positives if #ifdef constructs # get in the way of brace matching. See the testBuildClass test in # cpplint_unittest.py for an example of this. for obj in self.stack: if isinstance(obj, _ClassInfo): error(filename, obj.starting_linenum, 'build/class', 5, 'Failed to find complete declaration of class %s' % obj.name) def CheckForNonStandardConstructs(filename, clean_lines, linenum, nesting_state, error): """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. Complain about several constructs which gcc-2 accepts, but which are not standard C++. Warning about these in lint is one way to ease the transition to new compilers. - put storage class first (e.g. "static const" instead of "const static"). - "%lld" instead of %qd" in printf-type functions. - "%1$d" is non-standard in printf-type functions. - "\%" is an undefined character escape sequence. - text after #endif is not allowed. - invalid inner-style forward declaration. - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', line): error(filename, linenum, 'build/deprecated', 3, '>? and ))?' # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' error(filename, linenum, 'runtime/member_string_references', 2, 'const string& members are dangerous. It is much better to use ' 'alternatives, such as pointers or simple constants.') # Everything else in this function operates on class declarations. # Return early if the top of the nesting stack is not a class, or if # the class head is not completed yet. classinfo = nesting_state.InnermostClass() if not classinfo or not classinfo.seen_open_brace: return # The class may have been declared with namespace or classname qualifiers. # The constructor and destructor will not have those qualifiers. base_classname = classinfo.name.split('::')[-1] # Look for single-argument constructors that aren't marked explicit. # Technically a valid construct, but against style. args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' % re.escape(base_classname), line) if (args and args.group(1) != 'void' and not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), args.group(1).strip())): error(filename, linenum, 'runtime/explicit', 5, 'Single-argument constructors should be marked explicit.') def CheckSpacingForFunctionCall(filename, line, linenum, error): """Checks for the correctness of various spacing around function calls. Args: filename: The name of the current file. line: The text of the line to check. linenum: The number of the line to check. error: The function to call with any errors found. """ # Since function calls often occur inside if/for/while/switch # expressions - which have their own, more liberal conventions - we # first see if we should be looking inside such an expression for a # function call, to which we can apply more strict standards. fncall = line # if there's no control flow construct, look at whole line for pattern in (r'\bif\s*\((.*)\)\s*{', r'\bfor\s*\((.*)\)\s*{', r'\bwhile\s*\((.*)\)\s*[{;]', r'\bswitch\s*\((.*)\)\s*{'): match = Search(pattern, line) if match: fncall = match.group(1) # look inside the parens for function calls break # Except in if/for/while/switch, there should never be space # immediately inside parens (eg "f( 3, 4 )"). We make an exception # for nested parens ( (a+b) + c ). Likewise, there should never be # a space before a ( when it's a function argument. I assume it's a # function argument when the char before the whitespace is legal in # a function name (alnum + _) and we're not starting a macro. Also ignore # pointers and references to arrays and functions coz they're too tricky: # we use a very simple way to recognize these: # " (something)(maybe-something)" or # " (something)(maybe-something," or # " (something)[something]" # Note that we assume the contents of [] to be short enough that # they'll never need to wrap. if ( # Ignore control structures. not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and # Ignore pointers/references to functions. not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and # Ignore pointers/references to arrays. not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call error(filename, linenum, 'whitespace/parens', 4, 'Extra space after ( in function call') elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Extra space after (') if (Search(r'\w\s+\(', fncall) and not Search(r'#\s*define|typedef', fncall) and not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): error(filename, linenum, 'whitespace/parens', 4, 'Extra space before ( in function call') # If the ) is followed only by a newline or a { + newline, assume it's # part of a control statement (if/while/etc), and don't complain if Search(r'[^)]\s+\)\s*[^{\s]', fncall): # If the closing parenthesis is preceded by only whitespaces, # try to give a more descriptive error message. if Search(r'^\s+\)', fncall): error(filename, linenum, 'whitespace/parens', 2, 'Closing ) should be moved to the previous line') else: error(filename, linenum, 'whitespace/parens', 2, 'Extra space before )') def IsBlankLine(line): """Returns true if the given line is blank. We consider a line to be blank if the line is empty or consists of only white spaces. Args: line: A line of a string. Returns: True, if the given line is blank. """ return not line or line.isspace() def CheckForFunctionLengths(filename, clean_lines, linenum, function_state, error): """Reports for long function bodies. For an overview why this is done, see: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions Uses a simplistic algorithm assuming other style guidelines (especially spacing) are followed. Only checks unindented functions, so class members are unchecked. Trivial bodies are unchecked, so constructors with huge initializer lists may be missed. Blank/comment lines are not counted so as to avoid encouraging the removal of vertical space and comments just to get through a lint check. NOLINT *on the last line of a function* disables this check. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. function_state: Current function name and lines in body so far. error: The function to call with any errors found. """ lines = clean_lines.lines line = lines[linenum] raw = clean_lines.raw_lines raw_line = raw[linenum] joined_line = '' starting_func = False regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... match_result = Match(regexp, line) if match_result: # If the name is all caps and underscores, figure it's a macro and # ignore it, unless it's TEST or TEST_F. function_name = match_result.group(1).split()[-1] if function_name == 'TEST' or function_name == 'TEST_F' or ( not Match(r'[A-Z_]+$', function_name)): starting_func = True if starting_func: body_found = False for start_linenum in xrange(linenum, clean_lines.NumLines()): start_line = lines[start_linenum] joined_line += ' ' + start_line.lstrip() if Search(r'(;|})', start_line): # Declarations and trivial functions body_found = True break # ... ignore elif Search(r'{', start_line): body_found = True function = Search(r'((\w|:)*)\(', line).group(1) if Match(r'TEST', function): # Handle TEST... macros parameter_regexp = Search(r'(\(.*\))', joined_line) if parameter_regexp: # Ignore bad syntax function += parameter_regexp.group(1) else: function += '()' function_state.Begin(function) break if not body_found: # No body for the function (or evidence of a non-function) was found. error(filename, linenum, 'readability/fn_size', 5, 'Lint failed to find start of function body.') elif Match(r'^\}\s*$', line): # function end function_state.Check(error, filename, linenum) function_state.End() elif not Match(r'^\s*$', line): function_state.Count() # Count non-blank/non-comment lines. _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') def CheckComment(comment, filename, linenum, error): """Checks for common mistakes in TODO comments. Args: comment: The text of the comment from the line in question. filename: The name of the current file. linenum: The number of the line to check. error: The function to call with any errors found. """ match = _RE_PATTERN_TODO.match(comment) if match: # One whitespace is correct; zero whitespace is handled elsewhere. leading_whitespace = match.group(1) if len(leading_whitespace) > 1: error(filename, linenum, 'whitespace/todo', 2, 'Too many spaces before TODO') username = match.group(2) if not username: error(filename, linenum, 'readability/todo', 2, 'Missing username in TODO; it should look like ' '"// TODO(my_username): Stuff."') middle_whitespace = match.group(3) # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 if middle_whitespace != ' ' and middle_whitespace != '': error(filename, linenum, 'whitespace/todo', 2, 'TODO(my_username) should be followed by a space') def CheckAccess(filename, clean_lines, linenum, nesting_state, error): """Checks for improper use of DISALLOW* macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' r'DISALLOW_EVIL_CONSTRUCTORS|' r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) if not matched: return if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): if nesting_state.stack[-1].access != 'private': error(filename, linenum, 'readability/constructors', 3, '%s must be in the private: section' % matched.group(1)) else: # Found DISALLOW* macro outside a class declaration, or perhaps it # was used inside a function when it should have been part of the # class declaration. We could issue a warning here, but it # probably resulted in a compiler error already. pass def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): """Find the corresponding > to close a template. Args: clean_lines: A CleansedLines instance containing the file. linenum: Current line number. init_suffix: Remainder of the current line after the initial <. Returns: True if a matching bracket exists. """ line = init_suffix nesting_stack = ['<'] while True: # Find the next operator that can tell us whether < is used as an # opening bracket or as a less-than operator. We only want to # warn on the latter case. # # We could also check all other operators and terminate the search # early, e.g. if we got something like this "a(),;\[\]]*([<>(),;\[\]])(.*)$', line) if match: # Found an operator, update nesting stack operator = match.group(1) line = match.group(2) if nesting_stack[-1] == '<': # Expecting closing angle bracket if operator in ('<', '(', '['): nesting_stack.append(operator) elif operator == '>': nesting_stack.pop() if not nesting_stack: # Found matching angle bracket return True elif operator == ',': # Got a comma after a bracket, this is most likely a template # argument. We have not seen a closing angle bracket yet, but # it's probably a few lines later if we look for it, so just # return early here. return True else: # Got some other operator. return False else: # Expecting closing parenthesis or closing bracket if operator in ('<', '(', '['): nesting_stack.append(operator) elif operator in (')', ']'): # We don't bother checking for matching () or []. If we got # something like (] or [), it would have been a syntax error. nesting_stack.pop() else: # Scan the next line linenum += 1 if linenum >= len(clean_lines.elided): break line = clean_lines.elided[linenum] # Exhausted all remaining lines and still no matching angle bracket. # Most likely the input was incomplete, otherwise we should have # seen a semicolon and returned early. return True def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): """Find the corresponding < that started a template. Args: clean_lines: A CleansedLines instance containing the file. linenum: Current line number. init_prefix: Part of the current line before the initial >. Returns: True if a matching bracket exists. """ line = init_prefix nesting_stack = ['>'] while True: # Find the previous operator match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) if match: # Found an operator, update nesting stack operator = match.group(2) line = match.group(1) if nesting_stack[-1] == '>': # Expecting opening angle bracket if operator in ('>', ')', ']'): nesting_stack.append(operator) elif operator == '<': nesting_stack.pop() if not nesting_stack: # Found matching angle bracket return True elif operator == ',': # Got a comma before a bracket, this is most likely a # template argument. The opening angle bracket is probably # there if we look for it, so just return early here. return True else: # Got some other operator. return False else: # Expecting opening parenthesis or opening bracket if operator in ('>', ')', ']'): nesting_stack.append(operator) elif operator in ('(', '['): nesting_stack.pop() else: # Scan the previous line linenum -= 1 if linenum < 0: break line = clean_lines.elided[linenum] # Exhausted all earlier lines and still no matching angle bracket. return False def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): """Checks for the correctness of various spacing issues in the code. Things we check for: spaces around operators, spaces after if/for/while/switch, no spaces around parens in function calls, two spaces between code and comment, don't start a block with a blank line, don't end a function with a blank line, don't add a blank line after public/protected/private, don't have too many blank lines in a row. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ raw = clean_lines.raw_lines line = raw[linenum] # Before nixing comments, check if the line is blank for no good # reason. This includes the first line after a block is opened, and # blank lines at the end of a function (ie, right before a line like '}' # # Skip all the blank line checks if we are immediately inside a # namespace body. In other words, don't issue blank line warnings # for this block: # namespace { # # } # # A warning about missing end of namespace comments will be issued instead. if IsBlankLine(line) and not nesting_state.InNamespaceBody(): elided = clean_lines.elided prev_line = elided[linenum - 1] prevbrace = prev_line.rfind('{') # TODO(unknown): Don't complain if line before blank line, and line after, # both start with alnums and are indented the same amount. # This ignores whitespace at the start of a namespace block # because those are not usually indented. if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: # OK, we have a blank line at the start of a code block. Before we # complain, we check if it is an exception to the rule: The previous # non-empty line has the parameters of a function header that are indented # 4 spaces (because they did not fit in a 80 column line when placed on # the same line as the function name). We also check for the case where # the previous line is indented 6 spaces, which may happen when the # initializers of a constructor do not fit into a 80 column line. exception = False if Match(r' {6}\w', prev_line): # Initializer list? # We are looking for the opening column of initializer list, which # should be indented 4 spaces to cause 6 space indentation afterwards. search_position = linenum-2 while (search_position >= 0 and Match(r' {6}\w', elided[search_position])): search_position -= 1 exception = (search_position >= 0 and elided[search_position][:5] == ' :') else: # Search for the function arguments or an initializer list. We use a # simple heuristic here: If the line is indented 4 spaces; and we have a # closing paren, without the opening paren, followed by an opening brace # or colon (for initializer lists) we assume that it is the last line of # a function header. If we have a colon indented 4 spaces, it is an # initializer list. exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', prev_line) or Match(r' {4}:', prev_line)) if not exception: error(filename, linenum, 'whitespace/blank_line', 2, 'Blank line at the start of a code block. Is this needed?') # Ignore blank lines at the end of a block in a long if-else # chain, like this: # if (condition1) { # // Something followed by a blank line # # } else if (condition2) { # // Something else # } if linenum + 1 < clean_lines.NumLines(): next_line = raw[linenum + 1] if (next_line and Match(r'\s*}', next_line) and next_line.find('} else ') == -1): error(filename, linenum, 'whitespace/blank_line', 3, 'Blank line at the end of a code block. Is this needed?') matched = Match(r'\s*(public|protected|private):', prev_line) if matched: error(filename, linenum, 'whitespace/blank_line', 3, 'Do not leave a blank line after "%s:"' % matched.group(1)) # Next, we complain if there's a comment too near the text commentpos = line.find('//') if commentpos != -1: # Check if the // may be in quotes. If so, ignore it # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 if (line.count('"', 0, commentpos) - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes # Allow one space for new scopes, two spaces otherwise: if (not Match(r'^\s*{ //', line) and ((commentpos >= 1 and line[commentpos-1] not in string.whitespace) or (commentpos >= 2 and line[commentpos-2] not in string.whitespace))): error(filename, linenum, 'whitespace/comments', 2, 'At least two spaces is best between code and comments') # There should always be a space between the // and the comment commentend = commentpos + 2 if commentend < len(line) and not line[commentend] == ' ': # but some lines are exceptions -- e.g. if they're big # comment delimiters like: # //---------------------------------------------------------- # or are an empty C++ style Doxygen comment, like: # /// # or they begin with multiple slashes followed by a space: # //////// Header comment match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^/+ ', line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') CheckComment(line[commentpos:], filename, linenum, error) line = clean_lines.elided[linenum] # get rid of comments and strings # Don't try to do spacing checks for operator methods line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". # Otherwise not. Note we only check for non-spaces on *both* sides; # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around =') # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned. It's hard to tell, # though, so we punt on this one for now. TODO. # You should always have whitespace around binary operators. # # Check <= and >= first to avoid false positives with < and >, then # check non-include lines for spacing around < and >. match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around %s' % match.group(1)) # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) if match and not (match.group(1).isdigit() and match.group(2).isdigit()): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around <<') elif not Match(r'#.*include', line): # Avoid false positives on -> reduced_line = line.replace('->', '') # Look for < that is not surrounded by spaces. This is only # triggered if both sides are missing spaces, even though # technically should should flag if at least one side is missing a # space. This is done to avoid some false positives with shifts. match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) if (match and not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around <') # Look for > that is not surrounded by spaces. Similar to the # above, we only trigger if both sides are missing spaces to avoid # false positives with shifts. match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) if (match and not FindPreviousMatchingAngleBracket(clean_lines, linenum, match.group(1))): error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >') # We allow no-spaces around >> for almost anything. This is because # C++11 allows ">>" to close nested templates, which accounts for # most cases when ">>" is not followed by a space. # # We still warn on ">>" followed by alpha character, because that is # likely due to ">>" being used for right shifts, e.g.: # value >> alpha # # When ">>" is used to close templates, the alphanumeric letter that # follows would be part of an identifier, and there should still be # a space separating the template type and the identifier. # type> alpha match = Search(r'>>[a-zA-Z_]', line) if match: error(filename, linenum, 'whitespace/operators', 3, 'Missing spaces around >>') # There shouldn't be space around unary operators match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) if match: error(filename, linenum, 'whitespace/operators', 4, 'Extra space for operator %s' % match.group(1)) # A pet peeve of mine: no spaces after an if, while, switch, or for match = Search(r' (if\(|for\(|while\(|switch\()', line) if match: error(filename, linenum, 'whitespace/parens', 5, 'Missing space before ( in %s' % match.group(1)) # For if/for/while/switch, the left and right parens should be # consistent about how many spaces are inside the parens, and # there should either be zero or one spaces inside the parens. # We don't want: "if ( foo)" or "if ( foo )". # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. match = Search(r'\b(if|for|while|switch)\s*' r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', line) if match: if len(match.group(2)) != len(match.group(4)): if not (match.group(3) == ';' and len(match.group(2)) == 1 + len(match.group(4)) or not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): error(filename, linenum, 'whitespace/parens', 5, 'Mismatching spaces inside () in %s' % match.group(1)) if not len(match.group(2)) in [0, 1]: error(filename, linenum, 'whitespace/parens', 5, 'Should have zero or one spaces inside ( and ) in %s' % match.group(1)) # You should always have a space after a comma (either as fn arg or operator) if Search(r',[^\s]', line): error(filename, linenum, 'whitespace/comma', 3, 'Missing space after ,') # You should always have a space after a semicolon # except for few corner cases # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more # space after ; if Search(r';[^\s};\\)/]', line): error(filename, linenum, 'whitespace/semicolon', 3, 'Missing space after ;') # Next we will look for issues with function calls. CheckSpacingForFunctionCall(filename, line, linenum, error) # Except after an opening paren, or after another opening brace (in case of # an initializer list, for instance), you should have spaces before your # braces. And since you should never have braces at the beginning of a line, # this is an easy test. if Search(r'[^ ({]{', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before {') # Make sure '} else {' has spaces. if Search(r'}else', line): error(filename, linenum, 'whitespace/braces', 5, 'Missing space before else') # You shouldn't have spaces before your brackets, except maybe after # 'delete []' or 'new char * []'. if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): error(filename, linenum, 'whitespace/braces', 5, 'Extra space before [') # You shouldn't have a space before a semicolon at the end of the line. # There's a special case for "for" since the style guide allows space before # the semicolon there. if Search(r':\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Semicolon defining empty statement. Use {} instead.') elif Search(r'^\s*;\s*$', line): error(filename, linenum, 'whitespace/semicolon', 5, 'Line contains only semicolon. If this should be an empty statement, ' 'use {} instead.') elif (Search(r'\s+;\s*$', line) and not Search(r'\bfor\b', line)): error(filename, linenum, 'whitespace/semicolon', 5, 'Extra space before last semicolon. If this should be an empty ' 'statement, use {} instead.') # In range-based for, we wanted spaces before and after the colon, but # not around "::" tokens that might appear. if (Search('for *\(.*[^:]:[^: ]', line) or Search('for *\(.*[^: ]:[^:]', line)): error(filename, linenum, 'whitespace/forcolon', 2, 'Missing space around colon in range-based for loop') def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): """Checks for additional blank line issues related to sections. Currently the only thing checked here is blank line before protected/private. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. class_info: A _ClassInfo objects. linenum: The number of the line to check. error: The function to call with any errors found. """ # Skip checks if the class is small, where small means 25 lines or less. # 25 lines seems like a good cutoff since that's the usual height of # terminals, and any class that can't fit in one screen can't really # be considered "small". # # Also skip checks if we are on the first line. This accounts for # classes that look like # class Foo { public: ... }; # # If we didn't find the end of the class, last_line would be zero, # and the check will be skipped by the first condition. if (class_info.last_line - class_info.starting_linenum <= 24 or linenum <= class_info.starting_linenum): return matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) if matched: # Issue warning if the line before public/protected/private was # not a blank line, but don't do this if the previous line contains # "class" or "struct". This can happen two ways: # - We are at the beginning of the class. # - We are forward-declaring an inner class that is semantically # private, but needed to be public for implementation reasons. # Also ignores cases where the previous line ends with a backslash as can be # common when defining classes in C macros. prev_line = clean_lines.lines[linenum - 1] if (not IsBlankLine(prev_line) and not Search(r'\b(class|struct)\b', prev_line) and not Search(r'\\$', prev_line)): # Try a bit harder to find the beginning of the class. This is to # account for multi-line base-specifier lists, e.g.: # class Derived # : public Base { end_class_head = class_info.starting_linenum for i in range(class_info.starting_linenum, linenum): if Search(r'\{\s*$', clean_lines.lines[i]): end_class_head = i break if end_class_head < linenum - 1: error(filename, linenum, 'whitespace/blank_line', 3, '"%s:" should be preceded by a blank line' % matched.group(1)) def GetPreviousNonBlankLine(clean_lines, linenum): """Return the most recent non-blank line and its line number. Args: clean_lines: A CleansedLines instance containing the file contents. linenum: The number of the line to check. Returns: A tuple with two elements. The first element is the contents of the last non-blank line before the current line, or the empty string if this is the first non-blank line. The second is the line number of that line, or -1 if this is the first non-blank line. """ prevlinenum = linenum - 1 while prevlinenum >= 0: prevline = clean_lines.elided[prevlinenum] if not IsBlankLine(prevline): # if not a blank line... return (prevline, prevlinenum) prevlinenum -= 1 return ('', -1) def CheckBraces(filename, clean_lines, linenum, error): """Looks for misplaced braces (e.g. at the end of line). Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # get rid of comments and strings if Match(r'\s*{\s*$', line): # We allow an open brace to start a line in the case where someone # is using braces in a block to explicitly create a new scope, # which is commonly used to control the lifetime of # stack-allocated variables. We don't detect this perfectly: we # just don't complain if the last non-whitespace character on the # previous non-blank line is ';', ':', '{', or '}', or if the previous # line starts a preprocessor block. prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if (not Search(r'[;:}{]\s*$', prevline) and not Match(r'\s*#', prevline)): error(filename, linenum, 'whitespace/braces', 4, '{ should almost always be at the end of the previous line') # An else clause should be on the same line as the preceding closing brace. if Match(r'\s*else\s*', line): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if Match(r'\s*}\s*$', prevline): error(filename, linenum, 'whitespace/newline', 4, 'An else should appear on the same line as the preceding }') # If braces come on one side of an else, they should be on both. # However, we have to worry about "else if" that spans multiple lines! if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if # find the ( after the if pos = line.find('else if') pos = line.find('(', pos) if pos > 0: (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) if endline[endpos:].find('{') == -1: # must be brace after if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') else: # common case: else not followed by a multi-line if error(filename, linenum, 'readability/braces', 5, 'If an else has a brace on one side, it should have it on both') # Likewise, an else should never have the else clause on the same line if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): error(filename, linenum, 'whitespace/newline', 4, 'Else clause should never be on same line as else (use 2 lines)') # In the same way, a do/while should never be on one line if Match(r'\s*do [^\s{]', line): error(filename, linenum, 'whitespace/newline', 4, 'do/while clauses should not be on a single line') # Braces shouldn't be followed by a ; unless they're defining a struct # or initializing an array. # We can't tell in general, but we can for some common cases. prevlinenum = linenum while True: (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): line = prevline + line else: break if (Search(r'{.*}\s*;', line) and line.count('{') == line.count('}') and not Search(r'struct|class|enum|\s*=\s*{', line)): error(filename, linenum, 'readability/braces', 4, "You don't need a ; after a }") def CheckEmptyLoopBody(filename, clean_lines, linenum, error): """Loop for empty loop body with only a single semicolon. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Search for loop keywords at the beginning of the line. Because only # whitespaces are allowed before the keywords, this will also ignore most # do-while-loops, since those lines should start with closing brace. line = clean_lines.elided[linenum] if Match(r'\s*(for|while)\s*\(', line): # Find the end of the conditional expression (end_line, end_linenum, end_pos) = CloseExpression( clean_lines, linenum, line.find('(')) # Output warning if what follows the condition expression is a semicolon. # No warning for all other cases, including whitespace or newline, since we # have a separate check for semicolons preceded by whitespace. if end_pos >= 0 and Match(r';', end_line[end_pos:]): error(filename, end_linenum, 'whitespace/empty_loop_body', 5, 'Empty loop bodies should use {} or continue') def ReplaceableCheck(operator, macro, line): """Determine whether a basic CHECK can be replaced with a more specific one. For example suggest using CHECK_EQ instead of CHECK(a == b) and similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. Args: operator: The C++ operator used in the CHECK. macro: The CHECK or EXPECT macro being called. line: The current source line. Returns: True if the CHECK can be replaced with a more specific one. """ # This matches decimal and hex integers, strings, and chars (in that order). match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' # Expression to match two sides of the operator with something that # looks like a literal, since CHECK(x == iterator) won't compile. # This means we can't catch all the cases where a more specific # CHECK is possible, but it's less annoying than dealing with # extraneous warnings. match_this = (r'\s*' + macro + r'\((\s*' + match_constant + r'\s*' + operator + r'[^<>].*|' r'.*[^<>]' + operator + r'\s*' + match_constant + r'\s*\))') # Don't complain about CHECK(x == NULL) or similar because # CHECK_EQ(x, NULL) won't compile (requires a cast). # Also, don't complain about more complex boolean expressions # involving && or || such as CHECK(a == b || c == d). return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) def CheckCheck(filename, clean_lines, linenum, error): """Checks the use of CHECK and EXPECT macros. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ # Decide the set of replacement macros that should be suggested raw_lines = clean_lines.raw_lines current_macro = '' for macro in _CHECK_MACROS: if raw_lines[linenum].find(macro) >= 0: current_macro = macro break if not current_macro: # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' return line = clean_lines.elided[linenum] # get rid of comments and strings # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. for operator in ['==', '!=', '>=', '>', '<=', '<']: if ReplaceableCheck(operator, current_macro, line): error(filename, linenum, 'readability/check', 2, 'Consider using %s instead of %s(a %s b)' % ( _CHECK_REPLACEMENT[current_macro][operator], current_macro, operator)) break def CheckAltTokens(filename, clean_lines, linenum, error): """Check alternative keywords being used in boolean expressions. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ line = clean_lines.elided[linenum] # Avoid preprocessor lines if Match(r'^\s*#', line): return # Last ditch effort to avoid multi-line comments. This will not help # if the comment started before the current line or ended after the # current line, but it catches most of the false positives. At least, # it provides a way to workaround this warning for people who use # multi-line comments in preprocessor macros. # # TODO(unknown): remove this once cpplint has better support for # multi-line comments. if line.find('/*') >= 0 or line.find('*/') >= 0: return for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): error(filename, linenum, 'readability/alt_tokens', 2, 'Use operator %s instead of %s' % ( _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) def GetLineWidth(line): """Determines the width of the line in column positions. Args: line: A string, which may be a Unicode string. Returns: The width of the line in column positions, accounting for Unicode combining characters and wide characters. """ if isinstance(line, unicode): width = 0 for uc in unicodedata.normalize('NFC', line): if unicodedata.east_asian_width(uc) in ('W', 'F'): width += 2 elif not unicodedata.combining(uc): width += 1 return width else: return len(line) def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, error): """Checks rules from the 'C++ style rules' section of cppguide.html. Most of these rules are hard to test (naming, comment style), but we do what we can. In particular we check for 2-space indents, line lengths, tab usage, spaces inside code, etc. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: The function to call with any errors found. """ raw_lines = clean_lines.raw_lines line = raw_lines[linenum] if line.find('\t') != -1: error(filename, linenum, 'whitespace/tab', 1, 'Tab found; better to use spaces') # One or three blank spaces at the beginning of the line is weird; it's # hard to reconcile that with 2-space indents. # NOTE: here are the conditions rob pike used for his tests. Mine aren't # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces # if(RLENGTH > 20) complain = 0; # if(match($0, " +(error|private|public|protected):")) complain = 0; # if(match(prev, "&& *$")) complain = 0; # if(match(prev, "\\|\\| *$")) complain = 0; # if(match(prev, "[\",=><] *$")) complain = 0; # if(match($0, " <<")) complain = 0; # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; initial_spaces = 0 cleansed_line = clean_lines.elided[linenum] while initial_spaces < len(line) and line[initial_spaces] == ' ': initial_spaces += 1 if line and line[-1].isspace(): error(filename, linenum, 'whitespace/end_of_line', 4, 'Line ends in whitespace. Consider deleting these extra spaces.') # There are certain situations we allow one space, notably for labels elif ((initial_spaces == 1 or initial_spaces == 3) and not Match(r'\s*\w+\s*:\s*$', cleansed_line)): error(filename, linenum, 'whitespace/indent', 3, 'Weird number of spaces at line-start. ' 'Are you using a 2-space indent?') # Labels should always be indented at least one space. elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', line): error(filename, linenum, 'whitespace/labels', 4, 'Labels should always be indented at least one space. ' 'If this is a member-initializer list in a constructor or ' 'the base class list in a class definition, the colon should ' 'be on the following line.') # Check if the line is a header guard. is_header_guard = False if file_extension == 'h': cppvar = GetHeaderGuardCPPVariable(filename) if (line.startswith('#ifndef %s' % cppvar) or line.startswith('#define %s' % cppvar) or line.startswith('#endif // %s' % cppvar)): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. # # URLs can be long too. It's possible to split these, but it makes them # harder to cut&paste. # # The "$Id:...$" comment may also get very long without it being the # developers fault. if (not line.startswith('#include') and not is_header_guard and not Match(r'^\s*//.*http(s?)://\S*$', line) and not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): line_width = GetLineWidth(line) if line_width > 100: error(filename, linenum, 'whitespace/line_length', 4, 'Lines should very rarely be longer than 100 characters') elif line_width > 80: error(filename, linenum, 'whitespace/line_length', 2, 'Lines should be <= 80 characters long') if (cleansed_line.count(';') > 1 and # for loops are allowed two ;'s (and may run over two lines). cleansed_line.find('for') == -1 and (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and # It's ok to have many commands in a switch case that fits in 1 line not ((cleansed_line.find('case ') != -1 or cleansed_line.find('default:') != -1) and cleansed_line.find('break;') != -1)): error(filename, linenum, 'whitespace/newline', 0, 'More than one command on the same line') # Some more style checks CheckBraces(filename, clean_lines, linenum, error) CheckEmptyLoopBody(filename, clean_lines, linenum, error) CheckAccess(filename, clean_lines, linenum, nesting_state, error) CheckSpacing(filename, clean_lines, linenum, nesting_state, error) CheckCheck(filename, clean_lines, linenum, error) CheckAltTokens(filename, clean_lines, linenum, error) classinfo = nesting_state.InnermostClass() if classinfo: CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) _RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') _RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') # Matches the first component of a filename delimited by -s and _s. That is: # _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' # _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' _RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') def _DropCommonSuffixes(filename): """Drops common suffixes like _test.cc or -inl.h from filename. For example: >>> _DropCommonSuffixes('foo/foo-inl.h') 'foo/foo' >>> _DropCommonSuffixes('foo/bar/foo.cc') 'foo/bar/foo' >>> _DropCommonSuffixes('foo/foo_internal.h') 'foo/foo' >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') 'foo/foo_unusualinternal' Args: filename: The input filename. Returns: The filename with the common suffix removed. """ for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', 'inl.h', 'impl.h', 'internal.h'): if (filename.endswith(suffix) and len(filename) > len(suffix) and filename[-len(suffix) - 1] in ('-', '_')): return filename[:-len(suffix) - 1] return os.path.splitext(filename)[0] def _IsTestFilename(filename): """Determines if the given filename has a suffix that identifies it as a test. Args: filename: The input filename. Returns: True if 'filename' looks like a test, False otherwise. """ if (filename.endswith('_test.cc') or filename.endswith('_unittest.cc') or filename.endswith('_regtest.cc')): return True else: return False def _ClassifyInclude(fileinfo, include, is_system): """Figures out what kind of header 'include' is. Args: fileinfo: The current file cpplint is running over. A FileInfo instance. include: The path to a #included file. is_system: True if the #include used <> rather than "". Returns: One of the _XXX_HEADER constants. For example: >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) _C_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) _CPP_SYS_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) _LIKELY_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), ... 'bar/foo_other_ext.h', False) _POSSIBLE_MY_HEADER >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) _OTHER_HEADER """ # This is a list of all standard c++ header files, except # those already checked for above. is_stl_h = include in _STL_HEADERS is_cpp_h = is_stl_h or include in _CPP_HEADERS if is_system: if is_cpp_h: return _CPP_SYS_HEADER else: return _C_SYS_HEADER # If the target file and the include we're checking share a # basename when we drop common extensions, and the include # lives in . , then it's likely to be owned by the target file. target_dir, target_base = ( os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) if target_base == include_base and ( include_dir == target_dir or include_dir == os.path.normpath(target_dir + '/../public')): return _LIKELY_MY_HEADER # If the target and include share some initial basename # component, it's possible the target is implementing the # include, so it's allowed to be first, but we'll never # complain if it's not there. target_first_component = _RE_FIRST_COMPONENT.match(target_base) include_first_component = _RE_FIRST_COMPONENT.match(include_base) if (target_first_component and include_first_component and target_first_component.group(0) == include_first_component.group(0)): return _POSSIBLE_MY_HEADER return _OTHER_HEADER def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): """Check rules that are applicable to #include lines. Strings on #include lines are NOT removed from elided line, to make certain tasks easier. However, to prevent false positives, checks applicable to #include lines in CheckLanguage must be put here. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ fileinfo = FileInfo(filename) line = clean_lines.lines[linenum] # "include" should use the new style "foo/bar.h" instead of just "bar.h" if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): error(filename, linenum, 'build/include', 4, 'Include the directory when naming .h files') # we shouldn't include a file more than once. actually, there are a # handful of instances where doing so is okay, but in general it's # not. match = _RE_PATTERN_INCLUDE.search(line) if match: include = match.group(2) is_system = (match.group(1) == '<') if include in include_state: error(filename, linenum, 'build/include', 4, '"%s" already included at %s:%s' % (include, filename, include_state[include])) else: include_state[include] = linenum # We want to ensure that headers appear in the right order: # 1) for foo.cc, foo.h (preferred location) # 2) c system files # 3) cpp system files # 4) for foo.cc, foo.h (deprecated location) # 5) other google headers # # We classify each include statement as one of those 5 types # using a number of techniques. The include_state object keeps # track of the highest type seen, and complains if we see a # lower type after that. error_message = include_state.CheckNextIncludeOrder( _ClassifyInclude(fileinfo, include, is_system)) if error_message: error(filename, linenum, 'build/include_order', 4, '%s. Should be: %s.h, c system, c++ system, other.' % (error_message, fileinfo.BaseName())) if not include_state.IsInAlphabeticalOrder(include): error(filename, linenum, 'build/include_alpha', 4, 'Include "%s" not in alphabetical order' % include) # Look for any of the stream classes that are part of standard C++. match = _RE_PATTERN_INCLUDE.match(line) if match: include = match.group(2) if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): # Many unit tests use cout, so we exempt them. if not _IsTestFilename(filename): error(filename, linenum, 'readability/streams', 3, 'Streams are highly discouraged.') def _GetTextInside(text, start_pattern): """Retrieves all the text between matching open and close parentheses. Given a string of lines and a regular expression string, retrieve all the text following the expression and between opening punctuation symbols like (, [, or {, and the matching close-punctuation symbol. This properly nested occurrences of the punctuations, so for the text like printf(a(), b(c())); a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. start_pattern must match string having an open punctuation symbol at the end. Args: text: The lines to extract text. Its comments and strings must be elided. It can be single line and can span multiple lines. start_pattern: The regexp string indicating where to start extracting the text. Returns: The extracted text. None if either the opening string or ending punctuation could not be found. """ # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably # rewritten to use _GetTextInside (and use inferior regexp matching today). # Give opening punctuations to get the matching close-punctuations. matching_punctuation = {'(': ')', '{': '}', '[': ']'} closing_punctuation = set(matching_punctuation.itervalues()) # Find the position to start extracting text. match = re.search(start_pattern, text, re.M) if not match: # start_pattern not found in text. return None start_position = match.end(0) assert start_position > 0, ( 'start_pattern must ends with an opening punctuation.') assert text[start_position - 1] in matching_punctuation, ( 'start_pattern must ends with an opening punctuation.') # Stack of closing punctuations we expect to have in text after position. punctuation_stack = [matching_punctuation[text[start_position - 1]]] position = start_position while punctuation_stack and position < len(text): if text[position] == punctuation_stack[-1]: punctuation_stack.pop() elif text[position] in closing_punctuation: # A closing punctuation without matching opening punctuations. return None elif text[position] in matching_punctuation: punctuation_stack.append(matching_punctuation[text[position]]) position += 1 if punctuation_stack: # Opening punctuations left without matching close-punctuations. return None # punctuations match. return text[start_position:position - 1] def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, error): """Checks rules from the 'C++ language rules' section of cppguide.html. Some of these rules are hard to test (function overloading, using uint32 inappropriately), but we do the best we can. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. file_extension: The extension (without the dot) of the filename. include_state: An _IncludeState instance in which the headers are inserted. error: The function to call with any errors found. """ # If the line is empty or consists of entirely a comment, no need to # check it. line = clean_lines.elided[linenum] if not line: return match = _RE_PATTERN_INCLUDE.search(line) if match: CheckIncludeLine(filename, clean_lines, linenum, include_state, error) return # Create an extended_line, which is the concatenation of the current and # next lines, for more effective checking of code that may span more than one # line. if linenum + 1 < clean_lines.NumLines(): extended_line = line + clean_lines.elided[linenum + 1] else: extended_line = line # Make Windows paths like Unix. fullname = os.path.abspath(filename).replace('\\', '/') # TODO(unknown): figure out if they're using default arguments in fn proto. # Check for non-const references in functions. This is tricky because & # is also used to take the address of something. We allow <> for templates, # (ignoring whatever is between the braces) and : for classes. # These are complicated re's. They try to capture the following: # paren (for fn-prototype start), typename, &, varname. For the const # version, we're willing for const to be before typename or after # Don't check the implementation on same line. fnline = line.split('{', 1)[0] if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', fnline))): # We allow non-const references in a few standard places, like functions # called "swap()" or iostream operators like "<<" or ">>". We also filter # out for loops, which lint otherwise mistakenly thinks are functions. if not Search( r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', fnline): error(filename, linenum, 'runtime/references', 2, 'Is this a non-const reference? ' 'If so, make const or use a pointer.') # Check to see if they're using an conversion function cast. # I just try to capture the most common basic types, though there are more. # Parameterless conversion functions, such as bool(), are allowed as they are # probably a member operator declaration or default constructor. match = Search( r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) if match: # gMock methods are defined using some variant of MOCK_METHODx(name, type) # where type may be float(), int(string), etc. Without context they are # virtually indistinguishable from int(x) casts. Likewise, gMock's # MockCallback takes a template parameter of the form return_type(arg_type), # which looks much like the cast we're trying to detect. if (match.group(1) is None and # If new operator, then this isn't a cast not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or Match(r'^\s*MockCallback<.*>', line))): # Try a bit harder to catch gmock lines: the only place where # something looks like an old-style cast is where we declare the # return type of the mocked method, and the only time when we # are missing context is if MOCK_METHOD was split across # multiple lines (for example http://go/hrfhr ), so we only need # to check the previous line for MOCK_METHOD. if (linenum == 0 or not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', clean_lines.elided[linenum - 1])): error(filename, linenum, 'readability/casting', 4, 'Using deprecated casting style. ' 'Use static_cast<%s>(...) instead' % match.group(2)) CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'static_cast', r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) # This doesn't catch all cases. Consider (const char * const)"hello". # # (char *) "foo" should always be a const_cast (reinterpret_cast won't # compile). if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): pass else: # Check pointer casts for other than string constants CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) # In addition, we look for people taking the address of a cast. This # is dangerous -- casts can assign to temporaries, so the pointer doesn't # point where you think. if Search( r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): error(filename, linenum, 'runtime/casting', 4, ('Are you taking an address of a cast? ' 'This is dangerous: could be a temp var. ' 'Take the address before doing the cast, rather than after')) # Check for people declaring static/global STL strings at the top level. # This is dangerous because the C++ language does not guarantee that # globals with constructors are initialized before the first access. match = Match( r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', line) # Make sure it's not a function. # Function template specialization looks like: "string foo(...". # Class template definitions look like: "string Foo::Method(...". if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3)): error(filename, linenum, 'runtime/string', 4, 'For a static/global string constant, use a C style string instead: ' '"%schar %s[]".' % (match.group(1), match.group(2))) # Check that we're not using RTTI outside of testing code. if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): error(filename, linenum, 'runtime/rtti', 5, 'Do not use dynamic_cast<>. If you need to cast within a class ' "hierarchy, use static_cast<> to upcast. Google doesn't support " 'RTTI.') if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): error(filename, linenum, 'runtime/init', 4, 'You seem to be initializing a member variable with itself.') if file_extension == 'h': # TODO(unknown): check that 1-arg constructors are explicit. # How to tell it's a constructor? # (handled in CheckForNonStandardConstructs for now) # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS # (level 1 error) pass # Check if people are using the verboten C basic types. The only exception # we regularly allow is "unsigned short port" for port. if Search(r'\bshort port\b', line): if not Search(r'\bunsigned short port\b', line): error(filename, linenum, 'runtime/int', 4, 'Use "unsigned short" for ports, not "short"') else: match = Search(r'\b(short|long(?! +double)|long long)\b', line) if match: error(filename, linenum, 'runtime/int', 4, 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) # When snprintf is used, the second argument shouldn't be a literal. match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) if match and match.group(2) != '0': # If 2nd arg is zero, snprintf is used to calculate size. error(filename, linenum, 'runtime/printf', 3, 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' 'to snprintf.' % (match.group(1), match.group(2))) # Check if some verboten C functions are being used. if Search(r'\bsprintf\b', line): error(filename, linenum, 'runtime/printf', 5, 'Never use sprintf. Use snprintf instead.') match = Search(r'\b(strcpy|strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Almost always, snprintf is better than %s' % match.group(1)) if Search(r'\bsscanf\b', line): error(filename, linenum, 'runtime/printf', 1, 'sscanf can be ok, but is slow and can overflow buffers.') # Check if some verboten operator overloading is going on # TODO(unknown): catch out-of-line unary operator&: # class X {}; # int operator&(const X& x) { return 42; } // unary operator& # The trick is it's hard to tell apart from binary operator&: # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& if Search(r'\boperator\s*&\s*\(\s*\)', line): error(filename, linenum, 'runtime/operator', 4, 'Unary operator& is dangerous. Do not use it.') # Check for suspicious usage of "if" like # } if (a == b) { if Search(r'\}\s*if\s*\(', line): error(filename, linenum, 'readability/braces', 4, 'Did you mean "else if"? If not, start a new line for "if".') # Check for potential format string bugs like printf(foo). # We constrain the pattern not to pick things like DocidForPrintf(foo). # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) # TODO(sugawarayu): Catch the following case. Need to change the calling # convention of the whole function to process multiple line to handle it. # printf( # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') if printf_args: match = Match(r'([\w.\->()]+)$', printf_args) if match and match.group(1) != '__VA_ARGS__': function_name = re.search(r'\b((?:string)?printf)\s*\(', line, re.I).group(1) error(filename, linenum, 'runtime/printf', 4, 'Potential format string bug. Do %s("%%s", %s) instead.' % (function_name, match.group(1))) # Check for potential memset bugs like memset(buf, sizeof(buf), 0). match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): error(filename, linenum, 'runtime/memset', 4, 'Did you mean "memset(%s, 0, %s)"?' % (match.group(1), match.group(2))) if Search(r'\busing namespace\b', line): error(filename, linenum, 'build/namespaces', 5, 'Do not use namespace using-directives. ' 'Use using-declarations instead.') # Detect variable-length arrays. match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) if (match and match.group(2) != 'return' and match.group(2) != 'delete' and match.group(3).find(']') == -1): # Split the size using space and arithmetic operators as delimiters. # If any of the resulting tokens are not compile time constants then # report the error. tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) is_const = True skip_next = False for tok in tokens: if skip_next: skip_next = False continue if Search(r'sizeof\(.+\)', tok): continue if Search(r'arraysize\(\w+\)', tok): continue tok = tok.lstrip('(') tok = tok.rstrip(')') if not tok: continue if Match(r'\d+', tok): continue if Match(r'0[xX][0-9a-fA-F]+', tok): continue if Match(r'k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue # A catch all for tricky sizeof cases, including 'sizeof expression', # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' # requires skipping the next token because we split on ' ' and '*'. if tok.startswith('sizeof'): skip_next = True continue is_const = False break if not is_const: error(filename, linenum, 'runtime/arrays', 1, 'Do not use variable-length arrays. Use an appropriately named ' "('k' followed by CamelCase) compile-time constant for the size.") # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing # in the class declaration. match = Match( (r'\s*' r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' r'\(.*\);$'), line) if match and linenum + 1 < clean_lines.NumLines(): next_line = clean_lines.elided[linenum + 1] # We allow some, but not all, declarations of variables to be present # in the statement that defines the class. The [\w\*,\s]* fragment of # the regular expression below allows users to declare instances of # the class or pointers to instances, but not less common types such # as function pointers or arrays. It's a tradeoff between allowing # reasonable code and avoiding trying to parse more C++ using regexps. if not Search(r'^\s*}[\w\*,\s]*;', next_line): error(filename, linenum, 'readability/constructors', 3, match.group(1) + ' should be the last thing in the class') # Check for use of unnamed namespaces in header files. Registration # macros are typically OK, so we allow use of "namespace {" on lines # that end with backslashes. if (file_extension == 'h' and Search(r'\bnamespace\s*{', line) and line[-1] != '\\'): error(filename, linenum, 'build/namespaces', 4, 'Do not use unnamed namespaces in header files. See ' 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' ' for more information.') def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, error): """Checks for a C-style cast by looking for the pattern. This also handles sizeof(type) warnings, due to similarity of content. Args: filename: The name of the current file. linenum: The number of the line to check. line: The line of code to check. raw_line: The raw line of code to check, with comments. cast_type: The string for the C++ cast to recommend. This is either reinterpret_cast, static_cast, or const_cast, depending. pattern: The regular expression used to find C-style casts. error: The function to call with any errors found. Returns: True if an error was emitted. False otherwise. """ match = Search(pattern, line) if not match: return False # e.g., sizeof(int) sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) if sizeof_match: error(filename, linenum, 'runtime/sizeof', 1, 'Using sizeof(type). Use sizeof(varname) instead if possible') return True # operator++(int) and operator--(int) if (line[0:match.start(1) - 1].endswith(' operator++') or line[0:match.start(1) - 1].endswith(' operator--')): return False remainder = line[match.end(0):] # The close paren is for function pointers as arguments to a function. # eg, void foo(void (*bar)(int)); # The semicolon check is a more basic function check; also possibly a # function pointer typedef. # eg, void foo(int); or void foo(int) const; # The equals check is for function pointer assignment. # eg, void *(*foo)(int) = ... # The > is for MockCallback<...> ... # # Right now, this will only catch cases where there's a single argument, and # it's unnamed. It should probably be expanded to check for multiple # arguments with some unnamed. function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) if function_match: if (not function_match.group(3) or function_match.group(3) == ';' or ('MockCallback<' not in raw_line and '/*' not in raw_line)): error(filename, linenum, 'readability/function', 3, 'All parameters should be named in a function') return True # At this point, all that should be left is actual casts. error(filename, linenum, 'readability/casting', 4, 'Using C-style cast. Use %s<%s>(...) instead' % (cast_type, match.group(1))) return True _HEADERS_CONTAINING_TEMPLATES = ( ('', ('deque',)), ('', ('unary_function', 'binary_function', 'plus', 'minus', 'multiplies', 'divides', 'modulus', 'negate', 'equal_to', 'not_equal_to', 'greater', 'less', 'greater_equal', 'less_equal', 'logical_and', 'logical_or', 'logical_not', 'unary_negate', 'not1', 'binary_negate', 'not2', 'bind1st', 'bind2nd', 'pointer_to_unary_function', 'pointer_to_binary_function', 'ptr_fun', 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', 'mem_fun_ref_t', 'const_mem_fun_t', 'const_mem_fun1_t', 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', 'mem_fun_ref', )), ('', ('numeric_limits',)), ('', ('list',)), ('', ('map', 'multimap',)), ('', ('allocator',)), ('', ('queue', 'priority_queue',)), ('', ('set', 'multiset',)), ('', ('stack',)), ('', ('char_traits', 'basic_string',)), ('', ('pair',)), ('', ('vector',)), # gcc extensions. # Note: std::hash is their hash, ::hash is our hash ('', ('hash_map', 'hash_multimap',)), ('', ('hash_set', 'hash_multiset',)), ('', ('slist',)), ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') _re_pattern_algorithm_header = [] for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', 'transform'): # Match max(..., ...), max(..., ...), but not foo->max, foo.max or # type::max(). _re_pattern_algorithm_header.append( (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), _template, '')) _re_pattern_templates = [] for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: for _template in _templates: _re_pattern_templates.append( (re.compile(r'(\<|\b)' + _template + r'\s*\<'), _template + '<>', _header)) def FilesBelongToSameModule(filename_cc, filename_h): """Check if these two filenames belong to the same module. The concept of a 'module' here is a as follows: foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the same 'module' if they are in the same directory. some/path/public/xyzzy and some/path/internal/xyzzy are also considered to belong to the same module here. If the filename_cc contains a longer path than the filename_h, for example, '/absolute/path/to/base/sysinfo.cc', and this file would include 'base/sysinfo.h', this function also produces the prefix needed to open the header. This is used by the caller of this function to more robustly open the header file. We don't have access to the real include paths in this context, so we need this guesswork here. Known bugs: tools/base/bar.cc and base/bar.h belong to the same module according to this implementation. Because of this, this function gives some false positives. This should be sufficiently rare in practice. Args: filename_cc: is the path for the .cc file filename_h: is the path for the header path Returns: Tuple with a bool and a string: bool: True if filename_cc and filename_h belong to the same module. string: the additional prefix needed to open the header file. """ if not filename_cc.endswith('.cc'): return (False, '') filename_cc = filename_cc[:-len('.cc')] if filename_cc.endswith('_unittest'): filename_cc = filename_cc[:-len('_unittest')] elif filename_cc.endswith('_test'): filename_cc = filename_cc[:-len('_test')] filename_cc = filename_cc.replace('/public/', '/') filename_cc = filename_cc.replace('/internal/', '/') if not filename_h.endswith('.h'): return (False, '') filename_h = filename_h[:-len('.h')] if filename_h.endswith('-inl'): filename_h = filename_h[:-len('-inl')] filename_h = filename_h.replace('/public/', '/') filename_h = filename_h.replace('/internal/', '/') files_belong_to_same_module = filename_cc.endswith(filename_h) common_path = '' if files_belong_to_same_module: common_path = filename_cc[:-len(filename_h)] return files_belong_to_same_module, common_path def UpdateIncludeState(filename, include_state, io=codecs): """Fill up the include_state with new includes found from the file. Args: filename: the name of the header to read. include_state: an _IncludeState instance in which the headers are inserted. io: The io factory to use to read the file. Provided for testability. Returns: True if a header was succesfully added. False otherwise. """ headerfile = None try: headerfile = io.open(filename, 'r', 'utf8', 'replace') except IOError: return False linenum = 0 for line in headerfile: linenum += 1 clean_line = CleanseComments(line) match = _RE_PATTERN_INCLUDE.search(clean_line) if match: include = match.group(2) # The value formatting is cute, but not really used right now. # What matters here is that the key is in include_state. include_state.setdefault(include, '%s:%d' % (filename, linenum)) return True def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs): """Reports for missing stl includes. This function will output warnings to make sure you are including the headers necessary for the stl containers and functions that you use. We only give one reason to include a header. For example, if you use both equal_to<> and less<> in a .h file, only one (the latter in the file) of these will be reported as a reason to include the . Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. include_state: An _IncludeState instance. error: The function to call with any errors found. io: The IO factory to use to read the header file. Provided for unittest injection. """ required = {} # A map of header name to linenumber and the template entity. # Example of required: { '': (1219, 'less<>') } for linenum in xrange(clean_lines.NumLines()): line = clean_lines.elided[linenum] if not line or line[0] == '#': continue # String is special -- it is a non-templatized type in STL. matched = _RE_PATTERN_STRING.search(line) if matched: # Don't warn about strings in non-STL namespaces: # (We check only the first match per line; good enough.) prefix = line[:matched.start()] if prefix.endswith('std::') or not prefix.endswith('::'): required[''] = (linenum, 'string') for pattern, template, header in _re_pattern_algorithm_header: if pattern.search(line): required[header] = (linenum, template) # The following function is just a speed up, no semantics are changed. if not '<' in line: # Reduces the cpu time usage by skipping lines. continue for pattern, template, header in _re_pattern_templates: if pattern.search(line): required[header] = (linenum, template) # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. # Let's copy the include_state so it is only messed up within this function. include_state = include_state.copy() # Did we find the header for this file (if any) and succesfully load it? header_found = False # Use the absolute path so that matching works properly. abs_filename = FileInfo(filename).FullName() # For Emacs's flymake. # If cpplint is invoked from Emacs's flymake, a temporary file is generated # by flymake and that file name might end with '_flymake.cc'. In that case, # restore original file name here so that the corresponding header file can be # found. # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' # instead of 'foo_flymake.h' abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) # include_state is modified during iteration, so we iterate over a copy of # the keys. header_keys = include_state.keys() for header in header_keys: (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) fullpath = common_path + header if same_module and UpdateIncludeState(fullpath, include_state, io): header_found = True # If we can't find the header file for a .cc, assume it's because we don't # know where to look. In that case we'll give up as we're not sure they # didn't include it in the .h file. # TODO(unknown): Do a better job of finding .h files so we are confident that # not having the .h file means there isn't one. if filename.endswith('.cc') and not header_found: return # All the lines have been processed, report the errors found. for required_header_unstripped in required: template = required[required_header_unstripped][1] if required_header_unstripped.strip('<>"') not in include_state: error(filename, required[required_header_unstripped][0], 'build/include_what_you_use', 4, 'Add #include ' + required_header_unstripped + ' for ' + template) _RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): """Check that make_pair's template arguments are deduced. G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are specified explicitly, and such use isn't intended in any case. Args: filename: The name of the current file. clean_lines: A CleansedLines instance containing the file. linenum: The number of the line to check. error: The function to call with any errors found. """ raw = clean_lines.raw_lines line = raw[linenum] match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) if match: error(filename, linenum, 'build/explicit_make_pair', 4, # 4 = high confidence 'For C++11-compatibility, omit template arguments from make_pair' ' OR use pair directly OR if appropriate, construct a pair directly') def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions=[]): """Processes a single line in the file. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. clean_lines: An array of strings, each representing a line of the file, with comments stripped. line: Number of line being processed. include_state: An _IncludeState instance in which the headers are inserted. function_state: A _FunctionState instance which counts function lines, etc. nesting_state: A _NestingState instance which maintains information about the current stack of nested blocks being parsed. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ raw_lines = clean_lines.raw_lines ParseNolintSuppressions(filename, raw_lines[line], line, error) nesting_state.Update(filename, clean_lines, line, error) if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: return CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) CheckLanguage(filename, clean_lines, line, file_extension, include_state, error) CheckForNonStandardConstructs(filename, clean_lines, line, nesting_state, error) CheckPosixThreading(filename, clean_lines, line, error) CheckInvalidIncrement(filename, clean_lines, line, error) CheckMakePairUsesDeduction(filename, clean_lines, line, error) for check_fn in extra_check_functions: check_fn(filename, clean_lines, line, error) def ProcessFileData(filename, file_extension, lines, error, extra_check_functions=[]): """Performs lint checks and reports any errors to the given error function. Args: filename: Filename of the file that is being processed. file_extension: The extension (dot not included) of the file. lines: An array of strings, each representing a line of the file, with the last element being empty if the file is terminated with a newline. error: A callable to which errors are reported, which takes 4 arguments: filename, line number, error level, and message extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) include_state = _IncludeState() function_state = _FunctionState() nesting_state = _NestingState() ResetNolintSuppressions() CheckForCopyright(filename, lines, error) if file_extension == 'h': CheckForHeaderGuard(filename, lines, error) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines) for line in xrange(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions) nesting_state.CheckClassFinished(filename, error) CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) # We check here rather than inside ProcessLine so that we see raw # lines rather than "cleaned" lines. CheckForUnicodeReplacementCharacters(filename, lines, error) CheckForNewlineAtEOF(filename, lines, error) def ProcessFile(filename, vlevel, extra_check_functions=[]): """Does google-lint on a single file. Args: filename: The name of the file to parse. vlevel: The level of errors to report. Every error of confidence >= verbose_level will be reported. 0 is a good default. extra_check_functions: An array of additional check functions that will be run on each source line. Each function takes 4 arguments: filename, clean_lines, line, error """ _SetVerboseLevel(vlevel) try: # Support the UNIX convention of using "-" for stdin. Note that # we are not opening the file with universal newline support # (which codecs doesn't support anyway), so the resulting lines do # contain trailing '\r' characters if we are reading a file that # has CRLF endings. # If after the split a trailing '\r' is present, it is removed # below. If it is not expected to be present (i.e. os.linesep != # '\r\n' as in Windows), a warning is issued below if this file # is processed. if filename == '-': lines = codecs.StreamReaderWriter(sys.stdin, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace').read().split('\n') else: lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') carriage_return_found = False # Remove trailing '\r'. for linenum in range(len(lines)): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') carriage_return_found = True except IOError: sys.stderr.write( "Skipping input '%s': Can't open for reading\n" % filename) return # Note, if no dot is found, this will give the entire filename as the ext. file_extension = filename[filename.rfind('.') + 1:] # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if filename != '-' and file_extension not in ('cc', 'h', 'cpp', 'hpp'): sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) if carriage_return_found and os.linesep != '\r\n': # Use 0 for linenum since outputting only one error for potentially # several lines. Error(filename, 0, 'whitespace/newline', 1, 'One or more unexpected \\r (^M) found;' 'better to use only a \\n') sys.stderr.write('Done processing %s\n' % filename) def PrintUsage(message): """Prints a brief usage string and exits, optionally with an error message. Args: message: The optional error message. """ sys.stderr.write(_USAGE) if message: sys.exit('\nFATAL ERROR: ' + message) else: sys.exit(1) def PrintCategories(): """Prints a list of all the error-categories used by error messages. These are the categories used to filter messages via --filter. """ sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) sys.exit(0) def ParseArguments(args): """Parses the command line arguments. This may set the output format and verbosity level as side-effects. Args: args: The command line arguments: Returns: The list of filenames to lint. """ try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=', 'root=']) except getopt.GetoptError: PrintUsage('Invalid arguments.') verbosity = _VerboseLevel() output_format = _OutputFormat() filters = '' counting_style = '' for (opt, val) in opts: if opt == '--help': PrintUsage(None) elif opt == '--output': if not val in ('emacs', 'vs7', 'eclipse'): PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') output_format = val elif opt == '--verbose': verbosity = int(val) elif opt == '--filter': filters = val if not filters: PrintCategories() elif opt == '--counting': if val not in ('total', 'toplevel', 'detailed'): PrintUsage('Valid counting options are total, toplevel, and detailed') counting_style = val elif opt == '--root': global _root _root = val if not filenames: PrintUsage('No files were specified.') _SetOutputFormat(output_format) _SetVerboseLevel(verbosity) _SetFilters(filters) _SetCountingStyle(counting_style) return filenames def main(): filenames = ParseArguments(sys.argv[1:]) # Change stderr to write with replacement characters so we don't die # if we try to print something containing non-ASCII characters. sys.stderr = codecs.StreamReaderWriter(sys.stderr, codecs.getreader('utf8'), codecs.getwriter('utf8'), 'replace') _cpplint_state.ResetErrorCounts() for filename in filenames: ProcessFile(filename, _cpplint_state.verbose_level) _cpplint_state.PrintErrorCounts() sys.exit(_cpplint_state.error_count > 0) if __name__ == '__main__': main() sipp-3.6.1/src/0000775000175000017500000000000013730472040012643 5ustar walterwaltersipp-3.6.1/src/call_generation_task.cpp0000664000175000017500000002113413730472040017520 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" class CallGenerationTask *CallGenerationTask::instance = NULL; unsigned long CallGenerationTask::calls_since_last_rate_change = 0; unsigned long CallGenerationTask::last_rate_change_time = 0; void CallGenerationTask::initialize() { assert(instance == NULL); instance = new CallGenerationTask(); } CallGenerationTask::CallGenerationTask() { setRunning(); } CallGenerationTask::~CallGenerationTask() { instance = NULL; } void CallGenerationTask::dump() { WARNING("Uniform rate call generation task: %f", rate); } unsigned int CallGenerationTask::wake() { int retval; if (paused || (users >= 0)) { // When paused or when we're doing user-based rather than // rate-based calls, return a sentinel value to indicate that // this task should wait forever before rescheduling. retval = DONT_RESCHEDULE; } else { float ms_per_call = rate_period_ms/MAX(rate, 1); /* We need to compute when the next call is going to be * opened. The current time is the time when the rate last * changed, plus the number of calls since then multiplied by * the number of milliseconds between each call. * * We then add the number of milliseconds between each call to that * figure. */ retval = (unsigned long) last_rate_change_time + (calls_since_last_rate_change * ms_per_call) + ms_per_call; /* On startup, when last_rate_change_time is 0, this calculation can be 0 (if we're opening multiple calls per ms). But 0 indicates that we should wait forever, so avoid that and return 1 instead. */ if (retval == 0 /* DONT_RESCHEDULE */) { retval = 1; } } return retval; } bool CallGenerationTask::run() { int calls_to_open = 0; if (quitting) { delete this; return false; } if (paused) { setPaused(); return true; } unsigned long long current_calls = main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall); unsigned long long total_calls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); if (users >= 0) { calls_to_open = users - current_calls; } else { float calls_per_ms = rate/rate_period_ms; unsigned int ms_since_last_rate_change = clock_tick - last_rate_change_time; unsigned int expected_total_calls = ms_since_last_rate_change * calls_per_ms; calls_to_open = expected_total_calls - calls_since_last_rate_change; } if (total_calls + calls_to_open > stop_after) { calls_to_open = stop_after - total_calls; } /* We base our scheduling on the number of calls made since the last rate * change, but if we reduce the number of calls we open in order to keep * within the limit, that throws this calculation off and brings CPU% up to * 100%. To avoid this, we increment calls_since_last_rate_change here. */ calls_since_last_rate_change += calls_to_open; if (open_calls_allowed && (current_calls + calls_to_open > open_calls_allowed)) { calls_to_open = open_calls_allowed - current_calls; } if (calls_to_open <= 0) { calls_to_open = 0; } unsigned int start_clock = getmilliseconds(); while(calls_to_open--) { /* Associate a user with this call, if we are in users mode. */ int userid = 0; if (users >= 0) { userid = freeUsers.back(); freeUsers.pop_back(); } // Adding a new outgoing call main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); call* call_ptr = call::add_call(userid, local_ip_is_ipv6, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr); if(!call_ptr) { ERROR("Out of memory allocating call!"); } outbound_congestion = false; if (!multisocket) { switch(transport) { case T_UDP: call_ptr->associate_socket(main_socket); main_socket->ss_count++; break; case T_TCP: case T_SCTP: case T_TLS: call_ptr->associate_socket(tcp_multiplex); tcp_multiplex->ss_count++; break; } } // We shouldn't run for more than 1ms, so as not to tie up the scheduler if (getmilliseconds() > start_clock) { break; } } if (calls_to_open <= 0) { setPaused(); } else { // We stopped before opening all the calls we needed to so as // not to tie up the scheduler - don't pause this task, so // that it gets rescheduled ASAP and can continue. } // Quit after asked number of calls is reached if(total_calls >= stop_after) { quitting = 1; return false; } return true; } void CallGenerationTask::set_paused(bool new_paused) { if (!instance) { /* Doesn't do anything, we must be in server mode. */ return; } if (new_paused) { instance->setPaused(); } else { instance->setRunning(); if (users >= 0) { set_users(users); } else { set_rate(rate); } } paused = new_paused; } void CallGenerationTask::set_rate(double new_rate) { if (!instance) { /* Doesn't do anything, we must be in server mode. */ } rate = new_rate; if(rate < 0) { rate = 0; } last_rate_change_time = getmilliseconds(); calls_since_last_rate_change = 0; if(!open_calls_user_setting) { // Calculate the maximum number of open calls from the rate // and the call duration, unless the user has set a fixed value. int call_duration_min = main_scenario->duration; if (duration > call_duration_min) { call_duration_min = duration; } if (call_duration_min < 1000) { call_duration_min = 1000; } open_calls_allowed = (int)((3.0 * rate * call_duration_min) / (double)rate_period_ms); if(!open_calls_allowed) { open_calls_allowed = 1; } } } void CallGenerationTask::set_users(int new_users) { if (!instance) { /* Doesn't do anything, we must be in server mode. */ return; } if (new_users < 0) { new_users = 0; } assert(users >= 0); while (users < new_users) { int userid; if (!retiredUsers.empty()) { userid = retiredUsers.back(); retiredUsers.pop_back(); } else { userid = users + 1; userVarMap[userid] = new VariableTable(userVariables); } freeUsers.push_front(userid); users++; } users = open_calls_allowed = new_users; last_rate_change_time = clock_tick; calls_since_last_rate_change = 0; assert(open_calls_user_setting); instance->setRunning(); } void CallGenerationTask::free_user(int userId) { if (main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) > open_calls_allowed) { retiredUsers.push_front(userId); } else { freeUsers.push_front(userId); /* Wake up the call creation thread. */ if (instance) { instance->setRunning(); } } } sipp-3.6.1/src/stat.cpp0000664000175000017500000020234513730472040014330 0ustar walterwalter/* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. * Wolfgang Beck * */ #include #include #include #include #include "config.h" #include "sipp.hpp" #include "scenario.hpp" #include "screen.hpp" #include "stat.hpp" /* ** Local definitions (macros) */ #define RESET_COUNTERS(PT) \ memset (PT, 0, CStat::E_NB_COUNTER * sizeof(unsigned long long)) #define RESET_C_COUNTERS \ { \ int i; \ for (i = CStat::CPT_G_C_OutOfCallMsgs; \ i <= CStat::CPT_G_C_AutoAnswered; \ i++) { \ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long)0; \ } \ for (i = CStat::CPT_C_IncomingCallCreated; \ i <= CStat::CPT_C_Retransmissions; \ i++) { \ M_counters[i] = (unsigned long)0; \ } \ for (unsigned int j = 0; j < M_genericMap.size(); j++) { \ M_genericCounters[j * GENERIC_TYPES + GENERIC_C] = 0; \ } \ for (unsigned int j = 0; j < M_rtdMap.size(); j++) { \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] = 0; \ } \ } #define RESET_PD_COUNTERS \ { \ int i; \ for (i = CStat::CPT_G_PD_OutOfCallMsgs; \ i <= CStat::CPT_G_PD_AutoAnswered; \ i++) { \ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long)0; \ } \ for (i = CStat::CPT_PD_IncomingCallCreated; \ i <= CStat::CPT_PD_Retransmissions; \ i++) { \ M_counters[i] = (unsigned long)0; \ } \ for (unsigned int j = 0; j < M_genericMap.size(); j++) { \ M_genericCounters[j * GENERIC_TYPES + GENERIC_PD] = 0; \ } \ for (unsigned int j = 0; j < M_rtdMap.size(); j++) { \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] = 0; \ } \ } #define RESET_PL_COUNTERS \ { \ int i; \ for (i = CStat::CPT_G_PL_OutOfCallMsgs; \ i <= CStat::CPT_G_PL_AutoAnswered; \ i++) { \ M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long)0; \ } \ for (i = CStat::CPT_PL_IncomingCallCreated; \ i <= CStat::CPT_PL_Retransmissions; \ i++) { \ M_counters[i] = (unsigned long)0; \ } \ for (unsigned int j = 0; j < M_genericMap.size(); j++) { \ M_genericCounters[j * GENERIC_TYPES + GENERIC_PL] = 0; \ } \ for (unsigned int j = 0; j < M_rtdMap.size(); j++) { \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] = 0; \ M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] = 0; \ } \ } /* __________________________________________________________________________ C L A S S CS t a t __________________________________________________________________________ */ unsigned long long CStat::M_G_counters[E_NB_G_COUNTER - E_NB_COUNTER]; CStat::~CStat() { int i; for (i = 0; i < nRtds(); i++) { if (M_ResponseTimeRepartition[i] != NULL) { delete [] M_ResponseTimeRepartition[i]; } } free(M_ResponseTimeRepartition); if (M_CallLengthRepartition != NULL) delete [] M_CallLengthRepartition; if(M_outputStream != NULL) { M_outputStream->close(); delete M_outputStream; } if(M_fileName != NULL) delete [] M_fileName; if(M_outputStreamRtt != NULL) { M_outputStreamRtt->close(); delete M_outputStreamRtt; } if(M_fileNameRtt != NULL) delete [] M_fileNameRtt; if(M_dumpRespTime != NULL) delete [] M_dumpRespTime ; free(M_rtdInfo); for (int_str_map::iterator i = M_revRtdMap.begin(); i != M_revRtdMap.end(); ++i) { free(i->second); } M_SizeOfResponseTimeRepartition = 0; M_SizeOfCallLengthRepartition = 0; M_CallLengthRepartition = NULL; M_fileName = NULL; M_outputStream = NULL; M_outputStreamRtt = NULL; M_fileNameRtt = NULL; M_dumpRespTime = NULL; } int CStat::init () { // reset of all counter RESET_COUNTERS(M_counters); GET_TIME (&M_startTime); memcpy (&M_pdStartTime, &M_startTime, sizeof (struct timeval)); memcpy (&M_plStartTime, &M_startTime, sizeof (struct timeval)); M_outputStream = NULL; M_headerAlreadyDisplayed = false; M_outputStreamRtt = NULL; M_headerAlreadyDisplayedRtt = false; std::vector error_codes(0); return(1); } int CStat::isWellFormed(char * P_listeStr, int * nombre) { char * ptr = P_listeStr; int sizeOf; bool isANumber; (*nombre) = 0; sizeOf = strlen(P_listeStr); // getting the number if(sizeOf > 0) { // is the string well formed ? [0-9] [,] isANumber = false; for(int i=0; i<=sizeOf; i++) { switch(ptr[i]) { case ',': if(isANumber == false) { return(0); } else { (*nombre)++; } isANumber = false; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': isANumber = true; break; case '\t': case ' ' : break; case '\0': if(isANumber == false) { return(0); } else { (*nombre)++; } break; default: return(0); } } // enf for } return(1); } int CStat::createIntegerTable(char * P_listeStr, unsigned int ** listeInteger, int * sizeOfList) { int nb=0; char * ptr = P_listeStr; char * ptr_prev = P_listeStr; unsigned int current_int; if(isWellFormed(P_listeStr, sizeOfList) == 1) { (*listeInteger) = new unsigned int[(*sizeOfList)]; while((*ptr) != ('\0')) { if((*ptr) == ',') { sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; ptr_prev = ptr+1; } ptr++; } // on lit le dernier sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; return(1); } return(0); } void CStat::setFileName(const char* P_name, const char* P_extension) { int sizeOf, sizeOfExtension; if(P_name != NULL) { // +6 for PID sizeOf = strlen(P_name) + 6; if(sizeOf > 0) { if(P_extension != NULL) { sizeOfExtension = strlen(P_extension); if(sizeOfExtension > 0) { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[MAX_PATH]; sprintf(M_fileName, "%s_%ld_", P_name, (long) getpid()); strcat(M_fileName, P_extension); } else { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[MAX_PATH]; sprintf(M_fileName, "%s_%ld_", P_name, (long) getpid()); strcat(M_fileName, DEFAULT_EXTENSION); } } else { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[MAX_PATH]; sprintf(M_fileName, "%s_%ld_", P_name, (long) getpid()); strcat(M_fileName, DEFAULT_EXTENSION); } } else { cerr << "new file name length is null - " << "keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } else { cerr << "new file name is NULL ! - keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } void CStat::setFileName(const char* P_name) { int sizeOf; if(P_name != NULL) { sizeOf = strlen(P_name); if(sizeOf > 0) { if(M_fileName != NULL) delete [] M_fileName; M_fileName = new char[sizeOf+1]; strcpy(M_fileName, P_name); } else { cerr << "new file name length is null - " "keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } else { cerr << "new file name is NULL ! - keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } void CStat::initRtt(const char* P_name, const char* P_extension, unsigned long P_report_freq_dumpRtt) { int sizeOf, sizeOfExtension; if(P_name != NULL) { sizeOf = strlen(P_name) ; if(sizeOf > 0) { // 4 for '_rtt' and 6 for pid sizeOf += 10 ; sizeOfExtension = strlen(P_extension); if(M_fileNameRtt != NULL) delete [] M_fileNameRtt; sizeOf += sizeOfExtension; M_fileNameRtt = new char[sizeOf+1]; sprintf (M_fileNameRtt, "%s_%ld_rtt%s", P_name, (long) getpid(),P_extension); } else { cerr << "new file name length is null - " << "keeping the default filename : " << DEFAULT_FILE_NAME << endl; } } else { cerr << "new file name is NULL ! - keeping the default filename : " << DEFAULT_FILE_NAME << endl; } // initiate the table dump response time M_report_freq_dumpRtt = P_report_freq_dumpRtt ; M_dumpRespTime = new T_value_rtt [P_report_freq_dumpRtt] ; if ( M_dumpRespTime == NULL ) { cerr << "Memory allocation failure" << endl; exit(EXIT_FATAL_ERROR); } for (unsigned L_i = 0 ; L_i < P_report_freq_dumpRtt; L_i ++) { M_dumpRespTime[L_i].date = 0.0; M_dumpRespTime[L_i].rtd_no = 0; M_dumpRespTime[L_i].rtt = 0.0; } } void CStat::setRepartitionCallLength(char * P_listeStr) { unsigned int * listeInteger; int sizeOfListe; if(createIntegerTable(P_listeStr, &listeInteger, &sizeOfListe) == 1) { initRepartition(listeInteger, sizeOfListe, &M_CallLengthRepartition, &M_SizeOfCallLengthRepartition); } else { ERROR("Could not create table for call length repartition '%s'", P_listeStr); } delete [] listeInteger; listeInteger = NULL; } void CStat::setRepartitionResponseTime (char * P_listeStr) { unsigned int * listeInteger; int sizeOfListe; int i; for (i = 0; i < nRtds(); i++) { if(createIntegerTable(P_listeStr, &listeInteger, &sizeOfListe) == 1) { initRepartition(listeInteger, sizeOfListe, &M_ResponseTimeRepartition[i], &M_SizeOfResponseTimeRepartition); } else { ERROR("Could not create table for response time repartition '%s'", P_listeStr); } delete [] listeInteger; listeInteger = NULL; } } void CStat::setRepartitionCallLength(unsigned int* repartition, int nombre) { initRepartition(repartition, nombre, &M_CallLengthRepartition, &M_SizeOfCallLengthRepartition); } void CStat::setRepartitionResponseTime(unsigned int* repartition, int nombre) { for (int i = 0; i < nRtds(); i++) { initRepartition(repartition, nombre, &M_ResponseTimeRepartition[i], &M_SizeOfResponseTimeRepartition); } } void CStat::initRepartition(unsigned int* repartition, int nombre, T_dynamicalRepartition ** tabRepartition, int* tabNb) { bool sortDone; int i; unsigned int swap; if((nombre <= 0) || (repartition == NULL) ) { (*tabNb) = 0; (*tabRepartition) = NULL; return; } (*tabNb) = nombre + 1; (*tabRepartition) = new T_dynamicalRepartition[(*tabNb)]; // copying the repartition table in the local table for(i=0; i (*tabRepartition)[i+1].borderMax) { // swapping this two value and setting sortDone to false swap = (*tabRepartition)[i].borderMax; (*tabRepartition)[i].borderMax = (*tabRepartition)[i+1].borderMax; (*tabRepartition)[i+1].borderMax = swap; sortDone = false; } } } // setting the range for max <= value < infinity (*tabRepartition)[nombre].borderMax = (*tabRepartition)[nombre-1].borderMax; (*tabRepartition)[nombre].nbInThisBorder = 0; } int CStat::computeStat (E_Action P_action) { switch (P_action) { case E_CREATE_OUTGOING_CALL : M_counters [CPT_C_OutgoingCallCreated]++; M_counters [CPT_PD_OutgoingCallCreated]++; M_counters [CPT_PL_OutgoingCallCreated]++; M_counters [CPT_C_CurrentCall]++; if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_C_CurrentCallPeak]) { M_counters [CPT_C_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_C_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PD_CurrentCallPeak]) { M_counters [CPT_PD_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PD_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PL_CurrentCallPeak]) { M_counters [CPT_PL_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PL_CurrentCallPeakTime] = clock_tick / 1000; } break; case E_CREATE_INCOMING_CALL : M_counters [CPT_C_IncomingCallCreated]++; M_counters [CPT_PD_IncomingCallCreated]++; M_counters [CPT_PL_IncomingCallCreated]++; M_counters [CPT_C_CurrentCall]++; if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_C_CurrentCallPeak]) { M_counters [CPT_C_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_C_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PD_CurrentCallPeak]) { M_counters [CPT_PD_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PD_CurrentCallPeakTime] = clock_tick / 1000; } if (M_counters[CPT_C_CurrentCall] > M_counters[CPT_PL_CurrentCallPeak]) { M_counters [CPT_PL_CurrentCallPeak] = M_counters[CPT_C_CurrentCall]; M_counters [CPT_PL_CurrentCallPeakTime] = clock_tick / 1000; } break; case E_CALL_FAILED : M_counters [CPT_C_FailedCall]++; M_counters [CPT_PD_FailedCall]++; M_counters [CPT_PL_FailedCall]++; M_counters [CPT_C_CurrentCall]--; break; case E_CALL_SUCCESSFULLY_ENDED : M_counters [CPT_C_SuccessfulCall]++; M_counters [CPT_PD_SuccessfulCall]++; M_counters [CPT_PL_SuccessfulCall]++; M_counters [CPT_C_CurrentCall]--; break; case E_FAILED_CANNOT_SEND_MSG : M_counters [CPT_C_FailedCallCannotSendMessage]++; M_counters [CPT_PD_FailedCallCannotSendMessage]++; M_counters [CPT_PL_FailedCallCannotSendMessage]++; break; case E_FAILED_MAX_UDP_RETRANS : M_counters [CPT_C_FailedCallMaxUdpRetrans]++; M_counters [CPT_PD_FailedCallMaxUdpRetrans]++; M_counters [CPT_PL_FailedCallMaxUdpRetrans]++; break; case E_FAILED_TCP_CONNECT : M_counters [CPT_C_FailedCallTcpConnect]++; M_counters [CPT_PD_FailedCallTcpConnect]++; M_counters [CPT_PL_FailedCallTcpConnect]++; break; case E_FAILED_TCP_CLOSED : M_counters [CPT_C_FailedCallTcpClosed]++; M_counters [CPT_PD_FailedCallTcpClosed]++; M_counters [CPT_PL_FailedCallTcpClosed]++; break; case E_FAILED_UNEXPECTED_MSG : M_counters [CPT_C_FailedCallUnexpectedMessage]++; M_counters [CPT_PD_FailedCallUnexpectedMessage]++; M_counters [CPT_PL_FailedCallUnexpectedMessage]++; break; case E_FAILED_CALL_REJECTED : M_counters [CPT_C_FailedCallCallRejected]++; M_counters [CPT_PD_FailedCallCallRejected]++; M_counters [CPT_PL_FailedCallCallRejected]++; break; case E_FAILED_CMD_NOT_SENT : M_counters [CPT_C_FailedCallCmdNotSent]++; M_counters [CPT_PD_FailedCallCmdNotSent]++; M_counters [CPT_PL_FailedCallCmdNotSent]++; break; case E_FAILED_REGEXP_DOESNT_MATCH : M_counters [CPT_C_FailedCallRegexpDoesntMatch]++; M_counters [CPT_PD_FailedCallRegexpDoesntMatch]++; M_counters [CPT_PL_FailedCallRegexpDoesntMatch]++; break; case E_FAILED_REGEXP_SHOULDNT_MATCH : M_counters [CPT_C_FailedCallRegexpShouldntMatch]++; M_counters [CPT_PD_FailedCallRegexpShouldntMatch]++; M_counters [CPT_PL_FailedCallRegexpShouldntMatch]++; break; case E_FAILED_REGEXP_HDR_NOT_FOUND : M_counters [CPT_C_FailedCallRegexpHdrNotFound]++; M_counters [CPT_PD_FailedCallRegexpHdrNotFound]++; M_counters [CPT_PL_FailedCallRegexpHdrNotFound]++; break; case E_FAILED_OUTBOUND_CONGESTION : M_counters [CPT_C_FailedOutboundCongestion]++; M_counters [CPT_PD_FailedOutboundCongestion]++; M_counters [CPT_PL_FailedOutboundCongestion]++; break; case E_FAILED_TIMEOUT_ON_RECV : M_counters [CPT_C_FailedTimeoutOnRecv]++; M_counters [CPT_PD_FailedTimeoutOnRecv]++; M_counters [CPT_PL_FailedTimeoutOnRecv]++; break; case E_FAILED_TEST_DOESNT_MATCH : M_counters [CPT_C_FailedCallTestDoesntMatch]++; M_counters [CPT_PD_FailedCallTestDoesntMatch]++; M_counters [CPT_PL_FailedCallTestDoesntMatch]++; break; case E_FAILED_TEST_SHOULDNT_MATCH : M_counters [CPT_C_FailedCallTestShouldntMatch]++; M_counters [CPT_PD_FailedCallTestShouldntMatch]++; M_counters [CPT_PL_FailedCallTestShouldntMatch]++; break; case E_FAILED_STRCMP_DOESNT_MATCH : M_counters [CPT_C_FailedCallStrcmpDoesntMatch]++; M_counters [CPT_PD_FailedCallStrcmpDoesntMatch]++; M_counters [CPT_PL_FailedCallStrcmpDoesntMatch]++; break; case E_FAILED_STRCMP_SHOULDNT_MATCH : M_counters [CPT_C_FailedCallStrcmpShouldntMatch]++; M_counters [CPT_PD_FailedCallStrcmpShouldntMatch]++; M_counters [CPT_PL_FailedCallStrcmpShouldntMatch]++; break; case E_FAILED_TIMEOUT_ON_SEND : M_counters [CPT_C_FailedTimeoutOnSend]++; M_counters [CPT_PD_FailedTimeoutOnSend]++; M_counters [CPT_PL_FailedTimeoutOnSend]++; break; case E_RETRANSMISSION : M_counters [CPT_C_Retransmissions]++; M_counters [CPT_PD_Retransmissions]++; M_counters [CPT_PL_Retransmissions]++; break; case E_RESET_C_COUNTERS : RESET_C_COUNTERS; GET_TIME (&M_startTime); break; case E_RESET_PD_COUNTERS : //DEBUG (C_Debug::E_LEVEL_4, "ENTER CASE", "%s", // "CStat::computeStat : RESET_PD_COUNTERS"); RESET_PD_COUNTERS; GET_TIME (&M_pdStartTime); break; case E_RESET_PL_COUNTERS : //DEBUG (C_Debug::E_LEVEL_4, "ENTER CASE", "%s", // "C_Stat::computeStat : RESET_PL_COUNTERS"); RESET_PL_COUNTERS; GET_TIME (&M_plStartTime); if (periodic_rtd) { resetRepartition(M_CallLengthRepartition, M_SizeOfCallLengthRepartition); for (int i = 0; i < nRtds(); i++) { resetRepartition(M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition); } } break; default : ERROR("CStat::ComputeStat() - Unrecognized Action %d", P_action); return (-1); } /* end switch */ return (0); } int CStat::globalStat (E_Action P_action) { switch (P_action) { case E_OUT_OF_CALL_MSGS : M_G_counters [CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_OutOfCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1]++; break; case E_WATCHDOG_MAJOR : M_G_counters [CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_WatchdogMajor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1]++; break; case E_WATCHDOG_MINOR : M_G_counters [CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_WatchdogMinor - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1]++; break; case E_DEAD_CALL_MSGS : M_G_counters [CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_DeadCallMsgs - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1]++; break; case E_FATAL_ERRORS : M_G_counters [CPT_G_C_FatalErrors - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_FatalErrors - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_FatalErrors - E_NB_COUNTER - 1]++; break; case E_WARNING : M_G_counters [CPT_G_C_Warnings - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_Warnings - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_Warnings - E_NB_COUNTER - 1]++; break; case E_AUTO_ANSWERED : // Let's count the automatic answered calls M_G_counters [CPT_G_C_AutoAnswered - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PD_AutoAnswered - E_NB_COUNTER - 1]++; M_G_counters [CPT_G_PL_AutoAnswered - E_NB_COUNTER - 1]++; break; default : ERROR("CStat::ComputeStat() - Unrecognized Action %d", P_action); return (-1); } /* end switch */ return (0); } void CStat::computeRtt (unsigned long long P_start_time, unsigned long long P_stop_time, int which) { M_dumpRespTime[M_counterDumpRespTime].date = (double)P_stop_time / (double)1000; M_dumpRespTime[M_counterDumpRespTime].rtd_no = which; M_dumpRespTime[M_counterDumpRespTime].rtt = ((double)(P_stop_time - P_start_time)) / (double)1000; M_counterDumpRespTime++ ; if (M_counterDumpRespTime > (M_report_freq_dumpRtt - 1)) { dumpDataRtt () ; } } unsigned long long CStat::GetStat (E_CounterName P_counter) { if (P_counter < E_NB_COUNTER) { return M_counters [P_counter]; } else { return M_G_counters [P_counter - E_NB_COUNTER - 1]; } } /* Get the current start time. */ void CStat::getStartTime(struct timeval *t) { memcpy(t, &M_startTime, sizeof(M_startTime)); } /* Use the short form standard deviation formula given the sum of the squares * and the sum. */ double CStat::computeStdev(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed, E_CounterName P_Squares) { if (M_counters[P_NbOfCallUsed] <= 0) return 0.0; double numerator = ((double)(M_counters[P_NbOfCallUsed]) * (double)(M_counters[P_Squares])) - ((double)(M_counters[P_SumCounter] * M_counters[P_SumCounter])); double denominator = (double)(M_counters[P_NbOfCallUsed]) * (((double)(M_counters[P_NbOfCallUsed])) - 1.0); return sqrt(numerator/denominator); } double CStat::computeMean(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed) { if (M_counters[P_NbOfCallUsed] == 0) return 0.0; return ((double)(M_counters[P_SumCounter]) / (double)(M_counters[P_NbOfCallUsed])); } double CStat::computeRtdMean(int which, int type) { unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_COUNT]; unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUM]; if (count == 0) return 0.0; return ((double)(sum) / (double)(count)); } double CStat::computeRtdStdev(int which, int type) { unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_COUNT]; unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUM]; unsigned long long sumsq = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) + RTD_SUMSQ]; if (count <= 1) return 0.0; double numerator = ((double)count * (double)sumsq) - (double)(sum * sum); double denominator = (double)(count) * ((double)(count) - 1.0); return sqrt(numerator/denominator); } void CStat::updateAverageCounter(E_CounterName P_SumCounter, E_CounterName P_NbOfCallUsed, E_CounterName P_Squares, unsigned long P_value) { if (M_counters [P_NbOfCallUsed] <= 0) { M_counters [P_NbOfCallUsed] ++; M_counters [P_SumCounter] = P_value; M_counters [P_Squares] = (P_value * P_value); } else { M_counters [P_SumCounter] += P_value; M_counters [P_Squares] += (P_value * P_value); M_counters [P_NbOfCallUsed] ++; } } int CStat::computeStat (E_Action P_action, unsigned long P_value) { return computeStat(P_action, P_value, 0); } int CStat::findCounter(const char *counter, bool alloc) { str_int_map::iterator it = M_genericMap.find(str_int_map::key_type(counter)); if (it != M_genericMap.end()) { return it->second; } if (!alloc) { return -1; } int ret = M_genericMap.size() + 1; M_genericMap[str_int_map::key_type(counter)] = ret; bool numeric = true; const char *p = counter; while (*p) { if (!isdigit(*p)) { numeric = false; break; } p++; } if (numeric) { char *s = new char[20]; snprintf(s, 20, "GenericCounter%s", counter); M_revGenericMap[ret] = s; M_genericDisplay[ret] = strdup(counter); } else { M_revGenericMap[ret] = strdup(counter); M_genericDisplay[ret] = strdup(counter); } M_genericCounters = (unsigned long long *)realloc(M_genericCounters, sizeof(unsigned long long) * GENERIC_TYPES * M_genericMap.size()); if (!M_genericCounters) { ERROR("Could not allocate generic counters!"); } M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_C] = 0; M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_PD] = 0; M_genericCounters[(ret - 1)* GENERIC_TYPES + GENERIC_PL] = 0; return ret; } int CStat::findRtd(const char *name, bool start) { str_int_map::iterator it = M_rtdMap.find(str_int_map::key_type(name)); if (it != M_rtdMap.end()) { if (start) { rtd_started[it->first] = true; } else { rtd_stopped[it->first] = true; } return it->second; } int ret = M_rtdMap.size() + 1; M_rtdMap[str_int_map::key_type(name)] = ret; M_revRtdMap[ret] = strdup(name); M_rtdInfo = (unsigned long long *)realloc(M_rtdInfo, sizeof(unsigned long long) * RTD_TYPES * GENERIC_TYPES * M_rtdMap.size()); if (!M_rtdInfo) { ERROR("Could not allocate RTD info!"); } M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] = 0; M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] = 0; M_ResponseTimeRepartition = (T_dynamicalRepartition **)realloc(M_ResponseTimeRepartition, sizeof(T_dynamicalRepartition *) * M_rtdMap.size()); if (!M_ResponseTimeRepartition) { ERROR("Could not allocate RTD info!"); } M_ResponseTimeRepartition[ret - 1] = NULL; if (start) { rtd_started[name] = true; } else { rtd_stopped[name] = true; } return ret; } int CStat::nRtds() { return M_rtdMap.size(); } /* If you start an RTD, then you should be interested in collecting statistics for it. */ void CStat::validateRtds() { for (str_int_map::iterator it = rtd_started.begin(); it != rtd_started.end(); it++) { str_int_map::iterator stopit = rtd_stopped.find(it->first); if (stopit == rtd_stopped.end() || !stopit->second) { ERROR("You have started Response Time Duration %s, but have never stopped it!", it->first.c_str()); } } } int CStat::computeStat (E_Action P_action, unsigned long P_value, int which) { switch (P_action) { case E_ADD_CALL_DURATION : // Updating Cumulative Counter updateAverageCounter(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength, CPT_C_AverageCallLength_Squares, P_value); updateRepartition(M_CallLengthRepartition, M_SizeOfCallLengthRepartition, P_value); // Updating Periodical Diplayed counter updateAverageCounter(CPT_PD_AverageCallLength_Sum, CPT_PD_NbOfCallUsedForAverageCallLength, CPT_PD_AverageCallLength_Squares, P_value); // Updating Periodical Logging counter updateAverageCounter(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength, CPT_PL_AverageCallLength_Squares, P_value); break; case E_ADD_GENERIC_COUNTER : M_genericCounters[which * GENERIC_TYPES + GENERIC_C] += P_value; M_genericCounters[which * GENERIC_TYPES + GENERIC_PD] += P_value; M_genericCounters[which * GENERIC_TYPES + GENERIC_PL] += P_value; break; case E_ADD_RESPONSE_TIME_DURATION : // Updating Cumulative Counter M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT]++; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] += P_value; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); updateRepartition(M_ResponseTimeRepartition[which], M_SizeOfResponseTimeRepartition, P_value); // Updating Periodical Diplayed counter M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT]++; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] += P_value; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); // Updating Periodical Logging counter M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT]++; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] += P_value; M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value); break; default : ERROR("CStat::ComputeStat() - Unrecognized Action %d", P_action); return (-1); } /* end switch */ return (0); } void CStat::updateRepartition(T_dynamicalRepartition* P_tabReport, int P_sizeOfTab, unsigned long P_value) { if(P_tabReport == NULL) { return; } for (int i = 0; i < P_sizeOfTab - 1; i++) { if (P_value < P_tabReport[i].borderMax) { P_tabReport[i].nbInThisBorder++; return; } } /* If this is not true, we never should have gotten here. */ assert(P_value >= P_tabReport[P_sizeOfTab-1].borderMax); P_tabReport[P_sizeOfTab-1].nbInThisBorder ++; } void CStat::resetRepartition(T_dynamicalRepartition* P_tabReport, int P_sizeOfTab) { if(P_tabReport == NULL) { return; } for (int i = 0; i < P_sizeOfTab; i++) { P_tabReport[i].nbInThisBorder = 0; } } CStat::CStat () { size_t L_size = 0; L_size += strlen(DEFAULT_FILE_NAME) ; L_size += strlen(DEFAULT_EXTENSION) ; L_size += 1 ; M_fileName = new char[L_size]; strcpy(M_fileName, DEFAULT_FILE_NAME); strcat(M_fileName, DEFAULT_EXTENSION); M_ResponseTimeRepartition = NULL; M_CallLengthRepartition = NULL; M_SizeOfResponseTimeRepartition = 0; M_SizeOfCallLengthRepartition = 0; M_fileNameRtt = NULL; M_genericCounters = NULL; M_time_ref = 0.0 ; M_dumpRespTime = NULL ; M_counterDumpRespTime = 0 ; M_dumpRespTime = NULL; M_fileNameRtt = NULL; M_rtdInfo = NULL; init(); } char* CStat::sRepartitionHeader(T_dynamicalRepartition * tabRepartition, int sizeOfTab, const char * P_repartitionName) { static char *repartitionHeader = NULL; char buffer[MAX_CHAR_BUFFER_SIZE]; int dlen = strlen(stat_delimiter); if(tabRepartition != NULL) { repartitionHeader = (char *)realloc(repartitionHeader, strlen(P_repartitionName) + dlen + 1); sprintf(repartitionHeader, "%s%s", P_repartitionName, stat_delimiter); for(int i=0; i<(sizeOfTab-1); i++) { sprintf(buffer, "%s_<%d%s", P_repartitionName, tabRepartition[i].borderMax, stat_delimiter); repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1); strcat(repartitionHeader, buffer); } sprintf(buffer, "%s_>=%d%s", P_repartitionName, tabRepartition[sizeOfTab-1].borderMax, stat_delimiter); repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1); strcat(repartitionHeader, buffer); } else { repartitionHeader = (char *)realloc(repartitionHeader, 2); strcpy(repartitionHeader, ""); } return(repartitionHeader); } char* CStat::sRepartitionInfo(T_dynamicalRepartition * tabRepartition, int sizeOfTab) { static char *repartitionInfo; char buffer[MAX_CHAR_BUFFER_SIZE]; int dlen = strlen(stat_delimiter); if(tabRepartition != NULL) { // if a repartition is present, this field match the repartition name repartitionInfo = (char *)realloc(repartitionInfo, dlen + 1); sprintf(repartitionInfo, "%s", stat_delimiter); for(int i=0; i<(sizeOfTab-1); i++) { sprintf(buffer, "%lu%s", tabRepartition[i].nbInThisBorder, stat_delimiter); repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1); strcat(repartitionInfo, buffer); } sprintf(buffer, "%lu%s", tabRepartition[sizeOfTab-1].nbInThisBorder, stat_delimiter); repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1); strcat(repartitionInfo, buffer); } else { repartitionInfo = (char *)realloc(repartitionInfo, 2); repartitionInfo[0] = '\0'; } return(repartitionInfo); } void CStat::dumpData () { long localElapsedTime, globalElapsedTime ; struct timeval currentTime; float averageCallRate; float realInstantCallRate; unsigned long numberOfCall; // computing the real call rate GET_TIME (¤tTime); globalElapsedTime = computeDiffTimeInMs (¤tTime, &M_startTime); localElapsedTime = computeDiffTimeInMs (¤tTime, &M_plStartTime); // the call rate is for all the call : incoming and outgoing numberOfCall = (M_counters[CPT_C_IncomingCallCreated] + M_counters[CPT_C_OutgoingCallCreated]); averageCallRate = (globalElapsedTime > 0 ? 1000*(float)numberOfCall/(float)globalElapsedTime : 0.0); numberOfCall = (M_counters[CPT_PL_IncomingCallCreated] + M_counters[CPT_PL_OutgoingCallCreated]); realInstantCallRate = (localElapsedTime > 0 ? 1000*(float)numberOfCall / (float)localElapsedTime : 0.0); if(M_outputStream == NULL) { // if the file is still not opened, we opened it now M_outputStream = new ofstream(M_fileName); M_headerAlreadyDisplayed = false; if(M_outputStream == NULL) { cerr << "Unable to open stat file '" << M_fileName << "' !" << endl; exit(EXIT_FATAL_ERROR); } #ifndef __osf__ if(!M_outputStream->is_open()) { cerr << "Unable to open stat file '" << M_fileName << "' !" << endl; exit(EXIT_FATAL_ERROR); } #endif } if(M_headerAlreadyDisplayed == false) { // header - it's dump in file only one time at the beginning of the file (*M_outputStream) << "StartTime" << stat_delimiter << "LastResetTime" << stat_delimiter << "CurrentTime" << stat_delimiter << "ElapsedTime(P)" << stat_delimiter << "ElapsedTime(C)" << stat_delimiter << "TargetRate" << stat_delimiter << "CallRate(P)" << stat_delimiter << "CallRate(C)" << stat_delimiter << "IncomingCall(P)" << stat_delimiter << "IncomingCall(C)" << stat_delimiter << "OutgoingCall(P)" << stat_delimiter << "OutgoingCall(C)" << stat_delimiter << "TotalCallCreated" << stat_delimiter << "CurrentCall" << stat_delimiter << "SuccessfulCall(P)" << stat_delimiter << "SuccessfulCall(C)" << stat_delimiter << "FailedCall(P)" << stat_delimiter << "FailedCall(C)" << stat_delimiter << "FailedCannotSendMessage(P)" << stat_delimiter << "FailedCannotSendMessage(C)" << stat_delimiter << "FailedMaxUDPRetrans(P)" << stat_delimiter << "FailedMaxUDPRetrans(C)" << stat_delimiter << "FailedTcpConnect(P)" << stat_delimiter << "FailedTcpConnect(C)" << stat_delimiter << "FailedTcpClosed(P)" << stat_delimiter << "FailedTcpClosed(C)" << stat_delimiter << "FailedUnexpectedMessage(P)" << stat_delimiter << "FailedUnexpectedMessage(C)" << stat_delimiter << "FailedCallRejected(P)" << stat_delimiter << "FailedCallRejected(C)" << stat_delimiter << "FailedCmdNotSent(P)" << stat_delimiter << "FailedCmdNotSent(C)" << stat_delimiter << "FailedRegexpDoesntMatch(P)" << stat_delimiter << "FailedRegexpDoesntMatch(C)" << stat_delimiter << "FailedRegexpShouldntMatch(P)" << stat_delimiter << "FailedRegexpShouldntMatch(C)" << stat_delimiter << "FailedRegexpHdrNotFound(P)" << stat_delimiter << "FailedRegexpHdrNotFound(C)" << stat_delimiter << "FailedOutboundCongestion(P)" << stat_delimiter << "FailedOutboundCongestion(C)" << stat_delimiter << "FailedTimeoutOnRecv(P)" << stat_delimiter << "FailedTimeoutOnRecv(C)" << stat_delimiter << "FailedTimeoutOnSend(P)" << stat_delimiter << "FailedTimeoutOnSend(C)" << stat_delimiter << "FailedTestDoesntMatch(P)" << stat_delimiter << "FailedTestDoesntMatch(C)" << stat_delimiter << "FailedTestShouldntMatch(P)" << stat_delimiter << "FailedTestShouldntMatch(C)" << stat_delimiter << "FailedStrcmpDoesntMatch(P)" << stat_delimiter << "FailedStrcmpDoesntMatch(C)" << stat_delimiter << "FailedStrcmpShouldntMatch(P)" << stat_delimiter << "FailedStrcmpShouldntMatch(C)" << stat_delimiter << "OutOfCallMsgs(P)" << stat_delimiter << "OutOfCallMsgs(C)" << stat_delimiter << "DeadCallMsgs(P)" << stat_delimiter << "DeadCallMsgs(C)" << stat_delimiter << "Retransmissions(P)" << stat_delimiter << "Retransmissions(C)" << stat_delimiter << "AutoAnswered(P)" << stat_delimiter << "AutoAnswered(C)" << stat_delimiter << "Warnings(P)" << stat_delimiter << "Warnings(C)" << stat_delimiter << "FatalErrors(P)" << stat_delimiter << "FatalErrors(C)" << stat_delimiter << "WatchdogMajor(P)" << stat_delimiter << "WatchdogMajor(C)" << stat_delimiter << "WatchdogMinor(P)" << stat_delimiter << "WatchdogMinor(C)" << stat_delimiter; for (int i = 1; i <= nRtds(); i++) { char s_P[80]; char s_C[80]; sprintf(s_P, "ResponseTime%s(P)%s", M_revRtdMap[i], stat_delimiter); sprintf(s_C, "ResponseTime%s(C)%s", M_revRtdMap[i], stat_delimiter); (*M_outputStream) << s_P << s_C; sprintf(s_P, "ResponseTime%sStDev(P)%s", M_revRtdMap[i], stat_delimiter); sprintf(s_C, "ResponseTime%sStDev(C)%s", M_revRtdMap[i], stat_delimiter); (*M_outputStream) << s_P << s_C; } (*M_outputStream) << "CallLength(P)" << stat_delimiter << "CallLength(C)" << stat_delimiter; (*M_outputStream) << "CallLengthStDev(P)" << stat_delimiter << "CallLengthStDev(C)" << stat_delimiter; for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) { (*M_outputStream) << M_revGenericMap[i] << "(P)" << stat_delimiter; (*M_outputStream) << M_revGenericMap[i] << "(C)" << stat_delimiter; } for (int i = 1; i <= nRtds(); i++) { char s[80]; sprintf(s, "ResponseTimeRepartition%s", M_revRtdMap[i]); (*M_outputStream) << sRepartitionHeader(M_ResponseTimeRepartition[i - 1], M_SizeOfResponseTimeRepartition, s); } (*M_outputStream) << sRepartitionHeader(M_CallLengthRepartition, M_SizeOfCallLengthRepartition, "CallLengthRepartition"); (*M_outputStream) << endl; M_headerAlreadyDisplayed = true; } // content (*M_outputStream) << formatTime(&M_startTime) << stat_delimiter; (*M_outputStream) << formatTime(&M_plStartTime) << stat_delimiter; (*M_outputStream) << formatTime(¤tTime) << stat_delimiter << msToHHMMSS(localElapsedTime) << stat_delimiter; (*M_outputStream) << msToHHMMSS(globalElapsedTime) << stat_delimiter; if (users >= 0) { (*M_outputStream) << users << stat_delimiter; } else { (*M_outputStream) << rate << stat_delimiter; } (*M_outputStream) << realInstantCallRate << stat_delimiter << averageCallRate << stat_delimiter << M_counters[CPT_PL_IncomingCallCreated] << stat_delimiter << M_counters[CPT_C_IncomingCallCreated] << stat_delimiter << M_counters[CPT_PL_OutgoingCallCreated] << stat_delimiter << M_counters[CPT_C_OutgoingCallCreated] << stat_delimiter << (M_counters[CPT_C_IncomingCallCreated]+ M_counters[CPT_C_OutgoingCallCreated])<< stat_delimiter << M_counters[CPT_C_CurrentCall] << stat_delimiter << M_counters[CPT_PL_SuccessfulCall] << stat_delimiter << M_counters[CPT_C_SuccessfulCall] << stat_delimiter << M_counters[CPT_PL_FailedCall] << stat_delimiter << M_counters[CPT_C_FailedCall] << stat_delimiter << M_counters[CPT_PL_FailedCallCannotSendMessage] << stat_delimiter << M_counters[CPT_C_FailedCallCannotSendMessage] << stat_delimiter << M_counters[CPT_PL_FailedCallMaxUdpRetrans] << stat_delimiter << M_counters[CPT_C_FailedCallMaxUdpRetrans ] << stat_delimiter << M_counters[CPT_PL_FailedCallTcpConnect] << stat_delimiter << M_counters[CPT_C_FailedCallTcpConnect] << stat_delimiter << M_counters[CPT_PL_FailedCallTcpClosed] << stat_delimiter << M_counters[CPT_C_FailedCallTcpClosed] << stat_delimiter << M_counters[CPT_PL_FailedCallUnexpectedMessage] << stat_delimiter << M_counters[CPT_C_FailedCallUnexpectedMessage] << stat_delimiter << M_counters[CPT_PL_FailedCallCallRejected] << stat_delimiter << M_counters[CPT_C_FailedCallCallRejected] << stat_delimiter << M_counters[CPT_PL_FailedCallCmdNotSent] << stat_delimiter << M_counters[CPT_C_FailedCallCmdNotSent] << stat_delimiter << M_counters[CPT_PL_FailedCallRegexpDoesntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallRegexpDoesntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallRegexpShouldntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallRegexpShouldntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallRegexpHdrNotFound] << stat_delimiter << M_counters[CPT_C_FailedCallRegexpHdrNotFound] << stat_delimiter << M_counters[CPT_PL_FailedOutboundCongestion] << stat_delimiter << M_counters[CPT_C_FailedOutboundCongestion] << stat_delimiter << M_counters[CPT_PL_FailedTimeoutOnRecv] << stat_delimiter << M_counters[CPT_C_FailedTimeoutOnRecv] << stat_delimiter << M_counters[CPT_PL_FailedTimeoutOnSend] << stat_delimiter << M_counters[CPT_C_FailedTimeoutOnSend] << stat_delimiter << M_counters[CPT_PL_FailedCallTestDoesntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallTestDoesntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallTestShouldntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallTestShouldntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallStrcmpDoesntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallStrcmpDoesntMatch] << stat_delimiter << M_counters[CPT_PL_FailedCallStrcmpShouldntMatch] << stat_delimiter << M_counters[CPT_C_FailedCallStrcmpShouldntMatch] << stat_delimiter << M_G_counters[CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1] << stat_delimiter << M_counters[CPT_PL_Retransmissions] << stat_delimiter << M_counters[CPT_C_Retransmissions] << stat_delimiter << M_G_counters[CPT_G_PL_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_AutoAnswered - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_Warnings - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_Warnings - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_FatalErrors - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_FatalErrors - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1] << stat_delimiter << M_G_counters[CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1] << stat_delimiter; // SF917289 << M_counters[CPT_C_UnexpectedMessage] << stat_delimiter; for (int i = 1; i <= nRtds(); i++) { (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdMean(i, GENERIC_PL)) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdMean(i, GENERIC_C)) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdStdev(i, GENERIC_PL)) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeRtdStdev(i, GENERIC_C)) << stat_delimiter; } (*M_outputStream) << msToHHMMSSus( (unsigned long)computeMean(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength) ) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeMean(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength) ) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeStdev(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength, CPT_PL_AverageCallLength_Squares )) << stat_delimiter; (*M_outputStream) << msToHHMMSSus( (unsigned long)computeStdev(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength, CPT_C_AverageCallLength_Squares )) << stat_delimiter; for (unsigned int i = 0; i < M_genericMap.size(); i++) { (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_PL] << stat_delimiter; (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_C] << stat_delimiter; } for (int i = 0; i < nRtds(); i++) { (*M_outputStream) << sRepartitionInfo(M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition); } (*M_outputStream) << sRepartitionInfo(M_CallLengthRepartition, M_SizeOfCallLengthRepartition); (*M_outputStream) << endl; // flushing the output file to let the tail -f working ! (*M_outputStream).flush(); } /* end of logData () */ void CStat::dumpDataRtt () { if(M_outputStreamRtt == NULL) { // if the file is still not opened, we opened it now M_outputStreamRtt = new ofstream(M_fileNameRtt); M_headerAlreadyDisplayedRtt = false; if(M_outputStreamRtt == NULL) { cerr << "Unable to open rtt file '" << M_fileNameRtt << "' !" << endl; exit(EXIT_FATAL_ERROR); } #ifndef __osf__ if(!M_outputStreamRtt->is_open()) { cerr << "Unable to open rtt file '" << M_fileNameRtt << "' !" << endl; exit(EXIT_FATAL_ERROR); } #endif } if(M_headerAlreadyDisplayedRtt == false) { (*M_outputStreamRtt) << "Date_ms" << stat_delimiter << "response_time_ms" << stat_delimiter << "rtd_no" << endl; M_headerAlreadyDisplayedRtt = true; } for (unsigned int L_i = 0; L_i < M_counterDumpRespTime ; L_i ++) { (*M_outputStreamRtt) << M_dumpRespTime[L_i].date << stat_delimiter ; (*M_outputStreamRtt) << M_dumpRespTime[L_i].rtt << stat_delimiter ; (*M_outputStreamRtt) << M_revRtdMap[M_dumpRespTime[L_i].rtd_no] << endl; (*M_outputStreamRtt).flush(); M_dumpRespTime[L_i].date = 0.0; M_dumpRespTime[L_i].rtt = 0.0; M_dumpRespTime[L_i].rtd_no = 0; } // flushing the output file (*M_outputStreamRtt).flush(); M_counterDumpRespTime = 0; } /* Time Gestion */ char* CStat::msToHHMMSS (unsigned long P_ms) { static char L_time [TIME_LENGTH]; unsigned long hh, mm, ss; P_ms = P_ms / 1000; hh = P_ms / 3600; mm = (P_ms - hh * 3600) / 60; ss = P_ms - (hh * 3600) - (mm * 60); sprintf (L_time, "%2.2lu:%2.2lu:%2.2lu", hh, mm, ss); return (L_time); } /* end of msToHHMMSS */ char* CStat::msToHHMMSSus (unsigned long P_ms) { static char L_time [TIME_LENGTH]; unsigned long sec, hh, mm, ss, us; sec = P_ms / 1000; hh = sec / 3600; mm = (sec - hh * 3600) / 60; ss = sec - (hh * 3600) - (mm * 60); us = 1000*(P_ms - (hh * 3600000) - (mm * 60000) - (ss*1000)); sprintf (L_time, "%2.2lu:%2.2lu:%2.2lu:%06lu", hh, mm, ss, us); return (L_time); } /* end of msToHHMMSSus */ char* CStat::formatTime (struct timeval* P_tv, bool with_epoch) { static char L_time [TIME_LENGTH]; struct tm * L_currentDate; // Get the current date and time L_currentDate = localtime ((const time_t *)&P_tv->tv_sec); // Format the time if (L_currentDate == NULL) { memset (L_time, 0, TIME_LENGTH); } else { if (with_epoch) { sprintf(L_time, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d.%06ld", L_currentDate->tm_year + 1900, L_currentDate->tm_mon + 1, L_currentDate->tm_mday, L_currentDate->tm_hour, L_currentDate->tm_min, L_currentDate->tm_sec, (long)P_tv->tv_usec); } else { sprintf(L_time, "%4.4d-%2.2d-%2.2d\t%2.2d:%2.2d:%2.2d.%06ld\t%10.10ld.%06ld", L_currentDate->tm_year + 1900, L_currentDate->tm_mon + 1, L_currentDate->tm_mday, L_currentDate->tm_hour, L_currentDate->tm_min, L_currentDate->tm_sec, (long)P_tv->tv_usec, (long)P_tv->tv_sec, /* time_t is int on some bsds */ (long)P_tv->tv_usec); } } return (L_time); } /* end of formatTime */ long CStat::computeDiffTimeInMs (struct timeval* tf, struct timeval* ti) { long v1, v2; v1 = tf->tv_sec - ti->tv_sec; v2 = tf->tv_usec - ti->tv_usec; if (v2 < 0) v2 += 1000000, v1--; return (v1*1000 + v2/1000); } CSample::~CSample() { } /* Implementation of a fixed distribution. */ CFixed::CFixed(double value) { this->value = value; } double CFixed::sample() { return value; } int CFixed::textDescr(char *s, int len) { return snprintf(s, len, "%lf", value); } int CFixed::timeDescr(char *s, int len) { return time_string(value, s, len); } double CFixed::cdfInv(double /*percentile*/) { return value; } /* Implementation of the default pause time. */ CDefaultPause::CDefaultPause() { } double CDefaultPause::sample() { return (double)duration; } int CDefaultPause::textDescr(char *s, int len) { return snprintf(s, len, "%d", duration); } int CDefaultPause::timeDescr(char *s, int len) { return time_string(duration, s, len); } double CDefaultPause::cdfInv(double /*percentile*/) { return duration; } /* Implementation of a uniform distribution. */ static bool uniform_init = false; CUniform::CUniform(double min, double max) { if (!uniform_init) { uniform_init = true; srand(time(NULL)); } this->min = min; this->max = max; } double CUniform::sample() { double rval = ((double)rand())/((double)RAND_MAX); return min + (rval * (max - min)); } int CUniform::textDescr(char *s, int len) { return snprintf(s, len, "%lf/%lf", min, max); } int CUniform::timeDescr(char *s, int len) { int used = time_string(min, s, len); used += snprintf(s + used, len - used, "/"); used += time_string(max, s + used, len - used); return used; } double CUniform::cdfInv(double percentile) { return min + (max * percentile); } #ifdef HAVE_GSL gsl_rng *gsl_init() { static gsl_rng *rng = NULL; if (rng) { return rng; } gsl_rng_env_setup(); rng = gsl_rng_alloc(gsl_rng_default); if (!rng) { ERROR("Could not initialize GSL random number generator"); } return rng; } /* Normal distribution. */ CNormal::CNormal(double mean, double stdev) { this->mean = mean; this->stdev = stdev; rng = gsl_init(); } double CNormal::sample() { double val = gsl_ran_gaussian(rng, stdev); return val + mean; } int CNormal::textDescr(char *s, int len) { return snprintf(s, len, "N(%.3lf,%.3lf)", mean, stdev); } int CNormal::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "N("); used += time_string(mean, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(stdev, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CNormal::cdfInv(double percentile) { return mean + gsl_cdf_gaussian_Pinv(percentile, stdev); } /* Lognormal distribution. */ double CLogNormal::sample() { return gsl_ran_lognormal(rng, mean, stdev); } int CLogNormal::textDescr(char *s, int len) { if (len == 0) return 0; s[0] = 'L'; return 1 + this->CNormal::textDescr(s + 1, len - 1); } int CLogNormal::timeDescr(char *s, int len) { if (len == 0) return 0; s[0] = 'L'; return 1 + this->CNormal::timeDescr(s + 1, len - 1); } double CLogNormal::cdfInv(double percentile) { return gsl_cdf_lognormal_Pinv(percentile, mean, stdev); } /* Exponential distribution. */ CExponential::CExponential(double mean) { this->mean = mean; rng = gsl_init(); } double CExponential::sample() { return gsl_ran_exponential(rng, mean); } int CExponential::textDescr(char *s, int len) { return snprintf(s, len, "Exp(%lf)", mean); } int CExponential::timeDescr(char *s, int len) { int used = snprintf(s, len, "Exp("); used += time_string(mean, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CExponential::cdfInv(double percentile) { return gsl_cdf_exponential_Pinv(percentile, mean); } /* Weibull distribution. */ CWeibull::CWeibull(double lambda, double k) { this->lambda = lambda; this->k = k; rng = gsl_init(); } double CWeibull::sample() { return gsl_ran_weibull(rng, lambda, k); } int CWeibull::textDescr(char *s, int len) { return snprintf(s, len, "Wb(%.3lf,%.3lf)", lambda, k); } int CWeibull::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "Wb("); used += time_string(lambda, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(k, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CWeibull::cdfInv(double percentile) { return gsl_cdf_weibull_Pinv(percentile, lambda, k); } /* Pareto distribution. */ CPareto::CPareto(double k, double xsubm) { this->k = k; this->xsubm = xsubm; rng = gsl_init(); } double CPareto::sample() { return gsl_ran_pareto(rng, k, xsubm); } int CPareto::textDescr(char *s, int len) { return snprintf(s, len, "P(%.3lf,%.3lf)", k, xsubm); } int CPareto::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "P("); used += time_string(k, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(xsubm, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CPareto::cdfInv(double percentile) { return gsl_cdf_pareto_Pinv(percentile, k, xsubm); } /* Generalized Pareto distribution. */ CGPareto::CGPareto(double shape, double scale, double location) { this->shape = shape; this->scale = scale; this->location = location; rng = gsl_init(); } double CGPareto::sample() { return cdfInv(gsl_ran_flat(rng, 0.0, 1.0)); } int CGPareto::textDescr(char *s, int len) { return snprintf(s, len, "P(%.3lf,%.3lf,%.3f)", shape, scale, location); } int CGPareto::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "P("); used += time_string(shape, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(scale, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(location, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CGPareto::cdfInv(double percentile) { return location + ((scale * (pow(percentile, -shape) - 1))/shape); } /* Gamma distribution. */ CGamma::CGamma(double k, double theta) { this->k = k; this->theta = theta; rng = gsl_init(); } double CGamma::sample() { return gsl_ran_gamma(rng, k, theta); } int CGamma::textDescr(char *s, int len) { return snprintf(s, len, "G(%.3lf,%.3lf)", k, theta); } int CGamma::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "G("); used += time_string(k, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(theta, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } double CGamma::cdfInv(double percentile) { return gsl_cdf_gamma_Pinv(percentile, k, theta); } /* NegBin distribution. */ CNegBin::CNegBin(double p, double n) { this->p = p; this->n = n; rng = gsl_init(); } double CNegBin::sample() { return gsl_ran_negative_binomial(rng, n, p); } int CNegBin::textDescr(char *s, int len) { return snprintf(s, len, "NB(%.3lf,%.3lf)", p, n); } int CNegBin::timeDescr(char *s, int len) { int used = 0; used += snprintf(s, len, "NB("); used += time_string(p, s + used, len - used); used += snprintf(s + used, len - used, ","); used += time_string(n, s + used, len - used); used += snprintf(s + used, len - used, ")"); return used; } /* We really don't implement this, but should so that sanity checking will * work. For now, just return zero. */ double CNegBin::cdfInv(double percentile) { return 0; } #endif sipp-3.6.1/src/milenage.c0000664000175000017500000001721013730472040014571 0ustar walterwalter/*------------------------------------------------------------------- * Example algorithms f1, f1*, f2, f3, f4, f5, f5* *------------------------------------------------------------------- * * A sample implementation of the example 3GPP authentication and * key agreement functions f1, f1*, f2, f3, f4, f5 and f5*. This is * a byte-oriented implementation of the functions, and of the block * cipher kernel function Rijndael. * * This has been coded for clarity, not necessarily for efficiency. * * The functions f2, f3, f4 and f5 share the same inputs and have * been coded together as a single function. f1, f1* and f5* are * all coded separately. * *-----------------------------------------------------------------*/ #include "milenage.h" #include "rijndael.h" #include /*--------------------------- prototypes --------------------------*/ /*------------------------------------------------------------------- * Algorithm f1 *------------------------------------------------------------------- * * Computes network authentication code MAC-A from key K, random * challenge RAND, sequence number SQN and authentication management * field AMF. * *-----------------------------------------------------------------*/ void f1(uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2], uint8_t mac_a[8], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t in1[16]; uint8_t out1[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); for (i=0; i<6; i++) { in1[i] = sqn[i]; in1[i+8] = sqn[i]; } for (i=0; i<2; i++) { in1[i+6] = amf[i]; in1[i+14] = amf[i]; } /* XOR op_c and in1, rotate by r1=64, and XOR * * on the constant c1 (which is all zeroes) */ for (i=0; i<16; i++) rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i]; /* XOR on the value temp computed before */ for (i=0; i<16; i++) rijndaelInput[i] ^= temp[i]; RijndaelEncrypt( rijndaelInput, out1 ); for (i=0; i<16; i++) out1[i] ^= op_c[i]; for (i=0; i<8; i++) mac_a[i] = out1[i]; return; } /* end of function f1 */ /*------------------------------------------------------------------- * Algorithms f2-f5 *------------------------------------------------------------------- * * Takes key K and random challenge RAND, and returns response RES, * confidentiality key CK, integrity key IK and anonymity key AK. * *-----------------------------------------------------------------*/ void f2345(uint8_t k[16], uint8_t rand[16], uint8_t res[8], uint8_t ck[16], uint8_t ik[16], uint8_t ak[6], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t out[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); /* To obtain output block OUT2: XOR OPc and TEMP, * * rotate by r2=0, and XOR on the constant c2 (which * * is all zeroes except that the last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[i] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 1; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<8; i++) res[i] = out[i+8]; for (i=0; i<6; i++) ak[i] = out[i]; /* To obtain output block OUT3: XOR OPc and TEMP, * * rotate by r3=32, and XOR on the constant c3 (which * * is all zeroes except that the next to last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[(i+12) % 16] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 2; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<16; i++) ck[i] = out[i]; /* To obtain output block OUT4: XOR OPc and TEMP, * * rotate by r4=64, and XOR on the constant c4 (which * * is all zeroes except that the 2nd from last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[(i+8) % 16] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 4; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<16; i++) ik[i] = out[i]; return; } /* end of function f2345 */ /*------------------------------------------------------------------- * Algorithm f1* *------------------------------------------------------------------- * * Computes resynch authentication code MAC-S from key K, random * challenge RAND, sequence number SQN and authentication management * field AMF. * *-----------------------------------------------------------------*/ void f1star(uint8_t k[16], uint8_t rand[16], uint8_t sqn[6], uint8_t amf[2], uint8_t mac_s[8], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t in1[16]; uint8_t out1[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); for (i=0; i<6; i++) { in1[i] = sqn[i]; in1[i+8] = sqn[i]; } for (i=0; i<2; i++) { in1[i+6] = amf[i]; in1[i+14] = amf[i]; } /* XOR op_c and in1, rotate by r1=64, and XOR * * on the constant c1 (which is all zeroes) */ for (i=0; i<16; i++) rijndaelInput[(i+8) % 16] = in1[i] ^ op_c[i]; /* XOR on the value temp computed before */ for (i=0; i<16; i++) rijndaelInput[i] ^= temp[i]; RijndaelEncrypt( rijndaelInput, out1 ); for (i=0; i<16; i++) out1[i] ^= op_c[i]; for (i=0; i<8; i++) mac_s[i] = out1[i+8]; return; } /* end of function f1star */ /*------------------------------------------------------------------- * Algorithm f5* *------------------------------------------------------------------- * * Takes key K and random challenge RAND, and returns resynch * anonymity key AK. * *-----------------------------------------------------------------*/ void f5star(uint8_t k[16], uint8_t rand[16], uint8_t ak[6], uint8_t op[16]) { uint8_t op_c[16]; uint8_t temp[16]; uint8_t out[16]; uint8_t rijndaelInput[16]; uint8_t i; RijndaelKeySchedule( k ); ComputeOPc( op_c, op ); for (i=0; i<16; i++) rijndaelInput[i] = rand[i] ^ op_c[i]; RijndaelEncrypt( rijndaelInput, temp ); /* To obtain output block OUT5: XOR OPc and TEMP, * * rotate by r5=96, and XOR on the constant c5 (which * * is all zeroes except that the 3rd from last bit is 1). */ for (i=0; i<16; i++) rijndaelInput[(i+4) % 16] = temp[i] ^ op_c[i]; rijndaelInput[15] ^= 8; RijndaelEncrypt( rijndaelInput, out ); for (i=0; i<16; i++) out[i] ^= op_c[i]; for (i=0; i<6; i++) ak[i] = out[i]; return; } /* end of function f5star */ /*------------------------------------------------------------------- * Function to compute OPc from OP and K. Assumes key schedule has already been performed. *-----------------------------------------------------------------*/ void ComputeOPc(uint8_t op_c[16], uint8_t op[16]) { uint8_t i; RijndaelEncrypt( op, op_c ); for (i=0; i<16; i++) op_c[i] ^= op[i]; return; } /* end of function ComputeOPc */ sipp-3.6.1/src/comp.c0000664000175000017500000000323713730472040013752 0ustar walterwalter/* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * */ #define COMP_MAIN #include "comp.h" #include #include char * comp_load(void) { void *handle; char *error; comp_error[0] = 0; handle = dlopen(COMP_PLUGGIN, RTLD_LAZY); if (!handle) { strcpy(comp_error, dlerror()); return comp_error; } *(void **)(&comp_compress) = dlsym(handle, "comp_compress"); if((error = (char *) dlerror())) { strcpy(comp_error, error); return comp_error; } *(void **)(&comp_uncompress) = dlsym(handle, "comp_uncompress"); if((error = (char *) dlerror())) { strcpy(comp_error, error); return comp_error; } *(void **)(&comp_free) = dlsym(handle, "comp_free"); if((error = (char *) dlerror())) { strcpy(comp_error, error); return comp_error; } return 0; } sipp-3.6.1/src/sslsocket.cpp0000664000175000017500000002432113730472040015363 0ustar walterwalter/* * 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 * * Author : Gundu RAO - 16 Jul 2004 * From Hewlett Packard Company. */ #include "sipp.hpp" #include "sslsocket.hpp" #ifdef USE_OPENSSL #define MUTEX_TYPE pthread_mutex_t #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) #define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) #define THREAD_ID pthread_self() #define CALL_BACK_USER_DATA "ksgr" static SSL_CTX* sip_trp_ssl_ctx = NULL; /* For SSL cserver context */ static SSL_CTX* sip_trp_ssl_ctx_client = NULL; /* For SSL cserver context */ static MUTEX_TYPE *mutex_buf = NULL; #if OPENSSL_VERSION_NUMBER < 0x10100000 static void locking_function(int mode, int n, const char *file, int line) { (void)file; /* unused, avoid warnings */ (void)line; /* unused, avoid warnings */ if (mode & CRYPTO_LOCK) MUTEX_LOCK(mutex_buf[n]); else MUTEX_UNLOCK(mutex_buf[n]); } #endif #if !defined(WIN32) && OPENSSL_VERSION_NUMBER < 0x10000000 static unsigned long id_function() { return (unsigned long)THREAD_ID; } #endif static int thread_setup() { int i; mutex_buf = (MUTEX_TYPE *)malloc(sizeof(MUTEX_TYPE) * CRYPTO_num_locks()); if (!mutex_buf) return 0; for (i = 0; i < CRYPTO_num_locks(); ++i) MUTEX_SETUP(mutex_buf[i]); #if !defined(WIN32) && OPENSSL_VERSION_NUMBER < 0x10000000 /* For openssl>=1.0 it uses the address of errno for thread id. * Works for us. */ CRYPTO_set_id_callback(id_function); #endif #if OPENSSL_VERSION_NUMBER < 0x10100000 /* > All OpenSSL code has now been transferred to use the new * > threading API, so the old one is no longer used and can be * > removed. [...] There is now no longer a need to set locking * > callbacks!! * https://github.com/openssl/openssl/commit/ * 2e52e7df518d80188c865ea3f7bb3526d14b0c08 */ CRYPTO_set_locking_callback(locking_function); #endif return 1; } static int passwd_call_back_routine(char *buf, int size, int /*flag*/, void *passwd) { strncpy(buf, (char *)(passwd), size); buf[size - 1] = '\0'; return(strlen(buf)); } /****** SSL error handling *************/ const char *SSL_error_string(int ssl_error, int orig_ret) { switch (ssl_error) { case SSL_ERROR_NONE: return "No error"; case SSL_ERROR_ZERO_RETURN: return "SSL connection has been closed. SSL returned: SSL_ERROR_ZERO_RETURN"; case SSL_ERROR_WANT_WRITE: return "SSL I/O function returned SSL_ERROR_WANT_WRITE"; case SSL_ERROR_WANT_READ: return "SSL I/O function returned SSL_ERROR_WANT_READ"; case SSL_ERROR_WANT_CONNECT: return "SSL I/O function returned SSL_ERROR_WANT_CONNECT"; case SSL_ERROR_WANT_ACCEPT: return "SSL I/O function returned SSL_ERROR_WANT_ACCEPT"; case SSL_ERROR_WANT_X509_LOOKUP: return "SSL I/O function returned SSL_ERROR_WANT_X509_LOOKUP"; case SSL_ERROR_SSL: return "SSL protocol error. SSL I/O function returned SSL_ERROR_SSL"; case SSL_ERROR_SYSCALL: if (orig_ret < 0) { /* not EOF */ return strerror(errno); } else { /* EOF */ return "Non-recoverable I/O error occurred. SSL I/O function returned SSL_ERROR_SYSCALL"; } } return "Unknown SSL Error."; } SSL* SSL_new_client() { return SSL_new(sip_trp_ssl_ctx_client); } SSL* SSL_new_server() { return SSL_new(sip_trp_ssl_ctx); } /****** Certificate Verification Callback FACILITY *************/ static int sip_tls_verify_callback(int ok , X509_STORE_CTX *store) { char data[512]; if (!ok) { X509 *cert = X509_STORE_CTX_get_current_cert(store); X509_NAME_oneline(X509_get_issuer_name(cert), data, 512); WARNING("TLS verification error for issuer: '%s'", data); X509_NAME_oneline(X509_get_subject_name(cert), data, 512); WARNING("TLS verification error for subject: '%s'", data); } return ok; } /*********** Load the CRL's into SSL_CTX **********************/ static int sip_tls_load_crls(SSL_CTX* ctx , const char* crlfile) { X509_STORE *store; X509_LOOKUP *lookup; /* Get the X509_STORE from SSL context */ if (!(store = SSL_CTX_get_cert_store(ctx))) { return (-1); } /* Add lookup file to X509_STORE */ if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()))) { return (-1); } /* Add the CRLS to the lookpup object */ if (X509_load_crl_file(lookup, crlfile, X509_FILETYPE_PEM) != 1) { return (-1); } /* Set the flags of the store so that CRLS's are consulted */ #if OPENSSL_VERSION_NUMBER >= 0x00907000L X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); #else #warning This version of OpenSSL (<0.9.7) cannot handle CRL files in capath ERROR("This version of OpenSSL (<0.9.7) cannot handle CRL files in capath"); #endif return (1); } static SSL_CTX* instantiate_ssl_context(const char* context_name) { SSL_CTX* ssl_ctx = NULL; if (tls_version == 0.0) { #if OPENSSL_VERSION_NUMBER >= 0x10100000 /* >= 1.1 */ ssl_ctx = SSL_CTX_new(TLS_method()); #else ssl_ctx = SSL_CTX_new(SSLv23_method()); #endif } else if (tls_version == 1.0) { ssl_ctx = SSL_CTX_new(TLSv1_method()); } else if (tls_version == 1.1) { ssl_ctx = SSL_CTX_new(TLSv1_1_method()); } else if (tls_version == 1.2) { ssl_ctx = SSL_CTX_new(TLSv1_2_method()); } else { ERROR("Unrecognized TLS version for [%s] context: %1.1f", context_name, tls_version); ssl_ctx = NULL; } #if OPENSSL_VERSION_NUMBER >= 0x10100000 /* >= 1.1 */ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); #endif return ssl_ctx; } #endif //USE_OPENSSL /************* Prepare the SSL context ************************/ enum tls_init_status TLS_init_context(void) { sip_trp_ssl_ctx = instantiate_ssl_context("generic"); if (sip_trp_ssl_ctx == NULL) { ERROR("TLS_init_context: SSL_CTX_new with TLS_method failed for generic context"); return TLS_INIT_ERROR; } sip_trp_ssl_ctx_client = instantiate_ssl_context("client"); if (sip_trp_ssl_ctx_client == NULL) { ERROR("TLS_init_context: SSL_CTX_new with TLS_method failed for client context"); return TLS_INIT_ERROR; } /* Load the trusted CA's */ if (strlen(tls_ca_name) != 0) { SSL_CTX_load_verify_locations(sip_trp_ssl_ctx, tls_ca_name, NULL); SSL_CTX_load_verify_locations(sip_trp_ssl_ctx_client, tls_ca_name, NULL); } /* TLS Verification only makes sense if an CA is specified or * we require CRL validation. */ if (strlen(tls_ca_name) != 0 || strlen(tls_crl_name) != 0) { if (sip_tls_load_crls(sip_trp_ssl_ctx, tls_crl_name) == -1) { ERROR("TLS_init_context: Unable to load CRL file (%s)", tls_crl_name); return TLS_INIT_ERROR; } if (sip_tls_load_crls(sip_trp_ssl_ctx_client, tls_crl_name) == -1) { ERROR("TLS_init_context: Unable to load CRL (client) file (%s)", tls_crl_name); return TLS_INIT_ERROR; } /* The following call forces to process the certificates with * the initialised SSL_CTX */ SSL_CTX_set_verify(sip_trp_ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, sip_tls_verify_callback); SSL_CTX_set_verify(sip_trp_ssl_ctx_client, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, sip_tls_verify_callback); } /* Selection Cipher suits - load the application specified ciphers */ SSL_CTX_set_default_passwd_cb_userdata(sip_trp_ssl_ctx, (void *)CALL_BACK_USER_DATA); SSL_CTX_set_default_passwd_cb_userdata(sip_trp_ssl_ctx_client, (void *)CALL_BACK_USER_DATA); SSL_CTX_set_default_passwd_cb(sip_trp_ssl_ctx, passwd_call_back_routine); SSL_CTX_set_default_passwd_cb(sip_trp_ssl_ctx_client, passwd_call_back_routine); if (SSL_CTX_use_certificate_file(sip_trp_ssl_ctx, tls_cert_name, SSL_FILETYPE_PEM) != 1) { ERROR("TLS_init_context: SSL_CTX_use_certificate_file failed"); return TLS_INIT_ERROR; } if (SSL_CTX_use_certificate_file(sip_trp_ssl_ctx_client, tls_cert_name, SSL_FILETYPE_PEM) != 1) { ERROR("TLS_init_context: SSL_CTX_use_certificate_file (client) failed"); return TLS_INIT_ERROR; } if (SSL_CTX_use_PrivateKey_file(sip_trp_ssl_ctx, tls_key_name, SSL_FILETYPE_PEM) != 1) { ERROR("TLS_init_context: SSL_CTX_use_PrivateKey_file failed"); return TLS_INIT_ERROR; } if (SSL_CTX_use_PrivateKey_file(sip_trp_ssl_ctx_client, tls_key_name, SSL_FILETYPE_PEM) != 1) { ERROR("TLS_init_context: SSL_CTX_use_PrivateKey_file (client) failed"); return TLS_INIT_ERROR; } return TLS_INIT_NORMAL; } int TLS_init() { if (!thread_setup() || !SSL_library_init()) { return -1; } SSL_load_error_strings(); return 1; } sipp-3.6.1/src/ratetask.cpp0000664000175000017500000000424713730472040015174 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" class ratetask *ratetask::instance = NULL; void ratetask::initialize() { assert(instance == NULL); if (rate_increase) { instance = new ratetask(); } } void ratetask::dump() { WARNING("Increasing call rate task."); } bool ratetask::run() { if (quitting >= 10) { delete this; return false; } /* Statistics Logs. */ if ((getmilliseconds() - last_rate_increase_time) >= rate_increase_freq) { if (rate_increase) { rate += rate_increase; if (rate_max && (rate > rate_max)) { rate = rate_max; if (rate_quit) { quitting += 10; } } CallGenerationTask::set_rate(rate); last_rate_increase_time = clock_tick; } } setPaused(); return true; } unsigned int ratetask::wake() { return last_rate_increase_time + rate_increase_freq; } sipp-3.6.1/src/listener.cpp0000664000175000017500000000367013730472040015202 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #include #include #include #include #include #include #include "sipp.hpp" listener_map listeners; listener::listener(const char *id, bool listening) { this->id = strdup(id); this->listening = false; if (listening) { startListening(); } } void listener::startListening() { assert(!listening); listeners.insert(pair(listener_map::key_type(id),this)); listening = true; } void listener::stopListening() { assert(listening); listener_map::iterator listener_it; listener_it = listeners.find(listener_map::key_type(id)); listeners.erase(listener_it); listening = false; } char *listener::getId() { return id; } listener::~listener() { if (listening) { stopListening(); } free(id); id = NULL; } listener *get_listener(const char *id) { listener_map::iterator listener_it = listeners.find(listener_map::key_type(id)); if (listener_it == listeners.end()) { return NULL; } return listener_it->second; } sipp-3.6.1/src/task.cpp0000664000175000017500000002127613730472040014321 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation */ #include #include #include #include #include #include #include #include "sipp.hpp" task_list all_tasks; task_list running_tasks; timewheel paused_tasks; /* Get the overall list of running tasks. */ task_list* get_running_tasks() { return &running_tasks; } void abort_all_tasks() { for (task_list::iterator task_it = all_tasks.begin(); task_it != all_tasks.end(); task_it = all_tasks.begin()) { (*task_it)->abort(); } } void dump_tasks() { WARNING("---- %zu Active Tasks ----", all_tasks.size()); for (task_list::iterator task_it = all_tasks.begin(); task_it != all_tasks.end(); task_it++) { (*task_it)->dump(); } } int expire_paused_tasks() { return paused_tasks.expire_paused_tasks(); } int paused_tasks_count() { return paused_tasks.size(); } // Methods for the task class task::task() { this->taskit = all_tasks.insert(all_tasks.end(), this); add_to_runqueue(); } task::~task() { if (running) { remove_from_runqueue(); } else { paused_tasks.remove_paused_task(this); } all_tasks.erase(taskit); } /* Put this task in the run queue. */ void task::add_to_runqueue() { this->runit = running_tasks.insert(running_tasks.end(), this); this->running = true; } void task::add_to_paused_tasks(bool increment) { paused_tasks.add_paused_task(this, increment); } void task::recalculate_wheel() { add_to_paused_tasks(false); } /* Remove this task from the run queue. */ bool task::remove_from_runqueue() { if (!this->running) { return false; } running_tasks.erase(this->runit); this->running = false; return true; } void task::setRunning() { if (!running) { paused_tasks.remove_paused_task(this); add_to_runqueue(); } } void task::setPaused() { if (running) { if (!remove_from_runqueue()) { WARNING("Tried to remove a running call that wasn't running!"); assert(0); } } else { paused_tasks.remove_paused_task(this); } assert(running == false); add_to_paused_tasks(true); } void task::abort() { delete this; } // Methods for the timewheel class // Based on the time a given task should next be woken up, finds the // correct time wheel for it and returns a list of other tasks // occuring at that point. task_list *timewheel::task2list(task *task) { unsigned int wake = task->wake(); if (wake == 0) { return &forever_list; } assert(wake >= wheel_base); if (wheel_base > clock_tick) { ERROR("wheel_base is %lu, clock_tick is %lu - expected wheel_base to be less than or equal to clock_tick", wheel_base, clock_tick); assert(wheel_base <= clock_tick); } unsigned int slot_in_first_wheel = wake % LEVEL_ONE_SLOTS; unsigned int slot_in_second_wheel = (wake / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS; unsigned int slot_in_third_wheel = (wake / (LEVEL_ONE_SLOTS * LEVEL_TWO_SLOTS)); bool fits_in_first_wheel = ((wake / LEVEL_ONE_SLOTS) == (wheel_base / LEVEL_ONE_SLOTS)); bool fits_in_second_wheel = ((wake / (LEVEL_ONE_SLOTS * LEVEL_TWO_SLOTS)) == (wheel_base / (LEVEL_ONE_SLOTS * LEVEL_TWO_SLOTS))); bool fits_in_third_wheel = (slot_in_third_wheel < LEVEL_THREE_SLOTS); if (fits_in_first_wheel) { return &wheel_one[slot_in_first_wheel]; } else if (fits_in_second_wheel) { return &wheel_two[slot_in_second_wheel]; } else if (fits_in_third_wheel) { return &wheel_three[slot_in_third_wheel]; } else{ ERROR("Attempted to schedule a task too far in the future"); return NULL; } } /* Iterate through our sorted set of paused tasks, removing those that * should no longer be paused, and adding them to the run queue. */ int timewheel::expire_paused_tasks() { int found = 0; // This while loop counts up from the wheel_base (i.e. the time // this function last ran) to the current scheduler time (i.e. clock_tick). while (wheel_base < clock_tick) { int slot1 = wheel_base % LEVEL_ONE_SLOTS; /* If slot1 is 0 (i.e. wheel_base is a multiple of 4096ms), * we need to repopulate the first timer wheel with the * contents of the first available slot of the second wheel. */ if (slot1 == 0) { /* slot2 represents the slot in the second timer wheel * containing the tasks for the next ~4s. So when * wheel_base is 4096, wheel2[1] will be moved into wheel 1, * when wheel_base of 8192 wheel2[2] will be moved into * wheel 1, etc. */ int slot2 = (wheel_base / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS; /* If slot2 is also zero, we must migrate tasks from slot3 into slot2. */ if (slot2 == 0) { /* Same logic above, except that each slot of wheel3 contains the next 69 minutes of tasks, enough to completely fill wheel 2. */ int slot3 = ((wheel_base / LEVEL_ONE_SLOTS) / LEVEL_TWO_SLOTS); assert(slot3 < LEVEL_THREE_SLOTS); for (task_list::iterator l3it = wheel_three[slot3].begin(); l3it != wheel_three[slot3].end(); l3it++) { /* Migrate this task to wheel two. */ (*l3it)->recalculate_wheel(); } wheel_three[slot3].clear(); } /* Repopulate wheel 1 from wheel 2 (which will now be full of the tasks pulled from wheel 3, if that was necessary) */ for (task_list::iterator l2it = wheel_two[slot2].begin(); l2it != wheel_two[slot2].end(); l2it++) { /* Migrate this task to wheel one. */ (*l2it)->recalculate_wheel(); } wheel_two[slot2].clear(); } /* Move tasks from the current slot of wheel 1 (i.e. the tasks scheduled to fire in the 1ms interval represented by wheel_base) onto a run queue. */ found += wheel_one[slot1].size(); for(task_list::iterator it = wheel_one[slot1].begin(); it != wheel_one[slot1].end(); it++) { (*it)->add_to_runqueue(); // Decrement the total number of tasks in this wheel. count--; } wheel_one[slot1].clear(); wheel_base++; // Move wheel_base to the next 1ms interval } return found; } // Adds a task to the correct timewheel. When increment is false, does // not increment the count of tasks owned by this timewheel, and so // can be used for recalculating the wheel of an existing task. void timewheel::add_paused_task(task *task, bool increment) { task_list::iterator task_it; if (task->wake() && task->wake() < wheel_base) { task->add_to_runqueue(); return; } task_list *list = task2list(task); task_it = list->insert(list->end(), task); task->pauselist = list; task->pauseit = task_it; if (increment) { count++; } } void timewheel::remove_paused_task(task *task) { task_list *list = task->pauselist; list->erase(task->pauseit); count--; } timewheel::timewheel() { count = 0; wheel_base = clock_tick; } int timewheel::size() { return count; } sipp-3.6.1/src/prepare_pcap.c0000664000175000017500000004635213730472040015462 0ustar walterwalter/* * 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 * * Author : Guillaume TEISSIER from FTR&D 02/02/2006 */ #include "config.h" #include #include #include #include #include #include #include #include "defines.h" #include "endianshim.h" #include "prepare_pcap.h" #ifndef HAVE_UDP_UH_PREFIX #define uh_ulen len #define uh_sum check #define uh_sport source #define uh_dport dest #endif /* Helpful RFCs for DTMF generation. * https://tools.ietf.org/html/rfc4733 * https://tools.ietf.org/html/rfc3550 */ /* We only need the fields, which are necessary to determine the type of the next header. * we could also define our own structures for UDP and IPv4. We currently use the structures * made available by the platform, as we had no problems to get them on all supported platforms. */ typedef struct _ether_type_hdr { uint16_t ether_type; /* we only need the type, so we can determine, if the next header is IPv4 or IPv6 */ } ether_type_hdr; int check(uint16_t *buffer, int len) { int sum; int i; sum = 0; for (i=0; i<(len&~1); i+= 2) sum += *buffer++; if (len & 1) { sum += htons((*(const uint8_t*)buffer) << 8); } return sum; } uint16_t checksum_carry(int s) { int s_c = (s >> 16) + (s & 0xffff); return (~(s_c + (s_c >> 16)) & 0xffff); } char errbuf[PCAP_ERRBUF_SIZE]; /* get octet offset to EtherType block in 802.11 frame */ size_t get_802_11_ethertype_offset(int link, const uint8_t* pktdata) { size_t offset = 0; uint8_t frame_type = 0; /* 2 bits */ uint8_t frame_sub_type = 0; /* 4 bits */ uint16_t frame_ctl_fld; /* Frame Control Field */ /* get RadioTap header length */ if (link == DLT_IEEE802_11_RADIO) { uint16_t rdtap_hdr_len = 0; /* http://www.radiotap.org */ /* rdtap_version[1], pad[1], rdtap_hdr_len[2], rdtap_flds[4] */ memcpy(&rdtap_hdr_len, pktdata + 2, sizeof(rdtap_hdr_len)); /* http://radiotap.org */ /* all data fields in the radiotap header are to be specified * in little-endian order */ rdtap_hdr_len = le16toh(rdtap_hdr_len); offset += rdtap_hdr_len; } memcpy(&frame_ctl_fld, pktdata + offset, sizeof(frame_ctl_fld)); /* extract frame type and subtype from Frame Control Field */ frame_type = frame_sub_type = frame_ctl_fld>>8; frame_type = frame_type>>2 & 0x03; frame_sub_type >>= 4; if (frame_type < 0x02) { /* Control or Management frame, so ignore it and try to get * EtherType from next one */ offset = 0; } else if (frame_type == 0x02) { /* only Data frames carry the relevant payload and EtherType */ if (frame_sub_type < 0x04 || (frame_sub_type > 0x07 && frame_sub_type < 0x0c)) { /* MAC header of a Data frame is at least 24 and at most 36 * octets long */ size_t mac_hdr_len = 24; uint8_t llc_hdr[8] = { 0x00 }; while (mac_hdr_len <= 36) { /* attempt to get Logical-Link Control header */ /* dsap[1],ssap[1],ctrl_fld[1],org_code[3],ethertype[2] */ memcpy(llc_hdr, pktdata + offset + mac_hdr_len, sizeof(llc_hdr)); /* check if Logical-Link Control header */ if (llc_hdr[0] == 0xaa && llc_hdr[1] == 0xaa && llc_hdr[2] == 0x03) { /* get EtherType and convert to host byte-order. * (reduce by sizeof(eth_type)) */ offset += mac_hdr_len + (sizeof(llc_hdr) - sizeof(uint16_t)); break; } mac_hdr_len++; } } else { /* could be Null Data frame, so ignore it and try to get * EtherType from next one */ offset = 0; } } else { ERROR("Unsupported frame type %d", frame_type); } return offset; } /* get octet offset to EtherType block */ size_t get_ethertype_offset(int link, const uint8_t* pktdata) { int is_le_encoded = 0; /* little endian */ uint16_t eth_type = 0; size_t offset = 0; /* http://www.tcpdump.org/linktypes.html */ if (link == DLT_EN10MB) { /* srcmac[6], dstmac[6], ethertype[2] */ offset = 12; } else if (link == DLT_LINUX_SLL) { /* http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html */ /* pkttype[2], arphrd_type[2], lladdrlen[2], lladdr[8], ethertype[2] */ offset = 14; } else if (link == DLT_IEEE802_11 || link == DLT_IEEE802_11_RADIO) { offset = get_802_11_ethertype_offset(link, pktdata); /* multi-octet fields in 802.11 frame are to be specified in * little-endian order */ is_le_encoded = 1; } else { ERROR("Unsupported link-type %d", link); } if (offset) { /* get EtherType and convert to host byte order */ memcpy(ð_type, pktdata + offset, sizeof(eth_type)); eth_type = (is_le_encoded) ? le16toh(eth_type) : ntohs(eth_type); if (eth_type != 0x0800 && eth_type != 0x86dd) { /* check if Ethernet 802.1Q VLAN */ if (eth_type == 0x8100) { /* vlan_tag[4] */ offset += 4; } else { ERROR("Unsupported ethernet type %d", eth_type); } } } return offset; } /* prepare a pcap file */ int prepare_pkts(const char* file, pcap_pkts* pkts) { pcap_t* pcap; #ifdef HAVE_PCAP_NEXT_EX struct pcap_pkthdr* pkthdr = NULL; #else struct pcap_pkthdr pkthdr_storage; struct pcap_pkthdr* pkthdr = &pkthdr_storage; #endif const uint8_t* pktdata = NULL; int n_pkts = 0; u_long max_length = 0; size_t ether_type_offset = 0; uint16_t base = 0xffff; u_long pktlen; pcap_pkt* pkt_index; ether_type_hdr* ethhdr; struct ip* iphdr; struct ip6_hdr* ip6hdr; struct udphdr* udphdr; pkts->pkts = NULL; pcap = pcap_open_offline(file, errbuf); if (!pcap) ERROR("Can't open PCAP file '%s': %s", file, errbuf); #ifdef HAVE_PCAP_NEXT_EX while (pcap_next_ex(pcap, &pkthdr, &pktdata) == 1) { #else while ((pktdata = pcap_next(pcap, pkthdr)) != NULL) { #endif if (pkthdr->len != pkthdr->caplen) { ERROR("You got truncated packets. Please create a new dump with -s0"); } /* Determine offset from packet to ether type only once. */ if (!ether_type_offset) { int datalink = pcap_datalink(pcap); ether_type_offset = get_ethertype_offset(datalink, pktdata); } ethhdr = (ether_type_hdr *)(pktdata + ether_type_offset); if (ntohs(ethhdr->ether_type) != 0x0800 /* IPv4 */ && ntohs(ethhdr->ether_type) != 0x86dd) { /* IPv6 */ fprintf(stderr, "Ignoring non IP{4,6} packet, got ether_type %hu!\n", ntohs(ethhdr->ether_type)); continue; } iphdr = (struct ip*)((char*)ethhdr + sizeof(*ethhdr)); if (iphdr && iphdr->ip_v == 6) { /* ipv6 */ ip6hdr = (struct ip6_hdr*)(void*)iphdr; if (ip6hdr->ip6_nxt != IPPROTO_UDP) { fprintf(stderr, "prepare_pcap.c: Ignoring non UDP packet!\n"); continue; } udphdr = (struct udphdr*)((char*)ip6hdr + sizeof(*ip6hdr)); } else { /* ipv4 */ if (iphdr->ip_p != IPPROTO_UDP) { fprintf(stderr, "prepare_pcap.c: Ignoring non UDP packet!\n"); continue; } udphdr = (struct udphdr*)((char*)iphdr + (iphdr->ip_hl << 2)); } pktlen = ntohs(udphdr->uh_ulen); if (pktlen > PCAP_MAXPACKET) { ERROR("Packet %d with size 0x%lx is too big! " "Recompile with bigger PCAP_MAXPACKET in prepare_pcap.h", n_pkts, pktlen); } /* BUG: inefficient */ pkts->pkts = (pcap_pkt *)realloc(pkts->pkts, sizeof(*(pkts->pkts)) * (n_pkts + 1)); if (!pkts->pkts) ERROR("Can't re-allocate memory for pcap pkt"); pkt_index = pkts->pkts + n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts = pkthdr->ts; pkt_index->data = (unsigned char *) malloc(pktlen); /* BUG: inefficient */ if (!pkt_index->data) ERROR("Can't allocate memory for pcap pkt data"); memcpy(pkt_index->data, udphdr, pktlen); udphdr->uh_sum = 0; /* compute a partial udp checksum */ /* not including port that will be changed */ /* when sending RTP */ pkt_index->partial_check = check((uint16_t*)&udphdr->uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); if (max_length < pktlen) max_length = pktlen; if (base > ntohs(udphdr->uh_dport)) base = ntohs(udphdr->uh_dport); n_pkts++; } pkts->max = pkts->pkts + n_pkts; pkts->max_length = max_length; pkts->base = base; fprintf(stderr, "In pcap %s, npkts %d\nmax pkt length %lu\nbase port %d\n", file, n_pkts, max_length, base); pcap_close(pcap); return 0; } struct rtphdr { /* Bit-fields are always assigned to the first available bit, possibly * constrained by other factors, such as alignment. That means that they * start at the low order bit for little-endian, and the high order bit * for big-endian. This is the "right" way to do things. It is very * unusual for a compiler to do this differently. */ #if BYTE_ORDER == LITTLE_ENDIAN uint8_t csicnt:4; uint8_t extension:1; uint8_t padding:1; uint8_t version:2; uint8_t payload_type:7; uint8_t marker:1; #elif BYTE_ORDER == BIG_ENDIAN uint8_t version:2; uint8_t padding:1; uint8_t extension:1; uint8_t csicnt:4; uint8_t marker:1; uint8_t payload_type:7; #else #error "Please fix endian macros" #endif uint16_t seqno; uint32_t timestamp; uint32_t ssrcid; }; struct rtpevent { uint8_t event_id; #if BYTE_ORDER == LITTLE_ENDIAN uint8_t volume:6; uint8_t reserved:1; uint8_t end_of_event:1; #elif BYTE_ORDER == BIG_ENDIAN uint8_t end_of_event:1; uint8_t reserved:1; uint8_t volume:6; #else #error "Please fix endian macros" #endif uint16_t duration; }; struct dtmfpacket { struct udphdr udp; struct rtphdr rtp; struct rtpevent dtmf; }; struct rtpnoop { #if BYTE_ORDER == LITTLE_ENDIAN uint32_t reserved:31; uint32_t request_rtcp:1; #elif BYTE_ORDER == BIG_ENDIAN uint32_t request_rtcp:1; uint32_t reserved:31; #else #error "Please fix endian macros" #endif }; struct nooppacket { struct udphdr udp; struct rtphdr rtp; struct rtpnoop noop; }; static u_long dtmf_ssrcid = 0x01020304; /* bug, should be random/unique */ static void fill_default_udphdr(struct udphdr* udp, u_long pktlen) { udp->uh_ulen = htons(pktlen); udp->uh_sum = 0; udp->uh_sport = 0; udp->uh_dport = 0; } static void fill_default_rtphdr(struct rtphdr* rtp, int marker, int seqno, int ts) { rtp->version = 2; rtp->padding = 0; rtp->extension = 0; rtp->csicnt = 0; rtp->marker = marker; rtp->payload_type = 0x60; /* 96 as in the SDP */ rtp->seqno = htons(seqno); rtp->timestamp = htonl(ts); rtp->ssrcid = htonl(dtmf_ssrcid); } static void fill_default_dtmf(struct dtmfpacket* dtmfpacket, int marker, int seqno, int ts, char digit, int eoe, int duration) { const u_long pktlen = sizeof(*dtmfpacket); fill_default_udphdr(&dtmfpacket->udp, pktlen); fill_default_rtphdr(&dtmfpacket->rtp, marker, seqno, ts); dtmfpacket->dtmf.event_id = digit; dtmfpacket->dtmf.end_of_event = eoe; dtmfpacket->dtmf.volume = 10; dtmfpacket->dtmf.duration = htons(duration * 8); } static void fill_default_noop(struct nooppacket* nooppacket, int seqno, int ts) { const u_long pktlen = sizeof(*nooppacket); fill_default_udphdr(&nooppacket->udp, pktlen); fill_default_rtphdr(&nooppacket->rtp, 0, seqno, ts); nooppacket->rtp.payload_type = 0x61; /* 97 for noop */ nooppacket->noop.request_rtcp = 0; nooppacket->noop.reserved = 0; } static void prepare_dtmf_digit_start( pcap_pkts* pkts, int* n_pkts, uint16_t start_seq_no, int n_digits, unsigned char uc_digit, int tone_len, unsigned long ts_offset, unsigned timestamp_start) { const u_long pktlen = sizeof(struct dtmfpacket); unsigned long cur_tone_len = 0; int marked = 0; while (cur_tone_len < tone_len) { unsigned long ts = ts_offset + (n_digits + 1) * tone_len * 2 + cur_tone_len; pcap_pkt* pkt_index; struct dtmfpacket* dtmfpacket; /* BUG: inefficient */ pkts->pkts = realloc(pkts->pkts, sizeof(*pkts->pkts) * (*n_pkts + 1)); if (!pkts->pkts) { ERROR("Can't re-allocate memory for dtmf pcap pkt"); } pkt_index = pkts->pkts + *n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts.tv_sec = ts / 1000; pkt_index->ts.tv_usec = (ts % 1000) * 1000; pkt_index->data = malloc(pktlen); /* BUG: inefficient */ if (!pkt_index->data) { ERROR("Can't allocate memory for pcap pkt data"); } dtmfpacket = (struct dtmfpacket*)pkt_index->data; fill_default_dtmf(dtmfpacket, !marked, *n_pkts + start_seq_no, n_digits * tone_len * 2 + timestamp_start, uc_digit, 0, cur_tone_len); marked = 1; /* set marker once per event */ pkt_index->partial_check = check(&dtmfpacket->udp.uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); (*n_pkts)++; cur_tone_len += 20; } } static void prepare_dtmf_digit_end( pcap_pkts* pkts, int* n_pkts, uint16_t start_seq_no, int n_digits, unsigned char uc_digit, int tone_len, unsigned long ts_offset, unsigned timestamp_start) { const u_long pktlen = sizeof(struct dtmfpacket); int i; for (i = 0; i < 3; i++) { unsigned long ts = ts_offset + (n_digits + 1) * tone_len * 2 + tone_len + i + 1; pcap_pkt* pkt_index; struct dtmfpacket* dtmfpacket; /* BUG: inefficient */ pkts->pkts = realloc(pkts->pkts, sizeof(*pkts->pkts) * (*n_pkts + 1)); if (!pkts->pkts) { ERROR("Can't re-allocate memory for dtmf pcap pkt"); } pkt_index = pkts->pkts + *n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts.tv_sec = ts / 1000; pkt_index->ts.tv_usec = (ts % 1000) * 1000; pkt_index->data = malloc(pktlen); if (!pkt_index->data) { ERROR("Can't allocate memory for pcap pkt data"); } dtmfpacket = (struct dtmfpacket*)pkt_index->data; fill_default_dtmf(dtmfpacket, 0, *n_pkts + start_seq_no, n_digits * tone_len * 2 + timestamp_start, uc_digit, 1, tone_len); pkt_index->partial_check = check(&dtmfpacket->udp.uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); (*n_pkts)++; } } static void prepare_noop( pcap_pkts* pkts, int* n_pkts, uint16_t* start_seq_no, unsigned long *ts_offset, unsigned *timestamp_start) { const u_long pktlen = sizeof(struct nooppacket); /* not dtmfpacket */ int i; for (i = 0; i < 20; i++) { /* 400ms of nothingness */ unsigned long ts = *ts_offset; pcap_pkt* pkt_index; struct nooppacket* nooppacket; *ts_offset += 20; /* BUG: inefficient */ pkts->pkts = realloc(pkts->pkts, sizeof(*pkts->pkts) * (*n_pkts + 1)); if (!pkts->pkts) { ERROR("Can't re-allocate memory for noop pcap pkt"); } pkt_index = pkts->pkts + *n_pkts; pkt_index->pktlen = pktlen; pkt_index->ts.tv_sec = ts / 1000; pkt_index->ts.tv_usec = (ts % 1000) * 1000; pkt_index->data = malloc(pktlen); if (!pkt_index->data) { ERROR("Can't allocate memory for pcap pkt data"); } nooppacket = (struct nooppacket*)pkt_index->data; fill_default_noop(nooppacket, *n_pkts + *start_seq_no, *timestamp_start + ts); pkt_index->partial_check = check(&nooppacket->udp.uh_ulen, pktlen - 4) + ntohs(IPPROTO_UDP + pktlen); (*n_pkts)++; (*start_seq_no)++; } *timestamp_start += *ts_offset; } /* prepare a dtmf pcap */ int prepare_dtmf(const char* digits, pcap_pkts* pkts, uint16_t start_seq_no) { unsigned long tone_len = 200; const u_long pktlen = sizeof(struct dtmfpacket); int n_pkts = 0; int n_digits = 0; int needs_filler = 0; /* warm up the stream */ const char* digit; unsigned long ts_offset = 0; /* packet timestamp */ unsigned timestamp_start = 24000; /* RTP timestamp, should be random */ /* If we see the DTMF as part of the entire audio stream, we'd need * to reuse the SSRC, but it's legal to start a new stream (new * SSRC) like we do. Note that the new SSRC that will cause some * devices to not pick up on the first event as quickly: we can work * around that by adding a few empty RTP packets with this SSRC * first. */ dtmf_ssrcid++; /* Because we need to warm up the stream (puncture NAT, make phone * accept the SSRC(?)), we add a few filler packets first. */ needs_filler = 1; pkts->pkts = NULL; char* comma = strchr(digits, ','); if (comma) { tone_len = atol(comma + 1); if (tone_len < 50 || tone_len > 2000) { tone_len = 200; } *comma = '\0'; } for (digit = digits; *digit; digit++) { unsigned char uc_digit; if (*digit >= '0' && *digit <= '9') { uc_digit = *digit - '0'; } else if (*digit == '*') { uc_digit = 10; } else if (*digit == '#') { uc_digit = 11; } else if (*digit == 'A') { uc_digit = 12; } else if (*digit == 'B') { uc_digit = 13; } else if (*digit == 'C') { uc_digit = 14; } else if (*digit == 'D') { uc_digit = 15; } else { continue; } if (needs_filler) { prepare_noop(pkts, &n_pkts, &start_seq_no, &ts_offset, ×tamp_start); needs_filler = 0; } prepare_dtmf_digit_start(pkts, &n_pkts, start_seq_no, n_digits, uc_digit, tone_len, ts_offset, timestamp_start); prepare_dtmf_digit_end(pkts, &n_pkts, start_seq_no, n_digits, uc_digit, tone_len, ts_offset, timestamp_start); n_digits++; } pkts->max = pkts->pkts + n_pkts; pkts->max_length = pktlen; pkts->base = 0; return n_pkts; } sipp-3.6.1/src/time.cpp0000664000175000017500000000635313730472040014314 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Stefan Esser * Andy Aicken */ #include #include #include #ifdef __MACH__ #include #include #endif #include "time.hpp" #include "sipp.hpp" #define MICROSECONDS_PER_SECOND 1000000LL #define MICROSECONDS_PER_MILLISECOND 1000LL #define NANOSECONDS_PER_MICROSECOND 1000LL // Returns the number of microseconds that have passed since SIPp // started. Also updates the current clock_tick. unsigned long long getmicroseconds() { struct timespec time; unsigned long long microseconds; static unsigned long long start_time = 0; #ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); time.tv_sec = mts.tv_sec; time.tv_nsec = mts.tv_nsec; #else #if defined(CLOCK_MONOTONIC_COARSE) clock_gettime(CLOCK_MONOTONIC_COARSE, &time); #else clock_gettime(CLOCK_MONOTONIC, &time); #endif #endif microseconds = (MICROSECONDS_PER_SECOND * time.tv_sec) + (time.tv_nsec / NANOSECONDS_PER_MICROSECOND); if (start_time == 0) { start_time = microseconds - 1; } microseconds = microseconds - start_time; // Static global from sipp.hpp clock_tick = microseconds / MICROSECONDS_PER_MILLISECOND; return microseconds; } // Returns the number of milliseconds that have passed since SIPp // started. Also updates the current clock_tick. unsigned long getmilliseconds() { return getmicroseconds() / MICROSECONDS_PER_MILLISECOND; } // Sleeps for the given number of microseconds. Avoids the potential // EINVAL when using usleep() to sleep for a second or more. void sipp_usleep(unsigned long usec) { if (usec >= 1000000) { sleep(usec / 1000000); } usec %= 1000000; usleep(usec); } sipp-3.6.1/src/message.cpp0000664000175000017500000005001613730472040014775 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Stefan Esser * Andy Aicken */ #include "sipp.hpp" #include "message.hpp" struct KeywordMap { const char *keyword; MessageCompType type; }; typedef std::map kw_map; kw_map keyword_map; /* These keywords take no parameters. */ struct KeywordMap SimpleKeywords[] = { {"remote_ip", E_Message_Remote_IP }, {"remote_host", E_Message_Remote_Host }, {"remote_port", E_Message_Remote_Port }, {"transport", E_Message_Transport }, {"local_ip", E_Message_Local_IP }, {"local_ip_type", E_Message_Local_IP_Type }, {"local_port", E_Message_Local_Port }, {"server_ip", E_Message_Server_IP }, {"media_ip", E_Message_Media_IP }, #ifdef PCAPPLAY {"auto_media_port", E_Message_Auto_Media_Port }, #endif #ifdef RTP_STREAM /* Legacy since 3.6-dev. Actually uses media_port. */ {"rtpstream_audio_port", E_Message_RTPStream_Audio_Port }, {"rtpstream_video_port", E_Message_RTPStream_Video_Port }, #endif {"media_port", E_Message_Media_Port }, {"media_ip_type", E_Message_Media_IP_Type }, {"call_number", E_Message_Call_Number }, {"dynamic_id", E_Message_DynamicId }, // wrapping global counter {"call_id", E_Message_Call_ID }, {"cseq", E_Message_CSEQ }, {"pid", E_Message_PID }, {"service", E_Message_Service }, {"branch", E_Message_Branch }, {"msg_index", E_Message_Index }, {"next_url", E_Message_Next_Url }, {"len", E_Message_Len }, {"peer_tag_param", E_Message_Peer_Tag_Param }, {"last_Request_URI", E_Message_Last_Request_URI }, {"last_cseq_number", E_Message_Last_CSeq_Number }, {"last_message", E_Message_Last_Message }, {"routes", E_Message_Routes }, {"tdmmap", E_Message_TDM_Map }, {"clock_tick", E_Message_ClockTick }, {"users", E_Message_Users }, {"userid", E_Message_UserID }, {"timestamp", E_Message_Timestamp }, {"date", E_Message_Date }, {"sipp_version", E_Message_SippVersion }, }; #define KEYWORD_SIZE 256 static char* quoted_strchr(const char* s, int c) { const char* p; for (p = s; *p && *p != c; p++) { if (*p == '"') { p++; p += strcspn(p, "\""); } } return *p == c ? const_cast(p) : NULL; } SendingMessage::SendingMessage(scenario* msg_scenario, const char* const_src, bool skip_sanity) { char * src = strdup(const_src); char * osrc = src; char * literal; int literalLen; char * dest; char * key; char current_line[MAX_HEADER_LEN]; char * line_mark = NULL; char * tsrc; int num_cr = get_cr_number(src); this->msg_scenario = msg_scenario; dest = literal = (char *)malloc(strlen(src) + num_cr + 1); current_line[0] = '\0'; *dest = 0; while(*src) { if (current_line[0] == '\0') { line_mark = strchr(src, '\n'); if (line_mark) { int header_len = line_mark - src; if (header_len > MAX_HEADER_LEN-1) header_len = MAX_HEADER_LEN-1; memcpy(current_line, src, header_len); current_line[header_len] = '\0'; } } /* This hex encoding could be done in XML parsing, allowing us to skip * these conditionals and branches. */ if ((*src == '\\') && (*(src+1) == 'x')) { /* Allows any hex coded char like '\x5B' ([) */ src += 2; if (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); } *dest++ = val & 0xff; } src++; } else if (*src == '\n') { *dest++ = '\r'; *dest++ = *src++; current_line[0] = '\0'; } else if (*src != '[') { *dest++ = *src++; } else { /* We have found a keyword, store the literal that we have been generating. */ literalLen = dest - literal; if (literalLen) { *dest = '\0'; literal = (char *)realloc(literal, literalLen + 1); if (!literal) { ERROR("Out of memory!"); } MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } newcomp->type = E_Message_Literal; newcomp->literal = literal; newcomp->literalLen = literalLen; // length without the terminator messageComponents.push_back(newcomp); } else { free(literal); } dest = literal = (char *)malloc(strlen(src) + num_cr + 1); *dest = '\0'; /* Now lets determine which keyword we have. */ MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } char keyword [KEYWORD_SIZE+1]; src++; tsrc = quoted_strchr(src, '['); key = quoted_strchr(src, ']'); if ((tsrc) && (tsrc KEYWORD_SIZE) || (!(key - src))) { ERROR("Syntax error or invalid [keyword] in scenario while parsing '%s'", current_line); } memcpy(keyword, src, key - src); keyword[key - src] = 0; src = key + 1; // allow +/-n for numeric variables newcomp->offset = 0; if ((strncmp(keyword, "authentication", strlen("authentication")) && strncmp(keyword, "tdmmap", strlen("tdmmap"))) && ((key = strchr(keyword,'+')) || (key = strchr(keyword,'-')))) { if (isdigit(*(key+1))) { newcomp->offset = atoi(key); *key = 0; } } char *spc = NULL; char ospc; if ((spc = strchr(keyword, ' '))) { ospc = *spc; *spc = '\0'; } kw_map::iterator it = keyword_map.find(keyword); if (spc) { *spc = ospc; } if (it != keyword_map.end()) { newcomp->type = E_Message_Custom; newcomp->comp_param.fxn = it->second; messageComponents.push_back(newcomp); continue; } bool simple_keyword = false; for (unsigned int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) { if (!strcmp(keyword, SimpleKeywords[i].keyword)) { newcomp->type = SimpleKeywords[i].type; simple_keyword = true; break; } } if (simple_keyword) { messageComponents.push_back(newcomp); continue; } if(!strncmp(keyword, "field", strlen("field"))) { newcomp->type = E_Message_Injection; /* Parse out the interesting things like file and number. */ newcomp->comp_param.field_param.field = atoi(keyword + strlen("field")); char fileName[KEYWORD_SIZE]; getKeywordParam(keyword, "file=", fileName); if (fileName[0] == '\0') { if (!default_file) { ERROR("No injection file was specified!"); } newcomp->comp_param.field_param.filename = strdup(default_file); } else { newcomp->comp_param.field_param.filename = strdup(fileName); } if (inFiles.find(newcomp->comp_param.field_param.filename) == inFiles.end()) { ERROR("Invalid injection file: %s", fileName); } char line[KEYWORD_SIZE]; getKeywordParam(keyword, "line=", line); if (line[0]) { /* Turn this into a new message component. */ newcomp->comp_param.field_param.line = new SendingMessage(msg_scenario, line, true); } } else if(!strncmp(keyword, "file", strlen("file"))) { newcomp->type = E_Message_File; /* Parse out the interesting things like file and number. */ char fileName[KEYWORD_SIZE]; getKeywordParam(keyword, "name=", fileName); if (fileName[0] == '\0') { ERROR("No name specified for 'file' keyword!"); } /* Turn this into a new message component. */ newcomp->comp_param.filename = new SendingMessage(msg_scenario, fileName, true); } else if(*keyword == '$') { newcomp->type = E_Message_Variable; if (!msg_scenario) { ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(keyword + 1, "Variable keyword"); } else if(!strncmp(keyword, "fill", strlen("fill"))) { newcomp->type = E_Message_Fill; char filltext[KEYWORD_SIZE]; char varName[KEYWORD_SIZE]; getKeywordParam(keyword, "text=", filltext); if (filltext[0] == '\0') { strcpy(filltext, "X"); } getKeywordParam(keyword, "variable=", varName); newcomp->literal = strdup(filltext); newcomp->literalLen = strlen(newcomp->literal); if (!msg_scenario) { ERROR("SendingMessage with variable usage outside of scenario!"); } newcomp->varId = msg_scenario->get_var(varName, "Fill Variable"); } else if(!strncmp(keyword, "last_", strlen("last_"))) { newcomp->type = E_Message_Last_Header; newcomp->literal = strdup(keyword + strlen("last_")); newcomp->literalLen = strlen(newcomp->literal); } else if(!strncmp(keyword, "authentication", strlen("authentication"))) { parseAuthenticationKeyword(msg_scenario, newcomp, keyword); } #ifndef PCAPPLAY else if(!strcmp(keyword, "auto_media_port")) { ERROR("The %s keyword requires PCAPPLAY", keyword); } #endif else { // scan for the generic parameters - must be last test int i = 0; while (generic[i]) { char *msg1 = *generic[i]; char *msg2 = *(generic[i] + 1); if(!strcmp(keyword, msg1)) { newcomp->type = E_Message_Literal; newcomp->literal = strdup(msg2); newcomp->literalLen = strlen(newcomp->literal); break; } ++i; } if (!generic[i]) { ERROR("Unsupported keyword '%s' in xml scenario file", keyword); } } messageComponents.push_back(newcomp); } } if (literal[0]) { *dest++ = '\0'; literalLen = dest - literal; literal = (char *)realloc(literal, literalLen); if (!literal) { ERROR("Out of memory!"); } MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent)); if (!newcomp) { ERROR("Out of memory!"); } newcomp->type = E_Message_Literal; newcomp->literal = literal; newcomp->literalLen = literalLen-1; messageComponents.push_back(newcomp); } else { free(literal); } if (skip_sanity) { cancel = response = ack = false; method = NULL; free(osrc); return; } if (numComponents() < 1) { ERROR("Can not create a message that is empty!"); } if (getComponent(0)->type != E_Message_Literal) { ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!\n%s\n", osrc); } char *p = method = strdup(getComponent(0)->literal); char *q; while (isspace(*p)) { p++; } if (!(q = strchr(method, ' '))) { ERROR("You can not use a keyword for the METHOD or to generate \"SIP/2.0\" to ensure proper [cseq] operation!\n%s\n", osrc); } *q++ = '\0'; while (isspace(*q)) { q++; } if (!strcmp(method, "SIP/2.0")) { char *endptr; code = strtol(q, &endptr, 10); if (*endptr && !isspace(*endptr)) { ERROR("Invalid reply code: %s", q); } if (code < 100 || code >= 700) { ERROR("Response codes must be in the range of 100-700"); } response = true; ack = false; cancel = false; free(method); method = NULL; } else { if (p != method) { memmove(method, p, strlen(p) + 1); } method = (char *)realloc(method, strlen(method) + 1); if (!method) { ERROR("Out of memory"); } ack = (!strcmp(method, "ACK")); cancel = (!strcmp(method, "CANCEL")); response = false; }; free(osrc); } SendingMessage::~SendingMessage() { for (int i = 0; i < numComponents(); i++) { freeMessageComponent(messageComponents[i]); } free(method); } bool SendingMessage::isAck() { return ack; } bool SendingMessage::isCancel() { return cancel; } bool SendingMessage::isResponse() { return response; } char *SendingMessage::getMethod() { return method; } int SendingMessage::getCode() { return code; } void SendingMessage::getQuotedParam(char * dest, char * src, int * len) { *len=0; /* Allows any hex coded string like '0x5B07F6' */ while (char c = *src++) { switch(c) { case '"': (*len)++; *dest = '\0'; return; case '\\': c = *src++; (*len)++; if (c == 0) { *dest = '\0'; return; } /* Fall-Through. */ default: *dest++ = c; (*len)++; } } *dest = '\0'; } void SendingMessage::getHexStringParam(char * dest, char * src, int * len) { *len=0; /* Allows any hex coded string like '0x5B07F6' */ while (isxdigit(*src)) { int val = get_decimal_from_hex(*src); src++; if (isxdigit(*src)) { val = (val << 4) + get_decimal_from_hex(*src); src++; } *dest++ = val & 0xff; (*len)++; } } void SendingMessage::getKeywordParam(char * src, const char * param, char * output) { char *key, *tmp; int len; len = 0; key = NULL; if ((tmp = strstr(src, param))) { tmp += strlen(param); key = tmp; if ((*key == '0') && (*(key+1) == 'x')) { key += 2; getHexStringParam(output, key, &len); } else if (*key == '\"') { key++; getQuotedParam(output, key, &len); } else { while (*key) { if (((key - src) > KEYWORD_SIZE) || (!(key - src))) { ERROR("Syntax error parsing '%s' parameter", param); } else if (*key == ']' || *key < 33 || *key > 126) { break; } key++; } strncpy(output, tmp, key-tmp); output[key-tmp] = '\0'; } } else { output[0] = '\0'; } } void SendingMessage::parseAuthenticationKeyword(scenario *msg_scenario, struct MessageComponent *dst, char *keyword) { char my_auth_user[KEYWORD_SIZE + 1]; char my_auth_pass[KEYWORD_SIZE + 1]; char my_aka[KEYWORD_SIZE + 1]; dst->type = E_Message_Authentication; memset(my_auth_user,0,KEYWORD_SIZE); memset(my_auth_pass,0,KEYWORD_SIZE); /* Look for optional username and password parameters */ getKeywordParam(keyword, "username=", my_auth_user); getKeywordParam(keyword, "password=", my_auth_pass); if(*my_auth_user == '\0') { strncpy(my_auth_user, auth_username ? auth_username : service, sizeof(my_auth_user) - 1); } if(*my_auth_pass == '\0') { strncpy(my_auth_pass, auth_password, sizeof(my_auth_pass) - 1); } dst->comp_param.auth_param.auth_user = new SendingMessage(msg_scenario, my_auth_user, true /* skip sanity */); dst->comp_param.auth_param.auth_pass = new SendingMessage(msg_scenario, my_auth_pass, true); /* add aka_OP, aka_AMF, aka_K */ getKeywordParam(keyword, "aka_K=", my_aka); if (my_aka[0]==0) { memcpy(my_aka,my_auth_pass,16); my_aka[16]=0; } dst->comp_param.auth_param.aka_K = new SendingMessage(msg_scenario, my_aka, true); getKeywordParam(keyword, "aka_OP=", my_aka); dst->comp_param.auth_param.aka_OP = new SendingMessage(msg_scenario, my_aka, true); getKeywordParam(keyword, "aka_AMF=", my_aka); dst->comp_param.auth_param.aka_AMF = new SendingMessage(msg_scenario, my_aka, true); } void SendingMessage::freeMessageComponent(struct MessageComponent *comp) { free(comp->literal); if (comp->type == E_Message_Authentication) { if (comp->comp_param.auth_param.auth_user) { delete comp->comp_param.auth_param.auth_user; } if (comp->comp_param.auth_param.auth_pass) { delete comp->comp_param.auth_param.auth_pass; } if (comp->comp_param.auth_param.aka_K) { delete comp->comp_param.auth_param.aka_K; } if (comp->comp_param.auth_param.aka_AMF) { delete comp->comp_param.auth_param.aka_AMF; } if (comp->comp_param.auth_param.aka_OP) { delete comp->comp_param.auth_param.aka_OP; } } else if (comp->type == E_Message_Injection) { free(comp->comp_param.field_param.filename); } free(comp); } int SendingMessage::numComponents() { return messageComponents.size(); } struct MessageComponent *SendingMessage::getComponent(int i) { return messageComponents[i]; } /* This is very simplistic and does not yet allow any arguments, but it is a start. */ int registerKeyword(char *keyword, customKeyword fxn) { if (keyword_map.find(keyword) != keyword_map.end()) { ERROR("Can not register keyword '%s', already registered!", keyword); } keyword_map[keyword] = fxn; return 0; } sipp-3.6.1/src/deadcall.cpp0000664000175000017500000000567013730472040015110 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation */ #include #include #include #include #include #include #include "sipp.hpp" #include "deadcall.hpp" #include "assert.h" /* Defined in call.cpp. */ extern timewheel paused_calls; deadcall::deadcall(const char *id, const char *reason) : listener(id, true) { this->expiration = clock_tick + deadcall_wait; this->reason = strdup(reason); setPaused(); } deadcall::~deadcall() { free(reason); } bool deadcall::process_incoming(const char* msg, const struct sockaddr_storage* /*src*/) { char buffer[MAX_HEADER_LEN]; CStat::globalStat(CStat::E_DEAD_CALL_MSGS); setRunning(); snprintf(buffer, MAX_HEADER_LEN, "Dead call %s (%s)", id, reason); WARNING("%s, received '%s'", buffer, msg); TRACE_MSG("-----------------------------------------------\n" "Dead call %s received a %s message:\n\n%s\n", id, TRANSPORT_TO_STRING(transport), msg); expiration = clock_tick + deadcall_wait; return run(); } bool deadcall::process_twinSippCom(char * msg) { CStat::globalStat(CStat::E_DEAD_CALL_MSGS); TRACE_MSG("Received twin message for dead (%s) call %s:%s\n", reason, id, msg); return true; } bool deadcall::run() { if (clock_tick > expiration) { delete this; return false; } else { setPaused(); return true; } } unsigned int deadcall::wake() { return expiration; } /* Dump call info to error log. */ void deadcall::dump() { WARNING("%s: Dead Call (%s) expiring at %lu", id, reason, expiration); } sipp-3.6.1/src/strings.cpp0000664000175000017500000001445213730472040015046 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include "strings.hpp" #include #include #include #include void get_host_and_port(const char * addr, char * host, int * port) { /* Separate the port number (if any) from the host name. * Thing is, the separator is a colon (':'). The colon may also exist * in the host portion if the host is specified as an IPv6 address (see * RFC 2732). If that's the case, then we need to skip past the IPv6 * address, which should be contained within square brackets ('[',']'). */ const char *has_brackets; int len; int port_result = 0; has_brackets = strchr(addr, '['); if (has_brackets != NULL) { has_brackets = strchr(has_brackets, ']'); } if (has_brackets == NULL) { /* addr is not a []-enclosed IPv6 address, but might still be IPv6 (without * a port), or IPv4 or a hostname (with or without a port) */ char *first_colon_location; char *second_colon_location; len = strlen(addr) + 1; memmove(host, addr, len); first_colon_location = strchr(host, ':'); if (first_colon_location == NULL) { /* No colon - just set the port to 0 */ port_result = 0; } else { second_colon_location = strchr(first_colon_location + 1, ':'); if (second_colon_location != NULL) { /* Found a second colon in addr - so this is an IPv6 address * without a port. Set the port to 0 */ port_result = 0; } else { /* IPv4 address or hostname with a colon in it - convert the colon to * a NUL terminator, and set the value after it as the port */ *first_colon_location = '\0'; port_result = atol(first_colon_location + 1); } } } else { /* If '['..']' found, */ const char *initial_bracket; /* extract the remote_host */ char *second_bracket; char *colon_before_port; initial_bracket = strchr( addr, '[' ); initial_bracket++; /* Step forward one character */ len = strlen(initial_bracket) + 1; memmove(host, initial_bracket, len); second_bracket = strchr( host, ']' ); *second_bracket = '\0'; /* Check for a port specified after the ] */ colon_before_port = strchr(second_bracket + 1, ':'); if (colon_before_port != NULL) { port_result = atol(colon_before_port + 1); } else { port_result = 0; } } // Set the port argument if it wasn't NULL if (port != NULL) { *port = port_result; } } int get_decimal_from_hex(char hex) { if (isdigit(hex)) return hex - '0'; else return tolower(hex) - 'a' + 10; } void trim(char *s) { char *p = s; while(isspace(*p)) { p++; } int l = strlen(p); for (int i = l - 1; i >= 0 && isspace(p[i]); i--) { p[i] = '\0'; } memmove(s, p, l + 1); } #ifdef GTEST #include "gtest/gtest.h" TEST(GetHostAndPort, IPv6) { int port_result = -1; char host_result[255]; get_host_and_port("fe80::92a4:deff:fe74:7af5", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("fe80::92a4:deff:fe74:7af5", host_result); } TEST(GetHostAndPort, IPv6Brackets) { int port_result = -1; char host_result[255]; get_host_and_port("[fe80::92a4:deff:fe74:7af5]", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("fe80::92a4:deff:fe74:7af5", host_result); } TEST(GetHostAndPort, IPv6BracketsAndPort) { int port_result = -1; char host_result[255]; get_host_and_port("[fe80::92a4:deff:fe74:7af5]:999", host_result, &port_result); EXPECT_EQ(999, port_result); EXPECT_STREQ("fe80::92a4:deff:fe74:7af5", host_result); } TEST(GetHostAndPort, IPv4) { int port_result = -1; char host_result[255]; get_host_and_port("127.0.0.1", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("127.0.0.1", host_result); } TEST(GetHostAndPort, IPv4AndPort) { int port_result = -1; char host_result[255]; get_host_and_port("127.0.0.1:999", host_result, &port_result); EXPECT_EQ(999, port_result); EXPECT_STREQ("127.0.0.1", host_result); } TEST(GetHostAndPort, IgnorePort) { char host_result[255]; get_host_and_port("127.0.0.1", host_result, NULL); EXPECT_STREQ("127.0.0.1", host_result); } TEST(GetHostAndPort, DNS) { int port_result = -1; char host_result[255]; get_host_and_port("sipp.sf.net", host_result, &port_result); EXPECT_EQ(0, port_result); EXPECT_STREQ("sipp.sf.net", host_result); } TEST(GetHostAndPort, DNSAndPort) { int port_result = -1; char host_result[255]; get_host_and_port("sipp.sf.net:999", host_result, &port_result); EXPECT_EQ(999, port_result); EXPECT_STREQ("sipp.sf.net", host_result); } #endif //GTEST sipp-3.6.1/src/call.cpp0000664000175000017500000045103313730472040014270 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation * Roland Meub * Andy Aicken * Martin H. VanLeeuwen */ #include #include #include #include #include #include #include #include #include #include #include #ifdef PCAPPLAY #include "send_packets.h" #endif #include "sipp.hpp" #include "auth.hpp" #include "deadcall.hpp" #include "config.h" #include "version.h" template void split(const std::string &s, char delim, Out result) { std::stringstream ss; ss.str(s); std::string item; while (std::getline(ss, item, delim)) { *(result++) = item; } } std::vector split(const std::string &s, char delim) { std::vector elems; split(s, delim, std::back_inserter(elems)); return elems; } std::string join(const std::vector &s, const char* delim) { std::ostringstream imploded; std::copy(s.begin(), s.end(), std::ostream_iterator(imploded, delim)); std::string ret = imploded.str(); if (ret.length()) { ret.resize(ret.length() - strlen(delim)); } return ret; } std::string trim(const std::string &s) { size_t first = s.find_first_not_of(' '); if (first == string::npos) { return s; } size_t last = s.find_last_not_of(' '); return s.substr(first, (last - first + 1)); } #define callDebug(...) do { if (useCallDebugf) { _callDebug( __VA_ARGS__ ); } } while (0) extern map map_perip_fd; #ifdef PCAPPLAY /* send_packets pthread wrapper */ void *send_wrapper(void *); #endif int call::dynamicId = 0; int call::maxDynamicId = 10000+2000*4; // FIXME both param to be in command line !!!! int call::startDynamicId = 10000; // FIXME both param to be in command line !!!! int call::stepDynamicId = 4; // FIXME both param to be in command line !!!! /************** Call map and management routines **************/ static unsigned int next_number = 1; static unsigned int get_tdm_map_number() { unsigned int nb = 0; unsigned int i=0; unsigned int interval=0; unsigned int random=0; bool found = false; /* Find a number in the tdm_map which is not in use */ interval = (tdm_map_a+1) * (tdm_map_b+1) * (tdm_map_c+1); random = rand() % interval; while ((iss_ipv6, false /* Not Auto. */, false); } call::call(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitialization) : listener(p_id, true) { init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic, isInitialization); } call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest) { static char call_id[MAX_HEADER_LEN]; const char * src = call_id_string; int count = 0; if(!next_number) { next_number ++; } while (*src && count < MAX_HEADER_LEN-1) { if (*src == '%') { ++src; switch(*src++) { case 'u': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%u", next_number); break; case 'p': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%u", pid); break; case 's': count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%s", local_ip); break; default: // treat all unknown sequences as %% call_id[count++] = '%'; break; } } else { call_id[count++] = *src++; } } call_id[count] = 0; return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */, false); } void call::init(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall) { this->call_scenario = call_scenario; zombie = false; debugBuffer = NULL; debugLength = 0; msg_index = 0; last_send_index = 0; last_send_msg = NULL; last_send_len = 0; last_recv_hash = 0; last_recv_index = -1; last_recv_msg = NULL; recv_retrans_hash = 0; recv_retrans_recv_index = -1; recv_retrans_send_index = -1; dialog_route_set = NULL; next_req_url = NULL; cseq = 0; next_retrans = 0; nb_retrans = 0; nb_last_delay = 0; paused_until = 0; call_port = 0; comp_state = NULL; start_time = clock_tick; call_established=false ; ack_is_pending=false ; last_recv_msg = NULL; cseq = base_cseq; nb_last_delay = 0; use_ipv6 = ipv6; queued_msg = NULL; dialog_authentication = NULL; dialog_challenge_type = 0; next_nonce_count = 1; #ifdef RTP_STREAM /* check and warn on rtpstream_new_call result? -> error alloc'ing mem */ rtpstream_new_call(&rtpstream_callinfo); #endif #ifdef PCAPPLAY hasMediaInformation = 0; play_args_a.last_seq_no = 1200; play_args_v.last_seq_no = 2400; #endif call_remote_socket = NULL; if (socket) { associate_socket(socket); socket->ss_count++; } else { call_socket = NULL; } if (dest) { memcpy(&call_peer, dest, sizeof(call_peer)); } else { memset(&call_peer, 0, sizeof(call_peer)); } // initialising the CallVariable with the Scenario variable int i; VariableTable *userVars = NULL; bool putUserVars = false; if (userId) { int_vt_map::iterator it = userVarMap.find(userId); if (it != userVarMap.end()) { userVars = it->second; } } else { userVars = new VariableTable(userVariables); /* Creating this table creates a reference to it, but if it is really used, * then the refcount will be increased. */ putUserVars = true; } if (call_scenario->allocVars->size > 0) { M_callVariableTable = new VariableTable(userVars, call_scenario->allocVars->size); } else if (userVars && userVars->size > 0) { M_callVariableTable = userVars->getTable(); } else if (globalVariables->size > 0) { M_callVariableTable = globalVariables->getTable(); } else { M_callVariableTable = NULL; } if (putUserVars) { userVars->putTable(); } if (call_scenario->transactions.size() > 0) { transactions = (struct txnInstanceInfo *)malloc(sizeof(txnInstanceInfo) * call_scenario->transactions.size()); memset(transactions, 0, sizeof(struct txnInstanceInfo) * call_scenario->transactions.size()); } else { transactions = NULL; } // If not updated by a message we use the start time // information to compute rtd information start_time_rtd = (unsigned long long *)malloc(sizeof(unsigned long long) * call_scenario->stats->nRtds()); if (!start_time_rtd) { ERROR("Could not allocate RTD times!"); } rtd_done = (bool *)malloc(sizeof(bool) * call_scenario->stats->nRtds()); if (!start_time_rtd) { ERROR("Could not allocate RTD done!"); } for (i = 0; i < call_scenario->stats->nRtds(); i++) { start_time_rtd[i] = getmicroseconds(); rtd_done[i] = false; } // by default, last action result is NO_ERROR last_action_result = call::E_AR_NO_ERROR; this->userId = userId; /* For automatic answer calls to an out of call request, we must not */ /* increment the input files line numbers to not disturb */ /* the input files read mechanism (otherwise some lines risk */ /* to be systematically skipped */ if (!isAutomatic) { m_lineNumber = new file_line_map(); for (file_map::iterator file_it = inFiles.begin(); file_it != inFiles.end(); file_it++) { (*m_lineNumber)[file_it->first] = file_it->second->nextLine(userId); } } else { m_lineNumber = NULL; } this->initCall = isInitCall; #ifdef PCAPPLAY memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_i.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_a.from), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_i.from), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.from), 0, sizeof(struct sockaddr_storage)); hasMediaInformation = 0; media_thread = 0; #endif peer_tag = NULL; recv_timeout = 0; send_timeout = 0; timewait = false; if (!isAutomatic) { /* Not advancing the number is safe, because for automatic calls we do not * assign the identifier, the only other place it is used is for the auto * media port. */ number = next_number++; if (use_tdmmap) { tdm_map_number = get_tdm_map_number(); if (tdm_map_number == 0) { /* Can't create the new call */ WARNING("Can't create new outgoing call: all tdm_map circuits busy"); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_OUTBOUND_CONGESTION); this->zombie = true; return; } /* Mark the entry in the list as busy */ tdm_map[tdm_map_number - 1] = true; } else { tdm_map_number = 0; } } callDebug("Starting call %s\n", id); setRunning(); } int call::_callDebug(const char *fmt, ...) { va_list ap; if (!useCallDebugf) { return 0; } /* First we figure out how much to allocate. */ va_start(ap, fmt); int ret = vsnprintf(NULL, 0, fmt, ap); va_end(ap); debugBuffer = (char *)realloc(debugBuffer, debugLength + ret + TIME_LENGTH + 2); if (!debugBuffer) { ERROR("Could not allocate buffer (%d bytes) for callDebug file!", debugLength + ret + TIME_LENGTH + 2); } struct timeval now; gettimeofday(&now, NULL); debugLength += snprintf(debugBuffer + debugLength, TIME_LENGTH + 2, "%s ", CStat::formatTime(&now)); va_start(ap, fmt); debugLength += vsnprintf(debugBuffer + debugLength, ret + 1, fmt, ap); va_end(ap); return ret; } call::~call() { computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time); if(comp_state) { comp_free(&comp_state); } if (call_remote_socket && (call_remote_socket != main_remote_socket)) { call_remote_socket->close(); } /* Deletion of the call variable */ if(M_callVariableTable) { M_callVariableTable->putTable(); } if (m_lineNumber) { delete m_lineNumber; } if (userId) { CallGenerationTask::free_user(userId); } if (transactions) { for (unsigned int i = 0; i < call_scenario->transactions.size(); i++) { free(transactions[i].txnID); } free(transactions); } if (last_recv_msg) { free(last_recv_msg); } if (last_send_msg) { free(last_send_msg); } if (peer_tag) { free(peer_tag); } if (dialog_route_set) { free(dialog_route_set); } if (next_req_url) { free(next_req_url); } #ifdef RTP_STREAM rtpstream_end_call(&rtpstream_callinfo); #endif if (dialog_authentication) { free(dialog_authentication); } if (use_tdmmap) { tdm_map[tdm_map_number] = false; } # ifdef PCAPPLAY if (media_thread != 0) { pthread_cancel(media_thread); pthread_join(media_thread, NULL); } #endif free(start_time_rtd); free(rtd_done); free(debugBuffer); } void call::computeStat (CStat::E_Action P_action) { if (initCall) { return; } call_scenario->stats->computeStat(P_action); } void call::computeStat (CStat::E_Action P_action, unsigned long P_value) { if (initCall) { return; } call_scenario->stats->computeStat(P_action, P_value); } void call::computeStat (CStat::E_Action P_action, unsigned long P_value, int which) { if (initCall) { return; } call_scenario->stats->computeStat(P_action, P_value, which); } /* Dump call info to error log. */ void call::dump() { char s[MAX_HEADER_LEN]; char tmpbuf[MAX_HEADER_LEN]; sprintf(s, "%s: State %d", id, msg_index); if (next_retrans) { snprintf(tmpbuf, 64, "%s (next retrans %u)", s, next_retrans); strcat(s, tmpbuf); } if (paused_until) { snprintf(tmpbuf, 64, "%s (paused until %u)", s, paused_until); strcat(s, tmpbuf); } if (recv_timeout) { snprintf(tmpbuf, 64, "%s (recv timeout %u)", s, recv_timeout); strcat(s, tmpbuf); } if (send_timeout) { snprintf(tmpbuf, 64, "%s (send timeout %u)", s, send_timeout); strcat(s, tmpbuf); } WARNING("%s", s); } bool call::connect_socket_if_needed() { bool existing; if(call_socket) return true; if(!multisocket) return true; if(transport == T_UDP) { struct sockaddr_storage saddr; if(sendMode != MODE_CLIENT) return true; char peripaddr[256]; if (!peripsocket) { if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a UDP socket (1)"); } } else { char *tmp = peripaddr; getFieldFromInputFile(ip_file, peripfield, NULL, tmp); map::iterator i; i = map_perip_fd.find(peripaddr); if (i == map_perip_fd.end()) { // Socket does not exist if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a UDP socket (2)"); } else { /* Ensure that it stays persistent, because it is recorded in the map. */ call_socket->ss_count++; map_perip_fd[peripaddr] = call_socket; } } else { // Socket exists already associate_socket(i->second); existing = true; i->second->ss_count++; } } if (existing) { return true; } memcpy(&saddr, &local_addr_storage, sizeof(struct sockaddr_storage)); if (use_ipv6) { saddr.ss_family = AF_INET6; } else { saddr.ss_family = AF_INET; } if (peripsocket) { gai_getsockaddr(&saddr, peripaddr, local_port, AI_PASSIVE, AF_UNSPEC); } if (sipp_bind_socket(call_socket, &saddr, &call_port)) { ERROR_NO("Unable to bind UDP socket"); } } else { /* TCP, SCTP or TLS. */ struct sockaddr_storage *L_dest = &remote_sockaddr; if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { ERROR_NO("Unable to get a TCP/SCTP/TLS socket"); } if (existing) { return true; } sipp_customize_socket(call_socket); if (use_remote_sending_addr) { L_dest = &remote_sending_sockaddr; } if (call_socket->connect(L_dest)) { if (reconnect_allowed()) { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ WARNING("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { WARNING("Unable to connect a TCP/SCTP/TLS socket"); } /* This connection failed. We must be in multisocket mode, because * otherwise we would already have a call_socket. This call can not * succeed, but does not affect any of our other calls. We do decrement * the reconnection counter however. */ if (reset_number != -1) { reset_number--; } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TCP_CONNECT); delete this; return false; } else { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { ERROR_NO("Unable to connect a TCP/SCTP/TLS socket"); } } } call_port = call_socket->ss_port; } return true; } bool call::lost(int index) { static int inited = 0; double percent = global_lost; if(!lose_packets) return false; if (call_scenario->messages[index]->lost >= 0) { percent = call_scenario->messages[index]->lost; } if (percent == 0) { return false; } if(!inited) { srand((unsigned int) time(NULL)); inited = 1; } return (((double)rand() / (double)RAND_MAX) < (percent / 100.0)); } int call::send_raw(const char * msg, int index, int len) { SIPpSocket *sock; int rc; callDebug("Sending %s message for call %s (index %d, hash %lu):\n%s\n\n", TRANSPORT_TO_STRING(transport), id, index, hash(msg), msg); if((index!=-1) && (lost(index))) { TRACE_MSG("%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport)); callDebug("%s message voluntary lost (while sending) (index %d, hash %lu).\n", TRANSPORT_TO_STRING(transport), index, hash(msg)); if(comp_state) { comp_free(&comp_state); } call_scenario->messages[index] -> nb_lost++; return 0; } sock = call_socket; if ((use_remote_sending_addr) && (sendMode == MODE_SERVER)) { if (!call_remote_socket) { if (multisocket || !main_remote_socket) { struct sockaddr_storage *L_dest = &remote_sending_sockaddr; if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) { ERROR_NO("Unable to get a socket for rsa option"); } sipp_customize_socket(call_remote_socket); if(transport != T_UDP) { if (call_remote_socket->connect(L_dest)) { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport)); } else { ERROR_NO("Unable to connect a socket for rsa option"); } } } if (!multisocket) { main_remote_socket = call_remote_socket; } } if (!multisocket) { call_remote_socket = main_remote_socket; main_remote_socket->ss_count++; } } sock=call_remote_socket ; } // If the length hasn't been explicitly specified, treat the message as a string if (len==0) { len = strlen(msg); } assert(sock); rc = sock->write(msg, len, WS_BUFFER, &call_peer); if(rc < 0 && errno == EWOULDBLOCK) { return rc; } if(rc < 0) { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CANNOT_SEND_MSG); delete this; } return rc; /* OK */ } /* This method is used to send messages that are not */ /* part of the XML scenario */ void call::sendBuffer(char * msg, int len) { /* call send_raw but with a special scenario index */ if (send_raw(msg, -1, len) < 0) { if (sendbuffer_warn) { ERROR_NO("Error sending raw message"); } else { WARNING_NO("Error sending raw message"); } } } char * call::get_header_field_code(const char *msg, const char * name) { static char code[MAX_HEADER_LEN]; const char * last_header; int i; last_header = NULL; i = 0; /* If we find the field in msg */ last_header = get_header_content(msg, name); if(last_header) { /* Extract the integer value of the field */ while(isspace(*last_header)) last_header++; sscanf(last_header,"%d", &i); sprintf(code, "%s %d", name, i); } return code; } char * call::get_last_header(const char * name) { int len; if((!last_recv_msg) || (!strlen(last_recv_msg))) { return NULL; } len = strlen(name); /* Ideally this check should be moved to the XML parser so that it is not * along a critical path. We could also handle lowercasing there. */ if (len > MAX_HEADER_LEN) { ERROR("call::get_last_header: Header to parse bigger than %d (%zu)", MAX_HEADER_LEN, strlen(name)); } if (name[len - 1] == ':') { return get_header(last_recv_msg, name, false); } else { char with_colon[MAX_HEADER_LEN]; sprintf(with_colon, "%s:", name); return get_header(last_recv_msg, with_colon, false); } } /* Return the last request URI from the To header. On any error returns the * empty string. The caller must free the result. */ char * call::get_last_request_uri() { char * tmp; char * tmp2; char * last_request_uri; int tmp_len; char * last_To = get_last_header("To:"); if (!last_To) { return strdup(""); } tmp = strchr(last_To, '<'); if (!tmp) { return strdup(""); } tmp++; tmp2 = strchr(last_To, '>'); if (!tmp2) { return strdup(""); } tmp_len = strlen(tmp) - strlen(tmp2); if (tmp_len < 0) { return strdup(""); } if (!(last_request_uri = (char *)malloc(tmp_len + 1))) { ERROR("Cannot allocate!"); } last_request_uri[0] = '\0'; if (tmp && (tmp_len > 0)) { strncpy(last_request_uri, tmp, tmp_len); } last_request_uri[tmp_len] = '\0'; return last_request_uri; } char * call::send_scene(int index, int *send_status, int *len) { #define MAX_MSG_NAME_SIZE 30 static char msg_name[MAX_MSG_NAME_SIZE]; char *L_ptr1 ; char *L_ptr2 ; int uselen = 0; assert(send_status); /* Socket port must be known before string substitution */ if (!connect_socket_if_needed()) { *send_status = -2; return NULL; } assert(call_socket); assert(call_scenario->messages[index]->send_scheme); if (!len) { len = &uselen; } char * dest; dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index, len); if (!dest) { *send_status = -2; return NULL; } L_ptr1=msg_name ; L_ptr2=dest ; while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t')) { *L_ptr1 = *L_ptr2; L_ptr1 ++; L_ptr2 ++; } *L_ptr1 = '\0' ; if (strcmp(msg_name,"ACK") == 0) { call_established = true ; ack_is_pending = false ; } *send_status = send_raw(dest, index, *len); return dest; } void call::do_bookkeeping(message *curmsg) { /* If this message increments a counter, do it now. */ if (curmsg -> counter) { computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, curmsg->counter - 1); } /* If this message can be used to compute RTD, do it now */ if (curmsg->start_rtd) { start_time_rtd[curmsg->start_rtd - 1] = getmicroseconds(); } if (curmsg->stop_rtd) { int rtd = curmsg->stop_rtd; if (!rtd_done[rtd - 1]) { unsigned long long start = start_time_rtd[rtd - 1]; unsigned long long end = getmicroseconds(); if (dumpInRtt) { call_scenario->stats->computeRtt(start, end, rtd); } computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION, (end - start) / 1000, rtd - 1); if (!curmsg->repeat_rtd) { rtd_done[rtd - 1] = true; } } } } void call::tcpClose() { terminate(CStat::E_FAILED_TCP_CLOSED); } void call::terminate(CStat::E_Action reason) { char reason_str[100]; stopListening(); // Call end -> was it successful? if(call::last_action_result != call::E_AR_NO_ERROR) { switch(call::last_action_result) { case call::E_AR_REGEXP_DOESNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "regexp match failure at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_REGEXP_SHOULDNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_REGEXP_SHOULDNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "regexp matched, but shouldn't at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_HDR_NOT_FOUND: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND); if (deadcall_wait && !initCall) { sprintf(reason_str, "regexp header not found at index %d", msg_index); new deadcall(id, reason_str); } break; case E_AR_CONNECT_FAILED: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TCP_CONNECT); if (deadcall_wait && !initCall) { sprintf(reason_str, "connection failed %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_NO_ERROR: case call::E_AR_STOP_CALL: /* Do nothing. */ break; case call::E_AR_TEST_DOESNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TEST_DOESNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test failure at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_TEST_SHOULDNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TEST_SHOULDNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test succeeded, but shouldn't at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_STRCMP_DOESNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_STRCMP_DOESNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test failure at index %d", msg_index); new deadcall(id, reason_str); } break; case call::E_AR_STRCMP_SHOULDNT_MATCH: computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_STRCMP_SHOULDNT_MATCH); if (deadcall_wait && !initCall) { sprintf(reason_str, "test succeeded, but shouldn't at index %d", msg_index); new deadcall(id, reason_str); } break; } } else { if (reason == CStat::E_CALL_SUCCESSFULLY_ENDED || timewait) { computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED); if (deadcall_wait && !initCall) { new deadcall(id, "successful"); } } else { computeStat(CStat::E_CALL_FAILED); if (reason != CStat::E_NO_ACTION) { computeStat(reason); } if (deadcall_wait && !initCall) { sprintf(reason_str, "failed at index %d", msg_index); new deadcall(id, reason_str); } } } delete this; } bool call::next() { msgvec * msgs = &call_scenario->messages; if (initCall) { msgs = &call_scenario->initmessages; } int test; /* What is the next message index? */ /* Default without branching: use the next message */ int new_msg_index = msg_index + 1; /* If branch needed, overwrite this default */ if (msg_index >= 0 && ((*msgs)[msg_index]->next >= 0) && (((test = ((*msgs)[msg_index]->test)) == -1) || M_callVariableTable->getVar(test)->isSet())) { /* Branching possible, check the probability */ int chance = (*msgs)[msg_index]->chance; if ((chance <= 0) || (rand() > chance )) { /* Branch == overwrite with the 'next' attribute value */ new_msg_index = (*msgs)[msg_index]->next; } } msg_index = new_msg_index; recv_timeout = 0; if (msg_index >= (int)((*msgs).size())) { terminate(CStat::E_CALL_SUCCESSFULLY_ENDED); return false; } return true; } bool call::executeMessage(message *curmsg) { if (curmsg->pause_distribution || curmsg->pause_variable != -1) { unsigned int pause; if (curmsg->pause_distribution) { double actualpause = curmsg->pause_distribution->sample(); if (actualpause < 1) { // Protect against distribution samples that give // negative results (and so pause for ~50 hours when // cast to a unsigned int). pause = 0; } else { pause = (unsigned int)actualpause; }; } else { int varId = curmsg->pause_variable; pause = (int) M_callVariableTable->getVar(varId)->getDouble(); } if (pause > INT_MAX) { pause = INT_MAX; } paused_until = clock_tick + pause; /* This state is used as the last message of a scenario, just for handling * final retransmissions. If the connection closes, we do not mark it is * failed. */ this->timewait = curmsg->timewait; /* Increment the number of sessions in pause state */ curmsg->sessions++; do_bookkeeping(curmsg); executeAction(NULL, curmsg); callDebug("Pausing call until %d (is now %ld).\n", paused_until, clock_tick); setPaused(); return true; } else if(curmsg -> M_type == MSG_TYPE_SENDCMD) { int send_status; if(next_retrans) { return true; } send_status = sendCmdMessage(curmsg); if(send_status != 0) { /* Send error */ return false; /* call deleted */ } curmsg -> M_nbCmdSent++; next_retrans = 0; do_bookkeeping(curmsg); executeAction(NULL, curmsg); return(next()); } else if(curmsg -> M_type == MSG_TYPE_NOP) { callDebug("Executing NOP at index %d.\n", curmsg->index); do_bookkeeping(curmsg); executeAction(NULL, curmsg); return(next()); } else if(curmsg -> send_scheme) { char * msg_snd; int msgLen; int send_status; /* Do not send a new message until the previous one which had * retransmission enabled is acknowledged */ if(next_retrans) { setPaused(); return true; } /* Handle counters and RTDs for this message. */ do_bookkeeping(curmsg); /* decide whether to increment cseq or not * basically increment for anything except response, ACK or CANCEL * Note that cseq is only used by the [cseq] keyword, and * not by default */ int incr_cseq = 0; if (!curmsg->send_scheme->isAck() && !curmsg->send_scheme->isCancel() && !curmsg->send_scheme->isResponse()) { ++cseq; incr_cseq = 1; } msg_snd = send_scene(msg_index, &send_status, &msgLen); if (!msg_snd) { /* This will hit connect_if_needed, and if it fails, the entire call is deleted... */ ERROR("Call failed, cannot continue safely..."); } if(send_status < 0 && errno == EWOULDBLOCK) { if (incr_cseq) --cseq; /* Have we set the timeout yet? */ if (send_timeout) { /* If we have actually timed out. */ if (clock_tick > send_timeout) { WARNING("Call-Id: %s, send timeout on message %s:%d: aborting call", id, curmsg->desc, curmsg->index); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } } else if (curmsg->timeout) { /* Initialize the send timeout to the per message timeout. */ send_timeout = clock_tick + curmsg->timeout; } else if (defl_send_timeout) { /* Initialize the send timeout to the global timeout. */ send_timeout = clock_tick + defl_send_timeout; } return true; /* No step, nothing done, retry later */ } else if(send_status < 0) { /* Send error */ /* The call was already deleted by connect_socket_if_needed or send_raw, * so we should no longer access members. */ return false; } /* We have sent the message, so the timeout is no longer needed. */ send_timeout = 0; last_send_index = curmsg->index; last_send_len = msgLen; realloc_ptr = (char *) realloc(last_send_msg, msgLen+1); if (realloc_ptr) { last_send_msg = realloc_ptr; } else { free(last_send_msg); ERROR("Out of memory!"); return false; } memcpy(last_send_msg, msg_snd, msgLen); last_send_msg[msgLen] = '\0'; if (curmsg->start_txn) { transactions[curmsg->start_txn - 1].txnID = (char *)realloc(transactions[curmsg->start_txn - 1].txnID, MAX_HEADER_LEN); extract_transaction(transactions[curmsg->start_txn - 1].txnID, last_send_msg); } if (curmsg->ack_txn) { transactions[curmsg->ack_txn - 1].ackIndex = curmsg->index; } if(last_recv_index >= 0) { /* We are sending just after msg reception. There is a great * chance that we will be asked to retransmit this message */ recv_retrans_hash = last_recv_hash; recv_retrans_recv_index = last_recv_index; recv_retrans_send_index = curmsg->index; callDebug("Set Retransmission Hash: %lu (recv index %d, send index %d)\n", recv_retrans_hash, recv_retrans_recv_index, recv_retrans_send_index); /* Prevent from detecting the cause relation between send and recv * in the next valid send */ last_recv_hash = 0; } /* Update retransmission information */ if(curmsg -> retrans_delay) { if((transport == T_UDP) && (retrans_enabled)) { next_retrans = clock_tick + curmsg -> retrans_delay; nb_retrans = 0; nb_last_delay = curmsg->retrans_delay; } } else { next_retrans = 0; } executeAction(msg_snd, curmsg); /* Update scenario statistics */ curmsg -> nb_sent++; return next(); } else if (curmsg->M_type == MSG_TYPE_RECV || curmsg->M_type == MSG_TYPE_RECVCMD ) { if (queued_msg) { char *msg = queued_msg; queued_msg = NULL; bool ret = process_incoming(msg); free(msg); return ret; } else if (recv_timeout) { if(recv_timeout > getmilliseconds()) { setPaused(); return true; } recv_timeout = 0; curmsg->nb_timeout++; if (curmsg->on_timeout < 0) { // if you set a timeout but not a label, the call is aborted WARNING("Call-Id: %s, receive timeout on message %s:%d without label to jump to (ontimeout attribute): aborting call", id, curmsg->desc, curmsg->index); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } WARNING("Call-Id: %s, receive timeout on message %s:%d, jumping to label %d", id, curmsg->desc, curmsg->index, curmsg->on_timeout); /* FIXME: We should do something like set index here, but it probably * does not matter too much as only nops are allowed in the init stanza. */ msg_index = curmsg->on_timeout; recv_timeout = 0; if (msg_index < (int)call_scenario->messages.size()) return true; // special case - the label points to the end - finish the call computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } else if (curmsg->timeout || defl_recv_timeout) { if (curmsg->timeout) // If timeout is specified on message receive, use it recv_timeout = getmilliseconds() + curmsg->timeout; else // Else use the default timeout if specified recv_timeout = getmilliseconds() + defl_recv_timeout; return true; } else { /* We are going to wait forever. */ setPaused(); } } else { WARNING("Unknown message type at %s:%d: %d", curmsg->desc, curmsg->index, curmsg->M_type); } return true; } bool call::run() { bool bInviteTransaction = false; assert(running); if (zombie) { delete this; return false; } getmilliseconds(); message *curmsg; if (initCall) { if(msg_index >= (int)call_scenario->initmessages.size()) { ERROR("Scenario initialization overrun for call %s (%p) (index = %d)", id, _RCAST(void*, this), msg_index); } curmsg = call_scenario->initmessages[msg_index]; } else { if(msg_index >= (int)call_scenario->messages.size()) { ERROR("Scenario overrun for call %s (%p) (index = %d)", id, _RCAST(void*, this), msg_index); } curmsg = call_scenario->messages[msg_index]; } callDebug("Processing message %d of type %d for call %s at %lu.\n", msg_index, curmsg->M_type, id, clock_tick); if (curmsg->condexec != -1) { bool exec = M_callVariableTable->getVar(curmsg->condexec)->isSet(); if (curmsg->condexec_inverse) { exec = !exec; } if (!exec) { callDebug("Conditional variable %s %s set, so skipping message %d.\n", call_scenario->allocVars->getName(curmsg->condexec), curmsg->condexec_inverse ? "" : "not", msg_index); return next(); } } /* Manages retransmissions or delete if max retrans reached */ if(next_retrans && (next_retrans < clock_tick)) { nb_retrans++; if ( (0 == strncmp (last_send_msg, "INVITE", 6)) ) { bInviteTransaction = true; } int rtAllowed = min(bInviteTransaction ? max_invite_retrans : max_non_invite_retrans, max_udp_retrans); callDebug("Retransmisison required (%d retransmissions, max %d)\n", nb_retrans, rtAllowed); if(nb_retrans > rtAllowed) { call_scenario->messages[last_send_index] -> nb_timeout ++; if (call_scenario->messages[last_send_index]->on_timeout >= 0) { // action on timeout WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", id, msg_index, call_scenario->messages[last_send_index]->on_timeout); msg_index = call_scenario->messages[last_send_index]->on_timeout; next_retrans = 0; recv_timeout = 0; if (msg_index < (int)call_scenario->messages.size()) { return true; } // here if asked to go to the last label delete the call computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { // Abort the call by sending proper SIP message return(abortCall(true)); } else { // Just delete existing call delete this; return false; } } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_MAX_UDP_RETRANS); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { // Abort the call by sending proper SIP message WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id); return(abortCall(true)); } else { // Just delete existing call delete this; return false; } } else { nb_last_delay *= 2; if (global_t2 < nb_last_delay) { if (!bInviteTransaction) { nb_last_delay = global_t2; } } if(send_raw(last_send_msg, last_send_index, last_send_len) < -1) { return false; } call_scenario->messages[last_send_index] -> nb_sent_retrans++; computeStat(CStat::E_RETRANSMISSION); next_retrans = clock_tick + nb_last_delay; } } if(paused_until) { /* Process a pending pause instruction until delay expiration */ if(paused_until > clock_tick) { callDebug("Call is paused until %d (now %ld).\n", paused_until, clock_tick); setPaused(); callDebug("Running: %d (wake %d).\n", running, wake()); return true; } /* Our pause is over. */ callDebug("Pause complete, waking up.\n"); paused_until = 0; return next(); } return executeMessage(curmsg); } const char *default_message_names[] = { "3pcc_abort", "ack", "ack2", "bye", "cancel", "200", }; const char *default_message_strings[] = { /* 3pcc_abort */ "call-id: [call_id]\ninternal-cmd: abort_call\n\n", /* ack */ "ACK [last_Request_URI] SIP/2.0\n" "[last_Via]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number] ACK\n" "Contact: \n" "Max-Forwards: 70\n" "Subject: Performance Test\n" "Content-Length: 0\n\n", /* ack2, the only difference is Via, I don't quite know why. */ "ACK [last_Request_URI] SIP/2.0\n" "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number] ACK\n" "Contact: \n" "Max-Forwards: 70\n" "Subject: Performance Test\n" "Content-Length: 0\n\n", /* bye */ "BYE [last_Request_URI] SIP/2.0\n" "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number+1] BYE\n" "Max-Forwards: 70\n" "Contact: \n" "Content-Length: 0\n\n", /* cancel */ "CANCEL [last_Request_URI] SIP/2.0\n" "[last_Via]\n" "[last_From]\n" "[last_To]\n" "Call-ID: [call_id]\n" "CSeq: [last_cseq_number] CANCEL\n" "Max-Forwards: 70\n" "Contact: \n" "Content-Length: 0\n\n", /* 200 */ "SIP/2.0 200 OK\n" "[last_Via:]\n" "[last_From:]\n" "[last_To:]\n" "[last_Call-ID:]\n" "[last_CSeq:]\n" "Contact: \n" "Content-Length: 0\n\n" }; SendingMessage **default_messages; void init_default_messages() { int messages = sizeof(default_message_strings)/sizeof(default_message_strings[0]); default_messages = new SendingMessage* [messages]; for (int i = 0; i < messages; i++) { default_messages[i] = new SendingMessage(main_scenario, const_cast(default_message_strings[i])); } } void free_default_messages() { int messages = sizeof(default_message_strings)/sizeof(default_message_strings[0]); if (!default_messages) { return; } for (int i = 0; i < messages; i++) { delete default_messages[i]; } delete [] default_messages; } SendingMessage *get_default_message(const char *which) { int messages = sizeof(default_message_names)/sizeof(default_message_names[0]); for (int i = 0; i < messages; i++) { if (!strcmp(which, default_message_names[i])) { return default_messages[i]; } } ERROR("Internal Error: Unknown default message: %s!", which); } void set_default_message(const char *which, char *msg) { int messages = sizeof(default_message_names)/sizeof(default_message_names[0]); for (int i = 0; i < messages; i++) { if (!strcmp(which, default_message_names[i])) { default_message_strings[i] = msg; return; } } ERROR("Internal Error: Unknown default message: %s!", which); } bool call::process_unexpected(const char* msg) { char buffer[MAX_HEADER_LEN]; char *desc = buffer; int res = 0; message *curmsg = call_scenario->messages[msg_index]; curmsg->nb_unexp++; if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Aborting "); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Continuing "); } desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "call on unexpected message for Call-Id '%s': ", id); if (curmsg -> M_type == MSG_TYPE_RECV) { if (curmsg -> recv_request) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%s' ", curmsg -> recv_request); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%d' ", curmsg -> recv_response); } } else if (curmsg -> M_type == MSG_TYPE_SEND) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending "); } else if (curmsg -> M_type == MSG_TYPE_PAUSE) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while pausing "); } else if (curmsg -> M_type == MSG_TYPE_SENDCMD) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending command "); } else if (curmsg -> M_type == MSG_TYPE_RECVCMD) { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting command "); } else { desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while in message type %d ", curmsg->M_type); } snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "(index %d)", msg_index); WARNING("%s, received '%s'", buffer, msg); TRACE_MSG("-----------------------------------------------\n" "Unexpected %s message received:\n\n%s\n", TRANSPORT_TO_STRING(transport), msg); callDebug("Unexpected %s message received (index %d, hash %lu):\n\n%s\n", TRANSPORT_TO_STRING(transport), msg_index, hash(msg), msg); if (get_reply_code(msg)) { this->call_scenario->stats->error_codes.push_back(get_reply_code(msg)); } if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1)); if (res < 0) { WARNING("sendCmdBuffer returned %d", res); return false; } } // usage of last_ keywords => for call aborting realloc_ptr = (char *) realloc(last_recv_msg, strlen(msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, msg); computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_UNEXPECTED_MSG); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { return (abortCall(true)); } else { delete this; return false; } } else { // Do not abort call nor send anything in reply if default behavior is disabled return false; } } void call::abort() { WARNING("Aborted call with Call-ID '%s'", id); abortCall(false); } bool call::abortCall(bool writeLog) { int is_inv; char * src_recv = NULL ; callDebug("Aborting call %s (index %d).\n", id, msg_index); if (last_send_msg != NULL) { is_inv = !strncmp(last_send_msg, "INVITE", 6); } else { is_inv = false; } if ((creationMode != MODE_SERVER) && (msg_index > 0)) { if ((call_established == false) && (is_inv)) { src_recv = last_recv_msg ; // Answer unexpected errors (4XX, 5XX and beyond) with an ACK // Contributed by F. Tarek Rogers if((src_recv) && (get_reply_code(src_recv) >= 400)) { sendBuffer(createSendingMessage(get_default_message("ack"), -2)); } else if (src_recv) { /* Call is not established and the reply is not a 4XX, 5XX */ /* And we already received a message. */ if (ack_is_pending == true) { /* If an ACK is expected from the other side, send it * and send a BYE afterwards */ ack_is_pending = false; /* Send an ACK */ sendBuffer(createSendingMessage(get_default_message("ack"), -1)); /* Send the BYE */ sendBuffer(createSendingMessage(get_default_message("bye"), -1)); } else { /* Send a CANCEL */ sendBuffer(createSendingMessage(get_default_message("cancel"), -1)); } } else { /* Call is not established and the reply is not a 4XX, 5XX */ /* and we didn't received any message. This is the case when */ /* we are aborting after having send an INVITE and not received */ /* any answer. */ /* Do nothing ! */ } } else if (last_recv_msg) { /* The call may not be established, if we haven't yet received a message, * because the earlier check depends on the first message being an INVITE * (although it could be something like a message message, therefore we * check that we received a message. */ sendBuffer(createSendingMessage(get_default_message("bye"), -1)); } } if (writeLog && useCallDebugf) { TRACE_CALLDEBUG ("-------------------------------------------------------------------------------\n"); TRACE_CALLDEBUG ("Call debugging information for call %s:\n", id); TRACE_CALLDEBUG("%s", debugBuffer); } stopListening(); if (deadcall_wait && !initCall) { char reason[100]; sprintf(reason, "aborted at index %d", msg_index); new deadcall(id, reason); } delete this; return false; } bool call::rejectCall() { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CALL_REJECTED); delete this; return false; } int call::sendCmdMessage(message *curmsg) { char * dest; char delimitor[2]; delimitor[0]=27; delimitor[1]=0; /* 3pcc extended mode */ char * peer_dest; SIPpSocket **peer_socket; if(curmsg -> M_sendCmdData) { // WARNING("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData); dest = createSendingMessage(curmsg -> M_sendCmdData, -1); strcat(dest, delimitor); //WARNING("---SEND_TWIN_CMD---%s---", dest); int rc; /* 3pcc extended mode */ peer_dest = curmsg->peer_dest; if(peer_dest) { peer_socket = get_peer_socket(peer_dest); rc = (*peer_socket)->write(dest, strlen(dest), WS_BUFFER, &call_peer); } else { rc = twinSippSocket->write(dest, strlen(dest), WS_BUFFER, &call_peer); } if(rc < 0) { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CMD_NOT_SENT); delete this; return(-1); } return(0); } else return(-1); } int call::sendCmdBuffer(char* cmd) { char * dest; char delimitor[2]; int rc; delimitor[0]=27; delimitor[1]=0; dest = cmd ; strcat(dest, delimitor); rc = twinSippSocket->write(dest, strlen(dest), WS_BUFFER, &twinSippSocket->ss_dest); if(rc < 0) { computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_CMD_NOT_SENT); delete this; return(-1); } return(0); } char* call::createSendingMessage(SendingMessage *src, int P_index, int *msgLen) { static char msg_buffer[SIPP_MAX_MSG_SIZE+2]; return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer), msgLen); } char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len, int *msgLen) { char * length_marker = NULL; char * auth_marker = NULL; MessageComponent *auth_comp = NULL; bool auth_comp_allocated = false; int len_offset = 0; char *dest = msg_buffer; bool suppresscrlf = false; *dest = '\0'; for (int i = 0; i < src->numComponents(); i++) { MessageComponent *comp = src->getComponent(i); int left = buf_len - (dest - msg_buffer); switch(comp->type) { case E_Message_Literal: if (suppresscrlf) { char *ptr = comp->literal; while (isspace(*ptr)) ptr++; dest += snprintf(dest, left, "%s", ptr); suppresscrlf = false; } else { memcpy(dest, comp->literal, comp->literalLen); dest += comp->literalLen; *dest = '\0'; } break; case E_Message_Remote_IP: dest += snprintf(dest, left, "%s", remote_ip_escaped); break; case E_Message_Remote_Host: dest += snprintf(dest, left, "%s", remote_host); break; case E_Message_Remote_Port: dest += snprintf(dest, left, "%d", remote_port + comp->offset); break; case E_Message_Local_IP: dest += snprintf(dest, left, "%s", local_ip_escaped); break; case E_Message_Local_Port: int port; if((multisocket) && (sendMode != MODE_SERVER)) { port = call_port; } else { port = local_port; } dest += snprintf(dest, left, "%d", port + comp->offset); break; case E_Message_Transport: dest += snprintf(dest, left, "%s", TRANSPORT_TO_STRING(transport)); break; case E_Message_Local_IP_Type: dest += snprintf(dest, left, "%s", (local_ip_is_ipv6 ? "6" : "4")); break; case E_Message_Server_IP: { /* We should do this conversion once per socket creation, rather than * repeating it every single time. */ struct sockaddr_storage server_sockaddr; sipp_socklen_t len = sizeof(server_sockaddr); getsockname(call_socket->ss_fd, (sockaddr *)(void *)&server_sockaddr, &len); char address[INET6_ADDRSTRLEN]; if (getnameinfo(_RCAST(sockaddr*, &server_sockaddr), len, address, sizeof(address), NULL, 0, NI_NUMERICHOST) < 0) { ERROR_NO("Unable to get socket name information"); } dest += snprintf(dest, left, "%s", address); } break; case E_Message_Media_IP: dest += snprintf(dest, left, "%s", media_ip); break; case E_Message_Media_Port: case E_Message_Auto_Media_Port: { int port = media_port + comp->offset; if (comp->type == E_Message_Auto_Media_Port) { port = media_port + (4 * (number - 1)) % 10000 + comp->offset; } #ifdef PCAPPLAY char *begin = dest; while (begin > msg_buffer) { if (*begin == '\n') { break; } begin--; } if (begin == msg_buffer) { ERROR("Can not find beginning of a line for the media port!"); } play_args_t* play_args = NULL; if (strstr(begin, "audio")) { play_args = &play_args_a; } else if (strstr(begin, "image")) { play_args = &play_args_i; } else if (strstr(begin, "video")) { play_args = &play_args_v; } else { ERROR("media_port keyword with no audio or video on the current line (%s)", begin); } if (media_ip_is_ipv6) { (_RCAST(struct sockaddr_in6 *, &(play_args->from)))->sin6_port = htons(port); } else { (_RCAST(struct sockaddr_in *, &(play_args->from)))->sin_port = htons(port); } #endif dest += sprintf(dest, "%u", port); break; } #ifdef RTP_STREAM case E_Message_RTPStream_Audio_Port: /* DEPRECATED */ case E_Message_RTPStream_Video_Port: /* DEPRECATED */ dest += sprintf(dest, "%u", media_port); break; #endif case E_Message_Media_IP_Type: dest += snprintf(dest, left, "%s", (media_ip_is_ipv6 ? "6" : "4")); break; case E_Message_Call_Number: dest += snprintf(dest, left, "%u", number); break; case E_Message_DynamicId: dest += snprintf(dest, left, "%u", call::dynamicId); // increment at each request dynamicId += stepDynamicId; if ( this->dynamicId > maxDynamicId ) { call::dynamicId = call::startDynamicId; } ; break; case E_Message_Call_ID: dest += snprintf(dest, left, "%s", id); break; case E_Message_CSEQ: dest += snprintf(dest, left, "%u", cseq + comp->offset); break; case E_Message_PID: dest += snprintf(dest, left, "%d", pid); break; case E_Message_Service: dest += snprintf(dest, left, "%s", service); break; case E_Message_Branch: /* Branch is magic cookie + call number + message index in scenario */ if(P_index == -2) { dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, msg_index-1 + comp->offset); } else { dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, P_index + comp->offset); } break; case E_Message_Index: dest += snprintf(dest, left, "%d", P_index); break; case E_Message_Next_Url: if (next_req_url) { dest += sprintf(dest, "%s", next_req_url); } break; case E_Message_Len: length_marker = dest; dest += snprintf(dest, left, " "); len_offset = comp->offset; break; case E_Message_Authentication: if (auth_marker) { ERROR("Only one [authentication] keyword is currently supported!"); } auth_marker = dest; dest += snprintf(dest, left, "[authentication place holder]"); auth_comp = comp; break; case E_Message_Peer_Tag_Param: if(peer_tag) { dest += snprintf(dest, left, ";tag=%s", peer_tag); } break; case E_Message_Routes: if (dialog_route_set) { dest += sprintf(dest, "Route: %s", dialog_route_set); } else if (*(dest - 1) == '\n') { suppresscrlf = true; } break; case E_Message_ClockTick: dest += snprintf(dest, left, "%lu", clock_tick); break; case E_Message_Timestamp: struct timeval currentTime; gettimeofday(¤tTime, NULL); dest += snprintf(dest, left, "%s", CStat::formatTime(¤tTime)); break; case E_Message_Date: char buf[256]; time_t t; struct tm *tm; t = time(NULL); tm = gmtime(&t); strftime(buf, 256, "%a, %d %b %Y %T %Z", tm); dest += snprintf(dest, left, "%s", buf); break; case E_Message_Users: dest += snprintf(dest, left, "%d", users); break; case E_Message_UserID: dest += snprintf(dest, left, "%d", userId); break; case E_Message_SippVersion: /* Drop the initial "v" from the VERSION string for legacy reasons. */ dest += snprintf(dest, left, "%s", (const char*)VERSION + 1); break; case E_Message_Variable: { int varId = comp->varId; CCallVariable *var = M_callVariableTable->getVar(varId); if(var->isSet()) { if (var->isRegExp()) { dest += sprintf(dest, "%s", var->getMatchingValue()); } else if (var->isDouble()) { dest += sprintf(dest, "%lf", var->getDouble()); } else if (var->isString()) { dest += sprintf(dest, "%s", var->getString()); } else if (var->isBool()) { dest += sprintf(dest, "true"); } } else if (var->isBool()) { dest += sprintf(dest, "false"); } if (*(dest - 1) == '\n') { suppresscrlf = true; } break; } case E_Message_Fill: { int varId = comp->varId; int length = (int) M_callVariableTable->getVar(varId)->getDouble(); if (length < 0) { length = 0; } char *filltext = comp->literal; int filllen = strlen(filltext); if (filllen == 0) { ERROR("Internal error: [fill] keyword has zero-length text."); } for (int i = 0, j = 0; i < length; i++, j++) { *dest++ = filltext[j % filllen]; } *dest = '\0'; break; } case E_Message_File: { char buffer[MAX_HEADER_LEN]; createSendingMessage(comp->comp_param.filename, -2, buffer, sizeof(buffer)); FILE *f = fopen(buffer, "r"); if (!f) { ERROR("Could not open '%s': %s", buffer, strerror(errno)); } int ret; while ((ret = fread(dest, 1, left, f)) > 0) { left -= ret; dest += ret; } if (ret < 0) { ERROR("Error reading '%s': %s", buffer, strerror(errno)); } fclose(f); break; } case E_Message_Injection: { char *orig_dest = dest; getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, comp->comp_param.field_param.line, dest); /* We are injecting an authentication line. */ if (char *tmp = strstr(orig_dest, "[authentication")) { if (auth_marker) { ERROR("Only one [authentication] keyword is currently supported!"); } auth_marker = tmp; auth_comp = (struct MessageComponent *)calloc(1, sizeof(struct MessageComponent)); if (!auth_comp) { ERROR("Out of memory!"); } auth_comp_allocated = true; tmp = strchr(auth_marker, ']'); char c = *tmp; *tmp = '\0'; SendingMessage::parseAuthenticationKeyword(call_scenario, auth_comp, auth_marker); *tmp = c; } if (*(dest - 1) == '\n') { suppresscrlf = true; } break; } case E_Message_Last_Header: { char * last_header = get_last_header(comp->literal); if(last_header) { dest += sprintf(dest, "%s", last_header); } if (*(dest - 1) == '\n') { suppresscrlf = true; } break; } case E_Message_Custom: { dest += comp->comp_param.fxn(this, comp, dest, left); break; } case E_Message_Last_Message: if(last_recv_msg && strlen(last_recv_msg)) { dest += sprintf(dest, "%s", last_recv_msg); } break; case E_Message_Last_Request_URI: { char * last_request_uri = get_last_request_uri(); dest += sprintf(dest, "%s", last_request_uri); free(last_request_uri); break; } case E_Message_Last_CSeq_Number: { int last_cseq = 0; char *last_header = get_last_header("CSeq:"); if(last_header) { last_header += 5; /* Extract the integer value of the field */ while(isspace(*last_header)) last_header++; sscanf(last_header,"%d", &last_cseq); } dest += sprintf(dest, "%d", last_cseq + comp->offset); break; } case E_Message_TDM_Map: if (!use_tdmmap) ERROR("[tdmmap] keyword without -tdmmap parameter on command line"); dest += snprintf(dest, left, "%d.%d.%d/%d", tdm_map_x+(int((tdm_map_number)/((tdm_map_b+1)*(tdm_map_c+1))))%(tdm_map_a+1), tdm_map_h, tdm_map_y+(int((tdm_map_number)/(tdm_map_c+1)))%(tdm_map_b+1), tdm_map_z+(tdm_map_number)%(tdm_map_c+1) ); break; } } /* Need the body for length and auth-int calculation */ char *body; const char *auth_body = NULL; if (length_marker || auth_marker) { body = strstr(msg_buffer, "\r\n\r\n"); if (body) { auth_body = body; auth_body += strlen("\r\n\r\n"); } } if (!auth_body) { auth_body = ""; } /* Fix up the length. */ if (length_marker) { if (auth_marker > body) { ERROR("The authentication keyword should appear in the message header, not the body!"); } if (body && dest - body > 4 && dest - body < 100004) { char tmp = length_marker[5]; sprintf(length_marker, "%5u", (unsigned)(dest - body - 4 + len_offset)); length_marker[5] = tmp; } else { // Other cases: Content-Length is 0 sprintf(length_marker, " 0\r\n\r\n"); } } if (msgLen) { *msgLen = dest - msg_buffer; } /* * The authentication substitution must be done outside the above * loop because auth-int will use the body (which must have already * been keyword substituted) to build the md5 hash */ if (auth_marker) { if (!dialog_authentication) { ERROR("Authentication keyword without dialog_authentication!"); } int auth_marker_len; int authlen; auth_marker_len = (strchr(auth_marker, ']') + 1) - auth_marker; /* Determine the type of credentials. */ char result[MAX_HEADER_LEN]; if (dialog_challenge_type == 401) { /* Registrars use Authorization */ authlen = sprintf(result, "Authorization: "); } else { /* Proxies use Proxy-Authorization */ authlen = sprintf(result, "Proxy-Authorization: "); } /* Build the auth credenticals */ char uri[MAX_HEADER_LEN]; sprintf (uri, "%s:%d", remote_ip, remote_port); /* These cause this function to not be reentrant. */ static char my_auth_user[MAX_HEADER_LEN + 2]; static char my_auth_pass[MAX_HEADER_LEN + 2]; static char my_aka_OP[MAX_HEADER_LEN + 2]; static char my_aka_AMF[MAX_HEADER_LEN + 2]; static char my_aka_K[MAX_HEADER_LEN + 2]; createSendingMessage(auth_comp->comp_param.auth_param.auth_user, -2, my_auth_user, sizeof(my_auth_user)); createSendingMessage(auth_comp->comp_param.auth_param.auth_pass, -2, my_auth_pass, sizeof(my_auth_pass)); createSendingMessage(auth_comp->comp_param.auth_param.aka_K, -2, my_aka_K, sizeof(my_aka_K)); createSendingMessage(auth_comp->comp_param.auth_param.aka_AMF, -2, my_aka_AMF, sizeof(my_aka_AMF)); createSendingMessage(auth_comp->comp_param.auth_param.aka_OP, -2, my_aka_OP, sizeof(my_aka_OP)); if (createAuthHeader(my_auth_user, my_auth_pass, src->getMethod(), uri, auth_body, dialog_authentication, my_aka_OP, my_aka_AMF, my_aka_K, next_nonce_count++, result + authlen) == 0) { ERROR("%s", result + authlen); } authlen = strlen(result); /* Shift the end of the message to its rightful place. */ memmove(auth_marker + authlen, auth_marker + auth_marker_len, strlen(auth_marker + auth_marker_len) + 1); /* Copy our result into the hole. */ memcpy(auth_marker, result, authlen); if (msgLen) { *msgLen += (authlen - auth_marker_len); } } if (auth_comp_allocated) { SendingMessage::freeMessageComponent(auth_comp); } return msg_buffer; } bool call::process_twinSippCom(char * msg) { int search_index; bool found = false; T_ActionResult actionResult; callDebug("Processing incoming command for call-ID %s:\n%s\n\n", id, msg); setRunning(); if (checkInternalCmd(msg) == false) { for(search_index = msg_index; search_index < (int)call_scenario->messages.size(); search_index++) { if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECVCMD) { if ((call_scenario->messages[search_index] -> optional) || (call_scenario->messages[search_index] -> M_type == MSG_TYPE_NOP)) { continue; } /* The received message is different from the expected one */ TRACE_MSG("Unexpected control message received (I was expecting a different type of message):\n%s\n", msg); callDebug("Unexpected control message received (I was expecting a different type of message):\n%s\n\n", msg); return rejectCall(); } else { if(extendedTwinSippMode) { // 3pcc extended mode if(check_peer_src(msg, search_index)) { found = true; break; } else { WARNING("Unexpected sender for the received peer message\n%s\n", msg); return rejectCall(); } } else { found = true; break; } } } if (found) { call_scenario->messages[search_index]->M_nbCmdRecv ++; do_bookkeeping(call_scenario->messages[search_index]); // variable treatment // Remove \r, \n at the end of a received command // (necessary for transport, to be removed for usage) while ( (msg[strlen(msg)-1] == '\n') && (msg[strlen(msg)-2] == '\r') ) { msg[strlen(msg)-2] = 0; } actionResult = executeAction(msg, call_scenario->messages[search_index]); if(actionResult != call::E_AR_NO_ERROR) { // Store last action result if it is an error // and go on with the scenario call::last_action_result = actionResult; if (actionResult == E_AR_STOP_CALL) { return rejectCall(); } else if (actionResult == E_AR_CONNECT_FAILED) { terminate(CStat::E_FAILED_TCP_CONNECT); return false; } } } else { TRACE_MSG("Unexpected control message received (no such message found):\n%s\n", msg); callDebug("Unexpected control message received (no such message found):\n%s\n\n", msg); return rejectCall(); } msg_index = search_index; //update the state machine return(next()); } else { return (false); } } bool call::checkInternalCmd(char * cmd) { char * L_ptr1, * L_ptr2, L_backup; L_ptr1 = strstr(cmd, "internal-cmd:"); if (!L_ptr1) { return (false); } L_ptr1 += 13 ; while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; } if (!(*L_ptr1)) { return (false); } L_ptr2 = L_ptr1; while((*L_ptr2) && (*L_ptr2 != ' ') && (*L_ptr2 != '\t') && (*L_ptr2 != '\r') && (*L_ptr2 != '\n')) { L_ptr2 ++; } if(!*L_ptr2) { return (false); } L_backup = *L_ptr2; *L_ptr2 = 0; if (strcmp(L_ptr1, "abort_call") == 0) { *L_ptr2 = L_backup; computeStat(CStat::E_CALL_FAILED); abortCall(true); return (true); } *L_ptr2 = L_backup; return (false); } bool call::check_peer_src(char * msg, int search_index) { char * L_ptr1, * L_ptr2, L_backup ; L_ptr1 = strstr(msg, "From:"); if (!L_ptr1) { return (false); } L_ptr1 += 5 ; while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; } if (!(*L_ptr1)) { return (false); } L_ptr2 = L_ptr1; while((*L_ptr2) && (*L_ptr2 != ' ') && (*L_ptr2 != '\t') && (*L_ptr2 != '\r') && (*L_ptr2 != '\n')) { L_ptr2 ++; } if(!*L_ptr2) { return (false); } L_backup = *L_ptr2; *L_ptr2 = 0; if (strcmp(L_ptr1, call_scenario->messages[search_index] -> peer_src) == 0) { *L_ptr2 = L_backup; return(true); } *L_ptr2 = L_backup; return (false); } void call::extract_cseq_method(char* method, const char* msg) { const char* cseq; if ((cseq = strstr (msg, "CSeq"))) { const char* value; if ((value = strchr(cseq, ':'))) { value++; while (isspace(*value)) value++; // ignore any white spaces after the : while (!isspace(*value)) value++; // ignore the CSEQ number while (isspace(*value)) value++; // ignore spaces after CSEQ number const char* end = value; int nbytes = 0; /* A '\r' terminates the line, so we want to catch that too. */ while ((*end != '\r') && (*end != '\n')) { end++; nbytes++; } if (nbytes > 0) strncpy (method, value, nbytes); method[nbytes] = '\0'; } } } void call::extract_transaction(char* txn, const char* msg) { char *via = get_header_content(msg, "via:"); if (!via) { txn[0] = '\0'; return; } char *branch = strstr(via, ";branch="); if (!branch) { txn[0] = '\0'; return; } branch += strlen(";branch="); while (*branch && *branch != ';' && *branch != ',' && !isspace(*branch)) { *txn++ = *branch++; } *txn = '\0'; } void call::formatNextReqUrl(const char* contact) { /* clean up the next_req_url */ while (*contact != '\0' && (*contact == ' ' || *contact == '\t')) { ++contact; } if (*contact == '<') { contact += 1; const char* end = strchr(contact, '>'); if (end) { next_req_url[0] = '\0'; strncat(next_req_url, contact, min(MAX_HEADER_LEN - 1, (int)(end - contact))); /* fits MAX_HEADER_LEN */ } } else { next_req_url[0] = '\0'; strncat(next_req_url, contact, MAX_HEADER_LEN - 1); } } void call::computeRouteSetAndRemoteTargetUri(const char* rr, const char* contact, bool bRequestIncoming) { if (!*contact) { WARNING("Cannot record route set if there is no Contact"); return; } if (!*rr) { /* There are no RR headers. Simply set up the contact as our * target uri. Note that this is only called if there was no * dialog_route_set at the moment. And in either case, we * wouldn't want to clear the dialog_route_set because changing * RR mid-dialog is not allowed. */ formatNextReqUrl(contact); return; } std::vector headers = split(rr, ','); std::vector::iterator it; std::vector::iterator end; int direction; if (bRequestIncoming) { it = headers.begin(); end = headers.end(); direction = 1; } else { it = headers.end() - 1; end = headers.begin() - 1; direction = -1; } std::vector routes; std::string targetUri; bool first = true; for (; it != end; it += direction) { const std::string& header = *it; if (first && header.find(";lr") == std::string::npos) { /* If the next hop is a static router, set target URI to * that router. We'll push the original contact onto the end * of the route set. We won't need to record this route, * because we've set the target to it. */ targetUri = header; } else { first = false; routes.push_back(trim(header)); } } /* If target URI is set, the first hop is a strict router. Add the * Contact as tailing route. */ if (targetUri.length()) { routes.push_back(trim(contact)); } else { targetUri = contact; } if (routes.size()) { dialog_route_set = strdup(join(routes, ", ").c_str()); } formatNextReqUrl(targetUri.c_str()); } bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn) { message *curmsg = call_scenario->messages[index]; if ((curmsg->recv_request)) { if (curmsg->regexp_match) { if (curmsg->regexp_compile == NULL) { regex_t *re = new regex_t; /* No regex match position needed (NOSUB), we're simply * looking for the * regex. */ if (regcomp(re, curmsg->recv_request, REGCOMP_PARAMS|REG_NOSUB)) { ERROR("Invalid regular expression for index %d: %s", index, curmsg->recv_request); } curmsg->regexp_compile = re; } return !regexec(curmsg->regexp_compile, request, (size_t)0, NULL, REGEXEC_PARAMS); } else { return !strcmp(curmsg->recv_request, request); } } else if (curmsg->recv_response && (curmsg->recv_response == reply_code)) { /* This is a potential candidate, we need to match transactions. */ if (curmsg->response_txn) { if (transactions[curmsg->response_txn - 1].txnID && !strcmp(transactions[curmsg->response_txn - 1].txnID, txn)) { return true; } else { return false; } } else if (index == 0) { /* Always true for the first message. */ return true; } else if (curmsg->recv_response_for_cseq_method_list && strstr(curmsg->recv_response_for_cseq_method_list, responsecseqmethod)) { /* If we do not have a transaction defined, we just check the CSEQ method. */ return true; } else { return false; } } return false; } void call::queue_up(const char* msg) { free(queued_msg); queued_msg = strdup(msg); } bool call::process_incoming(const char* msg, const struct sockaddr_storage* src) { int reply_code = 0; static char request[65]; char responsecseqmethod[65]; char txn[MAX_HEADER_LEN]; unsigned long cookie = 0; const char* ptr; int search_index; bool found = false; T_ActionResult actionResult; getmilliseconds(); callDebug("Processing %zu byte incoming message for call-ID %s (hash %lu):\n%s\n\n", strlen(msg), id, hash(msg), msg); setRunning(); message *curmsg = call_scenario->messages[msg_index]; /* Ignore the messages received during a pause if -pause_msg_ign is set */ if (curmsg->M_type == MSG_TYPE_PAUSE && pause_msg_ign) { return true; } /* Get our destination if we have none. */ if (call_peer.ss_family == AF_UNSPEC && src) { memcpy(&call_peer, src, sizeof(call_peer)); } /* Authorize nop as a first command, even in server mode */ if (msg_index == 0 && curmsg->M_type == MSG_TYPE_NOP) { queue_up(msg); paused_until = 0; return run(); } responsecseqmethod[0] = '\0'; txn[0] = '\0'; /* Check that we have a To:-header */ if (!get_header(msg, "To:", false)[0] && !process_unexpected(msg)) { return false; } if ((transport == T_UDP) && (retrans_enabled)) { /* Detects retransmissions from peer and retransmit the * message which was sent just after this one was received */ cookie = hash(msg); if((recv_retrans_recv_index >= 0) && (recv_retrans_hash == cookie)) { int status; if(lost(recv_retrans_recv_index)) { TRACE_MSG("%s message (retrans) lost (recv).", TRANSPORT_TO_STRING(transport)); callDebug("%s message (retrans) lost (recv) (hash %lu)\n", TRANSPORT_TO_STRING(transport), hash(msg)); if(comp_state) { comp_free(&comp_state); } call_scenario->messages[recv_retrans_recv_index] -> nb_lost++; return true; } call_scenario->messages[recv_retrans_recv_index] -> nb_recv_retrans++; send_scene(recv_retrans_send_index, &status, NULL); if(status >= 0) { call_scenario->messages[recv_retrans_send_index] -> nb_sent_retrans++; computeStat(CStat::E_RETRANSMISSION); } else if(status < 0) { return false; } return true; } if((last_recv_index >= 0) && (last_recv_hash == cookie)) { /* This one has already been received, but not processed * yet => (has not triggered something yet) so we can discard. * * This case appears when the UAS has send a 200 but not received * a ACK yet. Thus, the UAS retransmit the 200 (invite transaction) * until it receives a ACK. In this case, it nevers sends the 200 * from the BYE, until it has reveiced the previous 200. Thus, * the UAC retransmit the BYE, and this BYE is considered as an * unexpected. * * This case can also appear in case of message duplication by * the network. This should not be considered as an unexpected. */ call_scenario->messages[last_recv_index]->nb_recv_retrans++; return true; } } #ifdef RTP_STREAM /* Check if message has a SDP in it; and extract media information. */ if (!strcmp(get_header_content(msg, "Content-Type:"), "application/sdp") && hasMedia == 1 && !curmsg->ignoresdp) { const char* ptr = get_header_content(msg, "Content-Length:"); if (ptr && atoll(ptr) > 0) { extract_rtp_remote_addr(msg); } } #endif /* Is it a response ? */ if ((msg[0] == 'S') && (msg[1] == 'I') && (msg[2] == 'P') && (msg[3] == '/') && (msg[4] == '2') && (msg[5] == '.') && (msg[6] == '0') ) { reply_code = get_reply_code(msg); if (!reply_code) { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } #ifdef PCAPPLAY } else if (hasMedia == 1 && !curmsg->ignoresdp && *(strstr(msg, "\r\n\r\n") + 4) != '\0') { /* Get media info if we find something like an SDP */ get_remote_media_addr(msg); #endif } /* It is a response: update peer_tag */ ptr = get_peer_tag(msg); if (ptr) { if(strlen(ptr) > (MAX_HEADER_LEN - 1)) { ERROR("Peer tag too long. Change MAX_HEADER_LEN and recompile sipp"); } if(peer_tag) { free(peer_tag); } peer_tag = strdup(ptr); if (!peer_tag) { ERROR("Out of memory allocating peer tag."); } } request[0]=0; // extract the cseq method from the response extract_cseq_method (responsecseqmethod, msg); extract_transaction (txn, msg); } else if ((ptr = strchr(msg, ' '))) { if ((ptr - msg) < 64) { memcpy(request, msg, ptr - msg); request[ptr - msg] = 0; // Check if we received an ACK => call established if (strcmp(request,"ACK") == 0) { call_established = true; } #ifdef PCAPPLAY /* In case of INVITE or re-INVITE, ACK or PRACK get the media info if needed (= we got a pcap play action) */ if (((strncmp(request, "INVITE", 6) == 0) || (strncmp(request, "ACK", 3) == 0) || (strncmp(request, "PRACK", 5) == 0)) && hasMedia == 1 && !curmsg->ignoresdp) { get_remote_media_addr(msg); } #endif reply_code = 0; } else { ERROR("SIP method too long in received message '%s'", msg); } } else { ERROR("Invalid sip message received '%s'", msg); } /* Try to find it in the expected non mandatory responses * until the first mandatory response in the scenario */ for (search_index = msg_index; search_index < (int)call_scenario->messages.size(); search_index++) { if (!matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) { if (call_scenario->messages[search_index]->optional) { continue; } /* The received message is different for the expected one */ break; } found = true; /* TODO : this is a little buggy: If a 100 trying from an INVITE * is delayed by the network until the BYE is sent, it may * stop BYE transmission erroneously, if the BYE also expects * a 100 trying. */ break; } /* Try to find it in the old non-mandatory receptions */ if (!found) { bool contig = true; for(search_index = msg_index - 1; search_index >= 0; search_index--) { if (call_scenario->messages[search_index]->optional == OPTIONAL_FALSE) { contig = false; } if (matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) { if (contig || call_scenario->messages[search_index]->optional == OPTIONAL_GLOBAL) { found = true; break; } else { if (int checkTxn = call_scenario->messages[search_index]->response_txn) { /* This is a reply to an old transaction. */ if (!strcmp(transactions[checkTxn - 1].txnID, txn)) { /* This reply is provisional, so it should have no effect if we recieve it out-of-order. */ if (reply_code >= 100 && reply_code <= 199) { TRACE_MSG("-----------------------------------------------\n" "Ignoring provisional %s message for transaction %s:\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg); callDebug("Ignoring provisional %s message for transaction %s (hash %lu):\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); return true; } else if (int ackIndex = transactions[checkTxn - 1].ackIndex) { /* This is the message before an ACK, so verify that this is an invite transaction. */ assert (call_scenario->transactions[checkTxn - 1].isInvite); sendBuffer(createSendingMessage(call_scenario->messages[ackIndex] -> send_scheme, ackIndex)); return true; } else { assert (!call_scenario->transactions[checkTxn - 1].isInvite); /* This is a non-provisional message for the transaction, and * we have already gotten our allowable response. Just make sure * that it is not a retransmission of the final response. */ if (transactions[checkTxn - 1].txnResp == hash(msg)) { /* We have gotten this retransmission out-of-order, let's just ignore it. */ TRACE_MSG("-----------------------------------------------\n" "Ignoring final %s message for transaction %s:\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg); callDebug("Ignoring final %s message for transaction %s (hash %lu):\n\n%s\n", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); WARNING("Ignoring final %s message for transaction %s (hash %lu):\n\n%s", TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg); return true; } } } } else { /* * we received a non mandatory msg for an old transaction (this could be due to a retransmit. * If this response is for an INVITE transaction, retransmit the ACK to quench retransmits. */ if ( (reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)) ) && (call_scenario->messages[search_index+1]->M_type == MSG_TYPE_SEND) && (call_scenario->messages[search_index+1]->send_scheme->isAck()) ) { sendBuffer(createSendingMessage(call_scenario->messages[search_index+1] -> send_scheme, (search_index+1))); return true; } } } } } } /* If it is still not found, process an unexpected message */ if(!found) { if (call_scenario->unexpected_jump >= 0) { bool recursive = false; if (call_scenario->retaddr >= 0) { if (M_callVariableTable->getVar(call_scenario->retaddr)->getDouble() != 0) { /* We are already in a jump! */ recursive = true; } else { M_callVariableTable->getVar(call_scenario->retaddr)->setDouble(msg_index); } } if (!recursive) { if (call_scenario->pausedaddr >= 0) { M_callVariableTable->getVar(call_scenario->pausedaddr)->setDouble(paused_until); } msg_index = call_scenario->unexpected_jump; queue_up(msg); paused_until = 0; return run(); } else { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } } } else { T_AutoMode L_case; if ((L_case = checkAutomaticResponseMode(request)) == 0) { if (!process_unexpected(msg)) { return false; // Call aborted by unexpected message handling } } else { // call aborted by automatic response mode if needed return automaticResponseMode(L_case, msg); } } } int test = (!found) ? -1 : call_scenario->messages[search_index]->test; /* test==0: No branching" * test==-1 branching without testing" * test>0 branching with testing */ /* Simulate loss of messages */ if(lost(search_index)) { TRACE_MSG("%s message lost (recv).", TRANSPORT_TO_STRING(transport)); callDebug("%s message lost (recv) (hash %lu).\n", TRANSPORT_TO_STRING(transport), hash(msg)); if(comp_state) { comp_free(&comp_state); } call_scenario->messages[search_index] -> nb_lost++; return true; } /* If we are part of a transaction, mark this as the final response. */ if (int checkTxn = call_scenario->messages[search_index]->response_txn) { transactions[checkTxn - 1].txnResp = hash(msg); } /* Handle counters and RTDs for this message. */ do_bookkeeping(call_scenario->messages[search_index]); /* Increment the recv counter */ call_scenario->messages[search_index] -> nb_recv++; // Action treatment if (found) { //WARNING("---EXECUTE_ACTION_ON_MSG---%s---", msg); actionResult = executeAction(msg, call_scenario->messages[search_index]); if(actionResult != call::E_AR_NO_ERROR) { // Store last action result if it is an error // and go on with the scenario call::last_action_result = actionResult; if (actionResult == E_AR_STOP_CALL) { return rejectCall(); } else if (actionResult == E_AR_CONNECT_FAILED) { terminate(CStat::E_FAILED_TCP_CONNECT); return false; } } } if (*request) { // update [cseq] with received CSeq unsigned long int rcseq = get_cseq_value(msg); if (rcseq > cseq) cseq = rcseq; } /* This is an ACK/PRACK or a response, and its index is greater than the * current active retransmission message, so we stop the retrans timer. * True also for CANCEL and BYE that we also want to answer to */ if(((reply_code) || ((!strcmp(request, "ACK")) || (!strcmp(request, "CANCEL")) || (!strcmp(request, "BYE")) || (!strcmp(request, "PRACK")))) && (search_index > last_send_index)) { /* * We should stop any retransmission timers on receipt of a provisional response only for INVITE * transactions. Non INVITE transactions continue to retransmit at T2 until a final response is * received */ if ( (0 == reply_code) || // means this is a request. (200 <= reply_code) || // final response ((0 != reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)))) ) { // prov for INVITE next_retrans = 0; } else { /* * We are here due to a provisional response for non INVITE. Update our next retransmit. */ next_retrans = clock_tick + global_t2; nb_last_delay = global_t2; } } /* This is a response with 200 so set the flag indicating that an * ACK is pending (used to prevent from release a call with CANCEL * when an ACK+BYE should be sent instead) */ if (reply_code == 200) { ack_is_pending = true; } /* store the route set only once. TODO: does not support target refreshes!! */ if (call_scenario->messages[search_index]->bShouldRecordRoutes && dialog_route_set == NULL) { realloc_ptr = (char*)realloc(next_req_url, MAX_HEADER_LEN); if (realloc_ptr) { next_req_url = realloc_ptr; } else { free(next_req_url); ERROR("Out of memory!"); return false; } /* cache the route set and the contact */ char rr[MAX_HEADER_LEN], contact[MAX_HEADER_LEN]; rr[0] = contact[0] = '\0'; /* yuck, get_header_content returns a static buffer :( */ strncat(rr, get_header_content(msg, "Record-Route:"), MAX_HEADER_LEN - 1); strncat(contact, get_header_content(msg, "Contact:"), MAX_HEADER_LEN - 1); computeRouteSetAndRemoteTargetUri(rr, contact, !reply_code); // WARNING("next_req_url is [%s]", next_req_url); } /* store the authentication info */ if ((call_scenario->messages[search_index] -> bShouldAuthenticate) && (reply_code == 401 || reply_code == 407)) { /* is a challenge */ char auth[MAX_HEADER_LEN]; memset(auth, 0, sizeof(auth)); strncpy(auth, get_header_content(msg, (char*)"Proxy-Authenticate:"), sizeof(auth) - 1); if (auth[0] == 0) { strncpy(auth, get_header_content(msg, (char*)"WWW-Authenticate:"), sizeof(auth) - 1); } if (auth[0] == 0) { ERROR("Couldn't find 'Proxy-Authenticate' or 'WWW-Authenticate' in 401 or 407!"); } realloc_ptr = (char *) realloc(dialog_authentication, strlen(auth) + 2); if (realloc_ptr) { dialog_authentication = realloc_ptr; } else { free(dialog_authentication); ERROR("Out of memory!"); return false; } sprintf(dialog_authentication, "%s", auth); /* Store the code of the challenge for building the proper header */ dialog_challenge_type = reply_code; next_nonce_count = 1; } /* If we are not advancing state, we should quite before we change this stuff. */ if (!call_scenario->messages[search_index]->advance_state) { return true; } /* Store last received message information for all messages so that we can * correctly identify retransmissions, and use its body for inclusion * in our messages. */ last_recv_index = search_index; last_recv_hash = cookie; callDebug("Set Last Recv Hash: %lu (recv index %d)\n", last_recv_hash, last_recv_index); realloc_ptr = (char *) realloc(last_recv_msg, strlen(msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, msg); /* If this was a mandatory message, or if there is an explicit next label set * we must update our state machine. */ if (!call_scenario->messages[search_index]->optional || (call_scenario->messages[search_index]->next && (test == -1 || M_callVariableTable->getVar(test)->isSet()))) { /* If we are paused, then we need to wake up so that we properly go through the state machine. */ paused_until = 0; msg_index = search_index; return next(); } else { unsigned int timeout = wake(); unsigned int candidate; if (call_scenario->messages[search_index]->next && M_callVariableTable->getVar(test)->isSet()) { WARNING("Last message generates an error and will not be used for next sends (for last_ variables):\n%s\n", msg); } /* We are just waiting for a message to be received, if any of the * potential messages have a timeout we set it as our timeout. We * start from the next message and go until any non-receives. */ for(search_index++; search_index < (int)call_scenario->messages.size(); search_index++) { if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECV) { break; } candidate = call_scenario->messages[search_index] -> timeout; if (candidate == 0) { if (defl_recv_timeout == 0) { continue; } candidate = defl_recv_timeout; } if (!timeout || (clock_tick + candidate < timeout)) { timeout = clock_tick + candidate; } } setPaused(); } return true; } double call::get_rhs(CAction *currentAction) { if (currentAction->getVarInId()) { return M_callVariableTable->getVar(currentAction->getVarInId())->getDouble(); } else { return currentAction->getDoubleValue(); } } call::T_ActionResult call::executeAction(const char* msg, message* curmsg) { CActions* actions; CAction* currentAction; actions = curmsg->M_actions; // looking for action to do on this message if (actions == NULL) { return(call::E_AR_NO_ERROR); } for (int i = 0; i < actions->getActionSize(); i++) { currentAction = actions->getAction(i); if(currentAction == NULL) { continue; } if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) { char msgPart[MAX_SUB_MESSAGE_LENGTH]; /* Where to look. */ const char* haystack = NULL; if(currentAction->getLookingPlace() == CAction::E_LP_HDR) { extractSubMessage (msg, currentAction->getLookingChar(), msgPart, currentAction->getCaseIndep(), currentAction->getOccurrence(), currentAction->getHeadersOnly()); if(currentAction->getCheckIt() == true && (strlen(msgPart) == 0)) { // the sub message is not found and the checking action say it // MUST match --> Call will be marked as failed but will go on WARNING("Failed regexp match: header %s not found in message\n%s\n", currentAction->getLookingChar(), msg); return(call::E_AR_HDR_NOT_FOUND); } haystack = msgPart; } else if(currentAction->getLookingPlace() == CAction::E_LP_BODY) { haystack = strstr(msg, "\r\n\r\n"); if (!haystack) { if (currentAction->getCheckIt() == true) { WARNING("Failed regexp match: body not found in message\n%s\n", msg); return(call::E_AR_HDR_NOT_FOUND); } msgPart[0] = '\0'; haystack = msgPart; } haystack += strlen("\r\n\r\n"); } else if(currentAction->getLookingPlace() == CAction::E_LP_MSG) { haystack = msg; } else if(currentAction->getLookingPlace() == CAction::E_LP_VAR) { /* Get the input variable. */ haystack = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); if (!haystack) { if (currentAction->getCheckIt() == true) { WARNING("Failed regexp match: variable $%d not set", currentAction->getVarInId()); return(call::E_AR_HDR_NOT_FOUND); } } } else { ERROR("Invalid looking place: %d", currentAction->getLookingPlace()); } bool did_match = (currentAction->executeRegExp(haystack, M_callVariableTable) > 0); if (!did_match && currentAction->getCheckIt()) { // the message doesn't match and the checkit action say it MUST match // Allow easier regexp debugging WARNING("Failed regexp match: looking in '%s', with regexp '%s'", haystack, currentAction->getRegularExpression()); return(call::E_AR_REGEXP_DOESNT_MATCH); } else if (did_match && currentAction->getCheckItInverse()) { // The inverse of the above WARNING("Regexp matched but should not: looking in '%s', with regexp '%s'", haystack, currentAction->getRegularExpression()); return(call::E_AR_REGEXP_SHOULDNT_MATCH); } } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) { double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) { M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) { struct timeval tv; gettimeofday(&tv, NULL); M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec); M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec); } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) { /* Create strings from the sending messages. */ char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); char *key = strdup(createSendingMessage(currentAction->getMessage(1), -2)); if (inFiles.find(file) == inFiles.end()) { ERROR("Invalid injection file for insert: %s", file); } double value = inFiles[file]->lookup(key); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); free(file); free(key); } else if (currentAction->getActionType() == CAction::E_AT_INSERT) { /* Create strings from the sending messages. */ char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); char *value = strdup(createSendingMessage(currentAction->getMessage(1), -2)); if (inFiles.find(file) == inFiles.end()) { ERROR("Invalid injection file for insert: %s", file); } inFiles[file]->insert(value); free(file); free(value); } else if (currentAction->getActionType() == CAction::E_AT_REPLACE) { /* Create strings from the sending messages. */ char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2)); char *line = strdup(createSendingMessage(currentAction->getMessage(1), -2)); char *value = strdup(createSendingMessage(currentAction->getMessage(2), -2)); if (inFiles.find(file) == inFiles.end()) { ERROR("Invalid injection file for replace: %s", file); } char *endptr; int lineNum = (int)strtod(line, &endptr); if (*endptr) { ERROR("Invalid line number for replace: %s", line); } inFiles[file]->replace(lineNum, value); free(file); free(line); free(value); } else if (currentAction->getActionType() == CAction::E_AT_CLOSE_CON) { if (call_socket) { call_socket->close(); call_socket = NULL; } } else if (currentAction->getActionType() == CAction::E_AT_SET_DEST) { /* Change the destination for this call. */ char *str_host = strdup(createSendingMessage(currentAction->getMessage(0), -2)); char *str_port = strdup(createSendingMessage(currentAction->getMessage(1), -2)); char *str_protocol = strdup(createSendingMessage(currentAction->getMessage(2), -2)); char *endptr; int port = (int)strtod(str_port, &endptr); if (*endptr) { ERROR("Invalid port for setdest: %s", str_port); } int protocol = 0; if (!strcmp(str_protocol, "udp") || !strcmp(str_protocol, "UDP")) { protocol = T_UDP; } else if (!strcmp(str_protocol, "tcp") || !strcmp(str_protocol, "TCP")) { protocol = T_TCP; } else if (!strcmp(str_protocol, "tls") || !strcmp(str_protocol, "TLS")) { protocol = T_TLS; } else if (!strcmp(str_protocol, "sctp") || !strcmp(str_protocol, "SCTP")) { protocol = T_SCTP; } else { ERROR("Unknown transport for setdest: '%s'", str_protocol); } if (!call_socket && ((protocol == T_TCP && transport == T_TCP) || (protocol == T_SCTP && transport == T_SCTP))) { bool existing; if ((associate_socket(SIPpSocket::new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) { switch (protocol) { case T_SCTP: ERROR_NO("Unable to get a SCTP socket"); break; default: ERROR_NO("Unable to get a TCP socket"); } } if (!existing) { sipp_customize_socket(call_socket); } } if (!call_socket) { ERROR("Unable to get a socket"); } if (protocol != call_socket->ss_transport) { ERROR("Can not switch protocols during setdest."); } if (protocol == T_UDP) { /* Nothing to do. */ } else if (protocol == T_TLS) { ERROR("Changing destinations is not supported for TLS."); } else if (protocol == T_TCP || protocol == T_SCTP) { if (!multisocket) { ERROR("Changing destinations for TCP or SCTP requires multisocket mode."); } if (call_socket->ss_count > 1) { ERROR("Can not change destinations for a TCP/SCTP socket that has more than one user."); } } if (gai_getsockaddr(&call_peer, str_host, port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s' for setdest", str_host); } memcpy(&call_socket->ss_dest, &call_peer, sizeof(call_peer)); free(str_host); free(str_port); free(str_protocol); if (protocol == T_TCP || protocol == T_SCTP) { close(call_socket->ss_fd); call_socket->ss_fd = -1; call_socket->ss_changed_dest = true; if (call_socket->reconnect()) { if (reconnect_allowed()) { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ WARNING("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { WARNING("Unable to connect a TCP/SCTP/TLS socket"); } /* This connection failed. We must be in multisocket mode, because * otherwise we would already have a call_socket. This call can not * succeed, but does not affect any of our other calls. We do decrement * the reconnection counter however. */ if (reset_number != -1) { reset_number--; } return E_AR_CONNECT_FAILED; } else { if(errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR("Unable to connect a TCP/SCTP/TLS socket, remote peer error"); } else { ERROR_NO("Unable to connect a TCP/SCTP/TLS socket"); } } } } } else if (currentAction->getActionType() == CAction::E_AT_VERIFY_AUTH) { bool result; const char* lf; const char* end; lf = strchr(msg, '\n'); end = strchr(msg, ' '); if (!lf || !end) { result = false; } else if (lf < end) { result = false; } else { char *auth = get_header(msg, "Authorization:", true); char *method = (char *)malloc(end - msg + 1); strncpy(method, msg, end - msg); method[end - msg] = '\0'; /* Generate the username to verify it against. */ char *tmp = createSendingMessage(currentAction->getMessage(0), -2 /* do not add crlf*/); char *username = strdup(tmp); /* Generate the password to verify it against. */ tmp= createSendingMessage(currentAction->getMessage(1), -2 /* do not add crlf*/); char *password = strdup(tmp); /* Need the body for length and auth-int calculation */ const char *body; const char *auth_body = NULL; body = strstr(msg, "\r\n\r\n"); if (body) { auth_body = body; auth_body += strlen("\r\n\r\n"); } else { auth_body = ""; } result = verifyAuthHeader(username, password, method, auth, auth_body); free(username); free(password); free(method); } M_callVariableTable->getVar(currentAction->getVarId())->setBool(result); } else if (currentAction->getActionType() == CAction::E_AT_JUMP) { double operand = get_rhs(currentAction); if (msg_index == ((int)operand)) { ERROR("Jump statement at index %d jumps to itself and causes an infinite loop", msg_index); } msg_index = (int)operand - 1; /* -1 is allowed to go to the first label, but watch out * when using msg_index. */ if (msg_index < -1 || msg_index >= (int)call_scenario->messages.size()) { ERROR("Jump statement out of range (not 0 <= %d <= %zu)", msg_index + 1, call_scenario->messages.size()); } } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) { double operand = get_rhs(currentAction); paused_until = (int)operand; } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand); } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) { double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble(); double operand = get_rhs(currentAction); if (operand == 0) { WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId()); } else { M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) { bool value = currentAction->compare(M_callVariableTable); if ((currentAction->getCheckIt() && !value) || (currentAction->getCheckItInverse() && value) ) { double lhs = M_callVariableTable->getVar(currentAction->getVarInId())->getDouble(); double rhs = currentAction->getVarIn2Id() ? M_callVariableTable->getVar(currentAction->getVarIn2Id())->getDouble() : currentAction->getDoubleValue(); char *lhsName = call_scenario->allocVars->getName(currentAction->getVarInId()); const char *rhsName = ""; if (currentAction->getVarIn2Id()) { rhsName = call_scenario->allocVars->getName(currentAction->getVarIn2Id()); } const char *_inverse = currentAction->getCheckIt() ? "" : "_inverse"; call::T_ActionResult result = currentAction->getCheckIt() ? call::E_AR_TEST_DOESNT_MATCH : call::E_AR_TEST_SHOULDNT_MATCH; WARNING("test \"%s:%f %s %s:%f\" with check_it%s failed", lhsName, lhs, currentAction->comparatorToString(currentAction->getComparator()), rhsName, rhs, _inverse ); return(result); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (currentAction->getVarId() || (!currentAction->getCheckIt() && !currentAction->getCheckItInverse()) ) { M_callVariableTable->getVar(currentAction->getVarId())->setBool(value); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) { char *lhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString(); char *rhs = currentAction->getVarIn2Id() ? M_callVariableTable->getVar(currentAction->getVarIn2Id())->getString() : currentAction->getStringValue(); int value = strcmp(lhs, rhs); if ((currentAction->getCheckIt() && value) || (currentAction->getCheckItInverse() && !value) ) { char *lhsName = call_scenario->allocVars->getName(currentAction->getVarInId()); const char *rhsName = ""; if (currentAction->getVarIn2Id()) { rhsName = call_scenario->allocVars->getName(currentAction->getVarIn2Id()); } const char *_inverse = currentAction->getCheckIt() ? "" : "_inverse"; call::T_ActionResult result = currentAction->getCheckIt() ? call::E_AR_STRCMP_DOESNT_MATCH : call::E_AR_STRCMP_SHOULDNT_MATCH; WARNING("strcmp %s:\"%s\" and %s:\"%s\" with check_it%s returned %d", lhsName, lhs, rhsName, rhs, _inverse, value ); return(result); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (currentAction->getVarId() || (!currentAction->getCheckIt() && !currentAction->getCheckItInverse()) ) { M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value); } } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) { CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId()); char *in = var->getString(); char *p = in; while (isspace(*p)) { p++; } char *q = strdup(p); var->setString(q); int l = strlen(q); for (int i = l - 1; (i >= 0) && isspace(q[i]); i--) { q[i] = '\0'; } } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) { double value; if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) { M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); } else { WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId()); } } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { double value = currentAction->getDistribution()->sample(); M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value); } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); char *str = strdup(x); if (!str) { ERROR("Out of memory duplicating string for assignment!"); } M_callVariableTable->getVar(currentAction->getVarId())->setString(str); } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); LOG_MSG("%s\n", x); } else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); WARNING("%s", x); } else if (currentAction->getActionType() == CAction::E_AT_LOG_ERROR) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); ERROR("%s", x); } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) { char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/); // TRACE_MSG("Trying to execute [%s]", x); pid_t l_pid; switch(l_pid = fork()) { case -1: // error when forking ! ERROR_NO("Forking error main"); break; case 0: // first child process - execute the command if((l_pid = fork()) < 0) { ERROR_NO("Forking error child"); } else { if( l_pid == 0) { int ret; ret = system(x); // second child runs if(ret == -1) { WARNING("system call error for %s", x); } } exit(EXIT_OTHER); } break; default: // parent process continue // reap first child immediately pid_t ret; while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) { if (ret != -1) { ERROR("waitpid returns %1ld for child %1ld", (long) ret, (long) l_pid); } } break; } } else if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) { switch (currentAction->getIntCmd()) { case CAction::E_INTCMD_STOP_ALL: quitting = 1; break; case CAction::E_INTCMD_STOP_NOW: sipp_exit(EXIT_TEST_RES_INTERNAL); break; case CAction::E_INTCMD_STOPCALL: default: return(call::E_AR_STOP_CALL); break; } #ifdef PCAPPLAY } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) || (currentAction->getActionType() == CAction::E_AT_PLAY_DTMF)) { play_args_t* play_args = 0; if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || (currentAction->getActionType() == CAction::E_AT_PLAY_DTMF)) { play_args = &(this->play_args_a); } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) { play_args = &(this->play_args_i); } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) { play_args = &(this->play_args_v); } else { ERROR("Can't find pcap data to play"); } // existing media thread could be using play_args, so we have to kill it before modifying parameters if (media_thread != 0) { // If a media_thread is already active, kill it before starting a new one pthread_cancel(media_thread); pthread_join(media_thread, NULL); media_thread = 0; } if (currentAction->getActionType() == CAction::E_AT_PLAY_DTMF) { char* digits = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf */); play_args->pcap = (pcap_pkts *) malloc(sizeof(pcap_pkts)); play_args->last_seq_no += parse_dtmf_play_args(digits, play_args->pcap, play_args->last_seq_no); play_args->free_pcap_when_done = 1; } else { play_args->pcap = currentAction->getPcapPkts(); play_args->free_pcap_when_done = 0; } /* port number is set in [auto_]media_port interpolation */ if (media_ip_is_ipv6) { struct sockaddr_in6* from = (struct sockaddr_in6*) &(play_args->from); from->sin6_family = AF_INET6; inet_pton(AF_INET6, media_ip, &(from->sin6_addr)); } else { struct sockaddr_in* from = (struct sockaddr_in*) &(play_args->from); from->sin_family = AF_INET; from->sin_addr.s_addr = inet_addr(media_ip); } /* Create a thread to send RTP or UDPTL packets */ pthread_attr_t attr; pthread_attr_init(&attr); #ifndef PTHREAD_STACK_MIN #define PTHREAD_STACK_MIN 16384 #endif int ret = pthread_create(&media_thread, &attr, send_wrapper, play_args); if (ret) { ERROR("Can't create thread to send RTP packets"); } pthread_attr_destroy(&attr); #endif #ifdef RTP_STREAM } else if (currentAction->getActionType() == CAction::E_AT_RTP_ECHO) { rtp_echo_state = (currentAction->getDoubleValue() != 0); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PAUSE) { rtpstream_pause(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_RESUME) { rtpstream_resume(&rtpstream_callinfo); } else if (currentAction->getActionType() == CAction::E_AT_RTP_STREAM_PLAY) { rtpstream_play(&rtpstream_callinfo, currentAction->getRTPStreamActInfo()); #endif } else { ERROR("call::executeAction unknown action"); } } // end for return(call::E_AR_NO_ERROR); } void call::extractSubMessage(const char* msg, char* matchingString, char* result, bool case_indep, int occurrence, bool headers) { const char *ptr, *ptr1; int sizeOf; int i = 0; int len = strlen(matchingString); char mat1 = tolower(*matchingString); char mat2 = toupper(*matchingString); ptr = msg; while (*ptr) { if (!case_indep) { ptr = strstr(ptr, matchingString); if (ptr == NULL) break; if (headers == true && ptr != msg && *(ptr-1) != '\n') { ++ptr; continue; } } else { if (headers) { if (ptr != msg) { ptr = strchr(ptr, '\n'); if (ptr == NULL) break; ++ptr; if (*ptr == 0) break; } } else { ptr1 = strchr(ptr, mat1); ptr = strchr(ptr, mat2); if (ptr == NULL) { if (ptr1 == NULL) break; ptr = ptr1; } else { if (ptr1 != NULL && ptr1 < ptr) ptr = ptr1; } } if (strncasecmp(ptr, matchingString, len) != 0) { ++ptr; continue; } } // here with ptr pointing to a matching string if (occurrence <= 1) break; --occurrence; ++ptr; } if(ptr != NULL && *ptr != 0) { strncpy(result, ptr+len, MAX_SUB_MESSAGE_LENGTH); sizeOf = strlen(result); if(sizeOf >= MAX_SUB_MESSAGE_LENGTH) sizeOf = MAX_SUB_MESSAGE_LENGTH-1; while((i inFiles[fileName]->numLines()) { line = -1; } } if (line < 0) { return; } dest += inFiles[fileName]->getField(line, field, dest, SIPP_MAX_MSG_SIZE); } call::T_AutoMode call::checkAutomaticResponseMode(char* P_recv) { if (strcmp(P_recv, "BYE")==0) { return E_AM_UNEXP_BYE; } else if (strcmp(P_recv, "CANCEL") == 0) { return E_AM_UNEXP_CANCEL; } else if (strcmp(P_recv, "PING") == 0) { return E_AM_PING; } else if (auto_answer && ((strcmp(P_recv, "INFO") == 0) || (strcmp(P_recv, "NOTIFY") == 0) || (strcmp(P_recv, "OPTIONS") == 0) || (strcmp(P_recv, "UPDATE") == 0))) { return E_AM_AA; } else { return E_AM_DEFAULT; } } void call::setLastMsg(const char *msg) { realloc_ptr = (char *) realloc(last_recv_msg, strlen(msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return; } strcpy(last_recv_msg, msg); } bool call::automaticResponseMode(T_AutoMode P_case, const char* P_recv) { int res ; char * old_last_recv_msg = NULL; bool last_recv_msg_saved = false; switch (P_case) { case E_AM_UNEXP_BYE: // response for an unexpected BYE // usage of last_ keywords realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); // The BYE is unexpected, count it call_scenario->messages[msg_index] -> nb_unexp++; if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { WARNING("Aborting call on an unexpected BYE for call: %s", (id==NULL)?"none":id); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { sendBuffer(createSendingMessage(get_default_message("200"), -1)); } // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1)); if (res) { WARNING("sendCmdBuffer returned %d", res); return false; } } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_UNEXPECTED_MSG); delete this; } else { WARNING("Continuing call on an unexpected BYE for call: %s", (id==NULL)?"none":id); } break ; case E_AM_UNEXP_CANCEL: // response for an unexpected cancel // usage of last_ keywords realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); // The CANCEL is unexpected, count it call_scenario->messages[msg_index] -> nb_unexp++; if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) { WARNING("Aborting call on an unexpected CANCEL for call: %s", (id==NULL)?"none":id); if (default_behaviors & DEFAULT_BEHAVIOR_BYE) { sendBuffer(createSendingMessage(get_default_message("200"), -1)); } // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1)); if (res) { WARNING("sendCmdBuffer returned %d", res); return false; } } computeStat(CStat::E_CALL_FAILED); computeStat(CStat::E_FAILED_UNEXPECTED_MSG); delete this; } else { WARNING("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id); } break ; case E_AM_PING: // response for a random ping // usage of last_ keywords realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); if (default_behaviors & DEFAULT_BEHAVIOR_PINGREPLY) { WARNING("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id); sendBuffer(createSendingMessage(get_default_message("200"), -1)); // Note: the call ends here but it is not marked as bad. PING is a // normal message. // if twin socket call => reset the other part here if (twinSippSocket && (msg_index > 0)) { res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1)); if (res) { WARNING("sendCmdBuffer returned %d", res); return false; } } CStat::globalStat(CStat::E_AUTO_ANSWERED); delete this; } else { WARNING("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id); } break ; case E_AM_AA: // response for a random INFO, NOTIFY, OPTIONS or UPDATE // store previous last msg if msg is INFO, NOTIFY, OPTIONS or UPDATE // restore last_recv_msg to previous one // after sending ok old_last_recv_msg = NULL; if (last_recv_msg != NULL) { last_recv_msg_saved = true; old_last_recv_msg = (char *) malloc(strlen(last_recv_msg)+1); strcpy(old_last_recv_msg, last_recv_msg); } // usage of last_ keywords realloc_ptr = (char *) realloc(last_recv_msg, strlen(P_recv) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); free(old_last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, P_recv); WARNING("Automatic response mode for an unexpected INFO, NOTIFY, OPTIONS or UPDATE for call: %s", (id == NULL) ? "none" : id); sendBuffer(createSendingMessage(get_default_message("200"), -1)); // restore previous last msg if (last_recv_msg_saved == true) { realloc_ptr = (char *) realloc(last_recv_msg, strlen(old_last_recv_msg) + 1); if (realloc_ptr) { last_recv_msg = realloc_ptr; } else { free(last_recv_msg); ERROR("Out of memory!"); return false; } strcpy(last_recv_msg, old_last_recv_msg); if (old_last_recv_msg != NULL) { free(old_last_recv_msg); old_last_recv_msg = NULL; } } CStat::globalStat(CStat::E_AUTO_ANSWERED); return true; break; default: ERROR("Internal error for automaticResponseMode - mode %d is not implemented!", P_case); break ; } return false; } #ifdef PCAPPLAY void *send_wrapper(void *arg) { play_args_t *s = (play_args_t *) arg; //struct sched_param param; //int ret; //param.sched_priority = 10; //ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); //if(ret) // ERROR("Can't set RTP play thread realtime parameters"); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); send_packets(s); pthread_exit(NULL); return NULL; } #endif #ifdef GTEST #include "gtest/gtest.h" #include "gtest/gtest.h" class mockcall : public call { public: mockcall(bool is_ipv6) : listener("//testing", true), call("///testing", is_ipv6, 0, NULL) {} /* Helpers to poke at protected internals */ void parse_media_addr(std::string const& msg) { get_remote_media_addr(msg); } #ifdef PCAPPLAY bool has_media() { return hasMediaInformation; } template T get_audio_addr() { T sa; std::memcpy(&sa, &play_args_a.to, sizeof(T)); return sa; } #endif }; bool operator==(const struct sockaddr_in& a, const struct sockaddr_in &b) { return a.sin_family == b.sin_family && a.sin_port == b.sin_port && std::memcmp(&a.sin_addr, &b.sin_addr, sizeof(in_addr)) == 0; } bool operator==(const struct sockaddr_in6& a, const struct sockaddr_in6 &b) { return a.sin6_family == b.sin6_family && a.sin6_port == b.sin6_port && std::memcmp(&a.sin6_addr, &b.sin6_addr, sizeof(in_addr)) == 0; } const std::string test_sdp_v4 = "v=0\r\n" "o=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n" "s=-\r\n" "c=IN IP4 127.0.0.1\r\n" "t=0 0\r\n" "m=audio 12345 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n"; const std::string test_sdp_v6 = "v=0\r\n" "o=user1 53655765 2353687637 IN IP6 ::1\r\n" "s=-\r\n" "c=IN IP6 ::1\r\n" "t=0 0\r\n" "m=audio 12345 RTP/AVP 0\r\n" "a=rtpmap:0 PCMU/8000\r\n"; TEST(sdp, parse_valid_sdp_msg) { ASSERT_EQ(find_in_sdp("c=IN IP4 ", test_sdp_v4), "127.0.0.1"); ASSERT_EQ(find_in_sdp("c=IN IP6 ", test_sdp_v6), "::1"); ASSERT_EQ(find_in_sdp("m=audio ", test_sdp_v4), "12345"); ASSERT_EQ(find_in_sdp("m=audio ", test_sdp_v6), "12345"); } TEST(sdp, parse_invalid_sdp_msg) { ASSERT_EQ(find_in_sdp("c=IN IP4 ", test_sdp_v6), ""); ASSERT_EQ(find_in_sdp("c=IN IP6 ", test_sdp_v4), ""); ASSERT_EQ(find_in_sdp("m=video ", test_sdp_v6), ""); ASSERT_EQ(find_in_sdp("m=video ", test_sdp_v4), ""); } #ifdef PCAPPLAY TEST(sdp, good_remote_media_addr_v4) { media_ip_is_ipv6 = false; struct sockaddr_in reference; reference.sin_family = AF_INET; reference.sin_port = htons(12345); inet_pton(AF_INET, "127.0.0.1", &reference.sin_addr); mockcall call(false); call.parse_media_addr(test_sdp_v4); ASSERT_EQ(call.has_media(), true); ASSERT_EQ(reference, call.get_audio_addr()); } TEST(sdp, good_remote_media_addr_v6) { media_ip_is_ipv6 = true; struct sockaddr_in6 reference; reference.sin6_family = AF_INET6; reference.sin6_port = htons(12345); inet_pton(AF_INET6, "::1", &reference.sin6_addr); mockcall call(true); call.parse_media_addr(test_sdp_v6); ASSERT_EQ(call.has_media(), true); ASSERT_EQ(reference, call.get_audio_addr()); } #endif /* PCAP_PLAY */ #endif sipp-3.6.1/src/watchdog.cpp0000664000175000017500000001162513730472040015154 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" void watchdog::dump() { WARNING("Watchdog Task: interval = %d, major_threshold = %d (%d triggers left), minor_threshold = %d (%d triggers left)", interval, major_threshold, major_maxtriggers, minor_threshold, minor_maxtriggers); } watchdog::watchdog(int interval, int reset_interval, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers) { this->interval = interval; this->reset_interval = reset_interval; this->major_threshold = major_threshold; this->major_maxtriggers = major_maxtriggers; this->minor_threshold = minor_threshold; this->minor_maxtriggers = minor_maxtriggers; major_triggers = 0; minor_triggers = 0; last_trigger = last_fire = getmilliseconds(); } bool watchdog::run() { getmilliseconds(); unsigned expected_major_trigger_time = last_fire + this->major_threshold; unsigned expected_minor_trigger_time = last_fire + this->minor_threshold; bool major_watchdog_tripped = clock_tick > expected_major_trigger_time; bool minor_watchdog_tripped = clock_tick > expected_minor_trigger_time; // Check if either watchdog has taken longer than expected to run, // and if so, warn that we are overloaded. if (major_watchdog_tripped) { major_triggers++; CStat::globalStat(CStat::E_WATCHDOG_MAJOR); last_trigger = clock_tick; WARNING("Overload warning: the major watchdog timer %dms has been tripped (%lu), %d trips remaining.", major_threshold, clock_tick - last_fire, major_maxtriggers - major_triggers); } else if (minor_watchdog_tripped) { minor_triggers++; last_trigger = clock_tick; CStat::globalStat(CStat::E_WATCHDOG_MINOR); WARNING("Overload warning: the minor watchdog timer %dms has been tripped (%lu), %d trips remaining.", minor_threshold, clock_tick - last_fire, minor_maxtriggers - minor_triggers); } bool major_watchdog_failure = ((this->major_maxtriggers != -1) && (major_triggers > this->major_maxtriggers)); bool minor_watchdog_failure = ((this->minor_maxtriggers != -1) && (minor_triggers > this->minor_maxtriggers)); // If the watchdogs have tripped too many times, end the SIPp run. if (major_watchdog_failure) { ERROR("Overload error: the watchdog timer has tripped the major threshold of %dms too many times (%d out of %d allowed) (%d out of %d minor %dms timeouts tripped)", major_threshold, major_triggers, major_maxtriggers, minor_triggers, minor_maxtriggers, minor_threshold); } else if (minor_watchdog_failure) { ERROR("Overload error: the watchdog timer has tripped the minor threshold of %dms too many times (%d out of %d allowed) (%d out of %d major %dms timeouts tripped)", minor_threshold, minor_triggers, minor_maxtriggers, major_triggers, major_maxtriggers, major_threshold); } if ((reset_interval > 0) && (major_triggers || minor_triggers) && (clock_tick > (last_trigger + reset_interval))) { WARNING("Resetting watchdog timer trigger counts, as it has not been triggered in over %lums.", clock_tick - last_trigger); major_triggers = minor_triggers = 0; } last_fire = clock_tick; setPaused(); // Return this task to a paused state return true; } // Returns the clock_tick when this task should next run unsigned int watchdog::wake() { return last_fire + interval; } sipp-3.6.1/src/rtpstream.cpp0000664000175000017500000007116013730472040015375 0ustar walterwalter/* * 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 * * Author : Deon van der Westhuysen - June 2012 - Vodacom PTY LTD */ #include #include #include #include #include "sipp.hpp" #include #include #include #include #include #include "rtpstream.hpp" /* stub to add extra debugging/logging... */ static void debugprint(const char* format, ...) { } #define RTPSTREAM_FILESPERBLOCK 16 #define BIND_MAX_TRIES 100 #define RTPSTREAM_THREADBLOCKSIZE 16 #define MAX_UDP_RECV_BUFFER 8192 #define TI_NULL_AUDIOIP 0x01 #define TI_NULLIP (TI_NULL_AUDIOIP) #define TI_PAUSERTP 0x04 #define TI_ECHORTP 0x08 /* Not currently implemented */ #define TI_KILLTASK 0x10 #define TI_PLAYFILE 0x40 #define TI_CONFIGFLAGS (TI_KILLTASK|TI_PLAYFILE) struct rtp_header_t { uint16_t flags; uint16_t seq; uint32_t timestamp; uint32_t ssrc_id; }; struct taskentry_t { threaddata_t *parent_thread; unsigned long nextwake_ms; volatile int flags; /* rtp stream information */ unsigned long long last_timestamp; unsigned short seq; char payload_type; unsigned int ssrc_id; /* current playback information */ int loop_count; char *file_bytes_start; char *current_file_bytes; int file_num_bytes; int file_bytes_left; /* playback timing information */ int ms_per_packet; int bytes_per_packet; int timeticks_per_packet; int timeticks_per_ms; /* new file playback information */ char new_payload_type; int new_loop_count; int new_file_size; char *new_file_bytes; int new_ms_per_packet; int new_bytes_per_packet; int new_timeticks_per_packet; /* sockets for audio/video rtp_rtcp */ int audio_rtp_socket; /* rtp peer address structures */ struct sockaddr_storage remote_audio_rtp_addr; /* we will have a mutex per call. should we consider refactoring to */ /* share mutexes across calls? makes the per-call code more complex */ /* thread mananagment structures */ pthread_mutex_t mutex; }; struct threaddata_t { pthread_mutex_t tasklist_mutex; int busy_list_index; int max_tasks; volatile int num_tasks; volatile int del_pending; volatile int exit_flag; taskentry_t *tasklist; }; struct cached_file_t { char filename[RTPSTREAM_MAX_FILENAMELEN]; char *bytes; int filesize; }; cached_file_t *cached_files = NULL; int num_cached_files = 0; int next_rtp_port = 0; threaddata_t **ready_threads = NULL; threaddata_t **busy_threads = NULL; int num_busy_threads = 0; int num_ready_threads = 0; int busy_threads_max = 0; int ready_threads_max = 0; unsigned int global_ssrc_id = 0xCA110000; //=================================================================================================== /* code checked */ static void rtpstream_free_taskinfo(taskentry_t* taskinfo) { if (taskinfo) { /* cleanup pthread library structure */ pthread_mutex_destroy(&(taskinfo->mutex)); free(taskinfo); } } /* code checked */ static void rtpstream_process_task_flags(taskentry_t* taskinfo) { if (taskinfo->flags & TI_PLAYFILE) { /* copy playback information */ taskinfo->loop_count = taskinfo->new_loop_count; taskinfo->file_bytes_start = taskinfo->new_file_bytes; taskinfo->current_file_bytes = taskinfo->new_file_bytes; taskinfo->file_num_bytes = taskinfo->new_file_size; taskinfo->file_bytes_left = taskinfo->new_file_size; taskinfo->payload_type = taskinfo->new_payload_type; taskinfo->ms_per_packet = taskinfo->new_ms_per_packet; taskinfo->bytes_per_packet = taskinfo->new_bytes_per_packet; taskinfo->timeticks_per_packet = taskinfo->new_timeticks_per_packet; taskinfo->timeticks_per_ms = taskinfo->timeticks_per_packet/taskinfo->ms_per_packet; taskinfo->last_timestamp = getmilliseconds()*taskinfo->timeticks_per_ms; taskinfo->flags &= ~TI_PLAYFILE; } } /**** todo - check code ****/ static unsigned long rtpstream_playrtptask(taskentry_t* taskinfo, unsigned long timenow_ms) { int rc; unsigned long next_wake; unsigned long long target_timestamp; union { rtp_header_t hdr; char buffer[MAX_UDP_RECV_BUFFER]; } udp; /* OK, now to play - sockets are supposed to be non-blocking */ /* no support for video stream at this stage. will need some work */ next_wake = timenow_ms + 100; /* default next wakeup time */ if (taskinfo->audio_rtp_socket != -1) { /* if/when we include echo functionality, we'll have to read * from the audio_rtp_socket too, and check by peer address if * it is "our" traffic */ /* are we playing back an audio file? */ if (taskinfo->loop_count) { target_timestamp = timenow_ms * taskinfo->timeticks_per_ms; next_wake = timenow_ms + taskinfo->ms_per_packet - timenow_ms % taskinfo->ms_per_packet; if (taskinfo->flags & (TI_NULL_AUDIOIP | TI_PAUSERTP)) { /* when paused, set timestamp so stream appears to be up to date */ taskinfo->last_timestamp = target_timestamp; } if (taskinfo->last_timestamp < target_timestamp) { /* need to send rtp payload - build rtp packet header... */ udp.hdr.flags = htons(0x8000 | taskinfo->payload_type); udp.hdr.seq = htons(taskinfo->seq); udp.hdr.timestamp = htonl((uint32_t)(taskinfo->last_timestamp & 0xFFFFFFFF)); udp.hdr.ssrc_id = htonl(taskinfo->ssrc_id); /* add payload data to the packet - handle buffer wraparound */ if (taskinfo->file_bytes_left >= taskinfo->bytes_per_packet) { /* no need for fancy acrobatics */ memcpy(udp.buffer + sizeof(rtp_header_t), taskinfo->current_file_bytes, taskinfo->bytes_per_packet); } else { /* copy from end and then begining of file. does not handle the */ /* case where file is shorter than the packet length!! */ memcpy(udp.buffer + sizeof(rtp_header_t), taskinfo->current_file_bytes, taskinfo->file_bytes_left); memcpy(udp.buffer + sizeof(rtp_header_t) + taskinfo->file_bytes_left, taskinfo->file_bytes_start, taskinfo->bytes_per_packet - taskinfo->file_bytes_left); } /* now send the actual packet */ size_t packet_len = taskinfo->bytes_per_packet + sizeof(rtp_header_t); socklen_t remote_addr_len = (media_ip_is_ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); rc = sendto(taskinfo->audio_rtp_socket, udp.buffer, packet_len, 0, (struct sockaddr*)&taskinfo->remote_audio_rtp_addr, remote_addr_len); if (rc < 0) { /* handle sending errors */ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { next_wake = timenow_ms + 2; /* retry after short sleep */ } else { /* this looks like a permanent error - should we ignore ENETUNREACH? */ debugprint("closing rtp socket %d due to error %d in rtpstream_new_call taskinfo=%p\n", taskinfo->audio_rtp_socket, errno, taskinfo); close(taskinfo->audio_rtp_socket); taskinfo->audio_rtp_socket = -1; } } else { /* statistics - only count successful sends */ rtpstream_bytes_out += taskinfo->bytes_per_packet + sizeof(rtp_header_t); rtpstream_pckts++; /* advance playback pointer to next packet */ taskinfo->seq++; /* must change if timer ticks per packet can be fractional */ taskinfo->last_timestamp += taskinfo->timeticks_per_packet; taskinfo->file_bytes_left -= taskinfo->bytes_per_packet; if (taskinfo->file_bytes_left > 0) { taskinfo->current_file_bytes += taskinfo->bytes_per_packet; } else { taskinfo->current_file_bytes = taskinfo->file_bytes_start - taskinfo->file_bytes_left; taskinfo->file_bytes_left += taskinfo->file_num_bytes; if (taskinfo->loop_count > 0) { /* one less loop to play. -1 (infinite loops) will stay as is */ taskinfo->loop_count--; } } if (taskinfo->last_timestamp < target_timestamp) { /* no sleep if we are behind */ next_wake = timenow_ms; } } } } else { /* not busy playing back a file - put possible rtp echo code here. */ } } return next_wake; } /*********************************************************************************/ /*********************************************************************************/ /*********************************************************************************/ /* code checked */ static void* rtpstream_playback_thread(void* params) { threaddata_t *threaddata = (threaddata_t *) params; taskentry_t *taskinfo; int taskindex; unsigned long timenow_ms; unsigned long waketime_ms; int sleeptime_us; rtpstream_numthreads++; /* perhaps wrap this in a mutex? */ while (!threaddata->exit_flag) { timenow_ms = getmilliseconds(); waketime_ms = timenow_ms + 100; /* default sleep 100ms */ /* iterate through tasks and handle playback and other actions */ for (taskindex = 0; taskindex < threaddata->num_tasks; taskindex++) { taskinfo = (&threaddata->tasklist)[taskindex]; if (taskinfo->flags & TI_CONFIGFLAGS) { if (taskinfo->flags & TI_KILLTASK) { /* remove this task entry and release its resources */ pthread_mutex_lock (&(threaddata->tasklist_mutex)); (&threaddata->tasklist)[taskindex--] = (&threaddata->tasklist)[--threaddata->num_tasks]; threaddata->del_pending--; /* must decrease del_pending after num_tasks */ pthread_mutex_unlock(&(threaddata->tasklist_mutex)); rtpstream_free_taskinfo(taskinfo); continue; } /* handle any other config related flags */ rtpstream_process_task_flags(taskinfo); } /* should we update current time inbetween tasks? */ if (taskinfo->nextwake_ms <= timenow_ms) { /* task needs to execute now */ taskinfo->nextwake_ms = rtpstream_playrtptask(taskinfo, timenow_ms); } if (waketime_ms > taskinfo->nextwake_ms) { waketime_ms = taskinfo->nextwake_ms; } } /* sleep until next iteration of playback loop */ sleeptime_us = (waketime_ms - getmilliseconds()) * 1000; if (sleeptime_us > 0) { usleep(sleeptime_us); } } /* Free all task and thread resources and exit the thread */ for (taskindex = 0; taskindex < threaddata->num_tasks; taskindex++) { /* check if we should delete this thread, else let owner call clear it */ /* small chance of race condition in this code */ taskinfo = (&threaddata->tasklist)[taskindex]; if (taskinfo->flags & TI_KILLTASK) { rtpstream_free_taskinfo(taskinfo); } else { taskinfo->parent_thread = NULL; /* no longer associated with a thread */ } } pthread_mutex_destroy(&(threaddata->tasklist_mutex)); free(threaddata); rtpstream_numthreads--; /* perhaps wrap this in a mutex? */ return NULL; } /* code checked */ static int rtpstream_start_task(rtpstream_callinfo_t* callinfo) { int ready_index; int allocsize; threaddata_t **threadlist; threaddata_t *threaddata; pthread_t newthread; /* safety check... */ if (!callinfo->taskinfo) { return 0; } /* we count on the fact that only one thread can add/remove playback tasks */ /* thus we don't have mutexes to protect the thread list objects. */ for (ready_index = 0; ready_index < num_ready_threads; ready_index++) { /* ready threads have a spare task slot or should have one very shortly */ /* if we find a task with no spare slots, just skip to the next one. */ if (ready_threads[ready_index]->num_tasks < ready_threads[ready_index]->max_tasks) { /* we found a thread with an open task slot. */ break; } } if (ready_index == num_ready_threads) { /* did not find a thread with spare task slots, thus we create one here */ if (num_ready_threads >= ready_threads_max) { /* need to allocate more memory for thread list */ ready_threads_max += RTPSTREAM_THREADBLOCKSIZE; threadlist = (threaddata_t **) realloc(ready_threads, sizeof(*ready_threads) * ready_threads_max); if (!threadlist) { /* could not allocate bigger block... worry [about it later] */ ready_threads_max -= RTPSTREAM_THREADBLOCKSIZE; return 0; } ready_threads = threadlist; } /* create and initialise data structure for new thread */ allocsize = sizeof(*threaddata) + sizeof(threaddata->tasklist) * (rtp_tasks_per_thread - 1); threaddata = (threaddata_t *) malloc(allocsize); if (!threaddata) { return 0; } memset(threaddata, 0, allocsize); threaddata->max_tasks = rtp_tasks_per_thread; threaddata->busy_list_index = -1; pthread_mutex_init(&(threaddata->tasklist_mutex), NULL); /* create the thread itself */ if (pthread_create(&newthread, NULL, rtpstream_playback_thread, threaddata)) { /* error creating the thread */ free(threaddata); return 0; } /* Add thread to list of ready (spare capacity) threads */ ready_threads[num_ready_threads++] = threaddata; } /* now add new task to a spare slot in our thread tasklist */ threaddata = ready_threads[ready_index]; callinfo->taskinfo->parent_thread = threaddata; pthread_mutex_lock(&(threaddata->tasklist_mutex)); (&threaddata->tasklist)[threaddata->num_tasks++] = callinfo->taskinfo; pthread_mutex_unlock(&(threaddata->tasklist_mutex)); /* this check relies on playback thread to decrement num_tasks before */ /* decrementing del_pending -- else we need to lock before this test */ if ((threaddata->del_pending == 0) && (threaddata->num_tasks >= threaddata->max_tasks)) { /* move this thread to the busy list - no free task slots */ /* first check if the busy list is big enough to hold new thread */ if (num_busy_threads >= busy_threads_max) { /* need to allocate more memory for thread list */ busy_threads_max += RTPSTREAM_THREADBLOCKSIZE; threadlist = (threaddata_t **) realloc(busy_threads, sizeof(*busy_threads) * busy_threads_max); if (!threadlist) { /* could not allocate bigger block... leave thread in ready list */ busy_threads_max -= RTPSTREAM_THREADBLOCKSIZE; return 1; /* success, sort of */ } busy_threads = threadlist; } /* add to busy list */ threaddata->busy_list_index = num_busy_threads; busy_threads[num_busy_threads++] = threaddata; /* remove from ready list */ ready_threads[ready_index] = ready_threads[--num_ready_threads]; } return 1; /* done! */ } /* code checked */ static void rtpstream_stop_task(rtpstream_callinfo_t* callinfo) { threaddata_t **threadlist; taskentry_t *taskinfo = callinfo->taskinfo; int busy_index; if (taskinfo) { if (taskinfo->parent_thread) { /* this call's task is registered with an executing thread */ /* first move owning thread to the ready list - will be ready soon */ busy_index = taskinfo->parent_thread->busy_list_index; if (busy_index >= 0) { /* make sure we have enough entries in ready list */ if (num_ready_threads >= ready_threads_max) { /* need to allocate more memory for thread list */ ready_threads_max += RTPSTREAM_THREADBLOCKSIZE; threadlist = (threaddata_t **) realloc(ready_threads, sizeof(*ready_threads) * ready_threads_max); if (!threadlist) { /* could not allocate bigger block... reset max threads */ /* this is a problem - ready thread gets "lost" on busy list */ ready_threads_max -= RTPSTREAM_THREADBLOCKSIZE; } else { ready_threads = threadlist; } } if (num_ready_threads < ready_threads_max) { /* OK, got space on ready list, move to ready list */ busy_threads[busy_index]->busy_list_index = -1; ready_threads[num_ready_threads++] = busy_threads[busy_index]; num_busy_threads--; /* fill up gap in the busy thread list */ if (busy_index != num_busy_threads) { busy_threads[busy_index] = busy_threads[num_busy_threads]; busy_threads[busy_index]->busy_list_index = busy_index; } } } /* then ask the thread to destory this task (and its memory) */ pthread_mutex_lock(&(taskinfo->parent_thread->tasklist_mutex)); taskinfo->parent_thread->del_pending++; taskinfo->flags |= TI_KILLTASK; pthread_mutex_unlock(&(taskinfo->parent_thread->tasklist_mutex)); } else { /* no playback thread owner, just free it */ rtpstream_free_taskinfo(taskinfo); } callinfo->taskinfo = NULL; } } /* code checked */ int rtpstream_new_call(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_new_call callinfo=%p\n", callinfo); taskentry_t* taskinfo; /* general init */ memset(callinfo, 0, sizeof(*callinfo)); taskinfo= (taskentry_t *) malloc(sizeof(*taskinfo)); if (!taskinfo) { /* cannot allocate taskinfo memory - bubble error up */ return 0; } callinfo->taskinfo = taskinfo; memset(taskinfo, 0, sizeof(*taskinfo)); taskinfo->flags = TI_NULLIP; /* socket descriptors */ taskinfo->audio_rtp_socket = -1; /* rtp stream members */ taskinfo->ssrc_id = global_ssrc_id++; /* pthread mutexes */ pthread_mutex_init(&(callinfo->taskinfo->mutex), NULL); return 1; } /* code checked */ void rtpstream_end_call(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_end_call callinfo=%p\n", callinfo); /* stop playback thread(s) for this call */ rtpstream_stop_task(callinfo); } /* code checked */ int rtpstream_cache_file(char* filename) { int count = 0; cached_file_t *newcachelist; char *filecontents; struct stat statbuffer; FILE *f; debugprint ("rtpstream_cache_file filename=%s\n", filename); /* cached file entries are stored in a dynamically grown array. */ /* could use a binary (or avl) tree but number of files should */ /* be small and doesn't really justify the effort. */ while (count < num_cached_files) { if (!strcmp(cached_files[count].filename, filename)) { /* found the file already loaded. just return index */ return count; } count++; } /* Allocate memory and load file */ if (stat(filename, &statbuffer)) { /* could not get file information */ return -1; } f = fopen(filename, "rb"); if (!f) { /* could not open file */ return -1; } filecontents = (char *)malloc(statbuffer.st_size); if (!filecontents) { /* could not alloc mem */ return -1; } if (!fread(filecontents, statbuffer.st_size, 1, f)) { /* could not read file */ free(filecontents); return -1; } fclose(f); if (!(num_cached_files % RTPSTREAM_FILESPERBLOCK)) { /* Time to allocate more memory for the next block of files */ newcachelist = (cached_file_t*) realloc(cached_files, sizeof(*cached_files) * (num_cached_files + RTPSTREAM_FILESPERBLOCK)); if (!newcachelist) { /* out of memory */ free(filecontents); return -1; } cached_files = newcachelist; } cached_files[num_cached_files].bytes = filecontents; strncpy(cached_files[num_cached_files].filename, filename, sizeof(cached_files[num_cached_files].filename) - 1); cached_files[num_cached_files].filesize = statbuffer.st_size; return num_cached_files++; } /* code checked */ void rtpstream_set_remote(rtpstream_callinfo_t* callinfo, int ip_ver, const char* ip_addr, int audio_port, int video_port) { struct sockaddr_storage address; struct in_addr *ip4_addr; struct in6_addr *ip6_addr; taskentry_t *taskinfo; unsigned count; int nonzero_ip; /* observe that we rely on ip_ver being in sync with media_ip_is_ipv6 */ /* we never alloc a socket here, we reuse the global media socket */ debugprint("rtpstream_set_remote callinfo=%p, ip_ver %d ip_addr %s audio %d video %d\n", callinfo, ip_ver, ip_addr, audio_port, video_port); taskinfo = callinfo->taskinfo; if (!taskinfo) { /* no task info found - cannot set remote data. just return */ return; } nonzero_ip = 0; taskinfo->flags |= TI_NULLIP; /// TODO: this (may) cause a gap in playback, if playback thread gets to exec while this is set and before new IP is checked. /* test that media ip address version match remote ip address version? */ /* initialise address family and IP address for remote socket */ memset(&address, 0, sizeof(address)); if (media_ip_is_ipv6) { /* process ipv6 address */ address.ss_family = AF_INET6; ip6_addr = &((_RCAST(struct sockaddr_in6 *, &address))->sin6_addr); if (inet_pton(AF_INET6, ip_addr, ip6_addr) == 1) { for (count = 0; count < sizeof(*ip6_addr); count++) { if (((char*)ip6_addr)[count]) { nonzero_ip = 1; break; } } } } else { /* process ipv4 address */ address.ss_family = AF_INET; ip4_addr = &((_RCAST(struct sockaddr_in *, &address))->sin_addr); if (inet_pton(AF_INET, ip_addr, ip4_addr) == 1) { for (count = 0; count < sizeof(*ip4_addr); count++) { if (((char*)ip4_addr)[count]) { nonzero_ip = 1; break; } } } } if (!nonzero_ip) { return; } /* enter critical section to lock address updates */ /* may want to leave this out -- low chance of race condition */ pthread_mutex_lock(&(taskinfo->mutex)); /* clear out existing addresses */ memset(&(taskinfo->remote_audio_rtp_addr), 0, sizeof(taskinfo->remote_audio_rtp_addr)); /* Audio */ if (audio_port) { sockaddr_update_port(&address, audio_port); memcpy(&(taskinfo->remote_audio_rtp_addr), &address, sizeof(address)); taskinfo->flags &= ~TI_NULL_AUDIOIP; } /* ok, we are done with the shared memory objects. let go mutex */ pthread_mutex_unlock (&(taskinfo->mutex)); /* may want to start a playback (listen) task here if no task running? */ /* only makes sense if we decide to send 0-filled packets on idle */ } /* code checked */ void rtpstream_play(rtpstream_callinfo_t* callinfo, rtpstream_actinfo_t* actioninfo) { debugprint("rtpstream_play callinfo=%p filename %s loop %d bytes %d payload %d ptime %d tick %d\n", callinfo, actioninfo->filename, actioninfo->loop_count, actioninfo->bytes_per_packet, actioninfo->payload_type, actioninfo->ms_per_packet, actioninfo->ticks_per_packet); int file_index = rtpstream_cache_file(actioninfo->filename); taskentry_t *taskinfo = callinfo->taskinfo; if (file_index < 0) { return; /* cannot find file to play */ } if (!taskinfo) { return; /* no task data structure */ } /* make sure we have an open socket from which to play the audio file */ taskinfo->audio_rtp_socket = media_socket_audio; /* start playback task if not already started */ if (!taskinfo->parent_thread) { if (!rtpstream_start_task(callinfo)) { /* error starting playback task */ return; } } /* save file parameter in taskinfo structure */ taskinfo->new_loop_count = actioninfo->loop_count; taskinfo->new_bytes_per_packet = actioninfo->bytes_per_packet; taskinfo->new_file_size = cached_files[file_index].filesize; taskinfo->new_file_bytes = cached_files[file_index].bytes; taskinfo->new_ms_per_packet = actioninfo->ms_per_packet; taskinfo->new_timeticks_per_packet = actioninfo->ticks_per_packet; taskinfo->new_payload_type = actioninfo->payload_type; /* set flag that we have a new file to play */ taskinfo->flags |= TI_PLAYFILE; } /* code checked */ void rtpstream_pause(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_pause callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags |= TI_PAUSERTP; } } /* code checked */ void rtpstream_resume(rtpstream_callinfo_t* callinfo) { debugprint("rtpstream_resume callinfo=%p\n", callinfo); if (callinfo->taskinfo) { callinfo->taskinfo->flags &= ~TI_PAUSERTP; } } /* code checked */ void rtpstream_shutdown(void) { int count = 0; debugprint("rtpstream_shutdown\n"); /* signal all playback threads that they should exit */ if (ready_threads) { for (count = 0; count < num_ready_threads; count++) { ready_threads[count]->exit_flag = 1; } free(ready_threads); ready_threads = NULL; } if (busy_threads) { for (count = 0; count < num_busy_threads; count++) { busy_threads[count]->exit_flag = 1; } free(busy_threads); busy_threads = NULL; } /* first make sure no playback threads are accessing the file buffers */ /* else small chance the playback thread tries to access freed memory */ while (rtpstream_numthreads) { usleep(50000); } /* now free cached file bytes and structure */ for (count = 0; count < num_cached_files; count++) { free(cached_files[count].bytes); } if (cached_files) { free(cached_files); cached_files = NULL; } } sipp-3.6.1/src/xp_parser_ut.cpp0000664000175000017500000001566713730472040016101 0ustar walterwalter/* * 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 * * Author : Walter Doekes - 24 Sep 2014 */ /* This is a separate file because xp_parser.c is a C file, and GTEST * only works with C++ files. */ #include "xp_parser.h" #ifdef GTEST #include "gtest/gtest.h" TEST(xp_parser, set_xml_buffer_from_string__good) { int res; int i; const char *buffers[] = { ("\r\n" "\r\n" "\r\n" "\r\n" " \r\n" "\r\n"), // 1 ("" "" "" "" " " ""), // 2 ("" "" "" " " ""), // 3 ("" "" " " ""), // 4 NULL }; for (i = 0; buffers[i]; ++i) { const char *elem, *prop; res = xp_set_xml_buffer_from_string(buffers[i]); EXPECT_EQ(i + 1, res * (i + 1)); // res == 1 if (!res) continue; elem = xp_open_element(0); EXPECT_STREQ("scenario", elem); prop = xp_get_value("name"); EXPECT_STREQ("Some Scenario", prop); } } TEST(xp_parser, set_xml_buffer_from_string__bad) { int res; int i; const char *buffers[] = { // No " "" "" " " ""), // -1 // Missing ?> ("" "" " " ""), // -2 // Not even a DOCTYPE. ("" " " ""), // -3 NULL }; for (i = 0; buffers[i]; ++i) { const char *elem, *prop; res = xp_set_xml_buffer_from_string(buffers[i]); EXPECT_EQ(-1 - i, (res - 1) * (i + 1)); // res == 0 if (!res) continue; elem = xp_open_element(0); EXPECT_STREQ("scenario", elem); prop = xp_get_value("name"); EXPECT_STREQ("Some Scenario", prop); } } static void xp_traverse_stack() { const char *elem; int i = 0; while ((elem = xp_open_element(i++))) { xp_traverse_stack(); xp_close_element(); } } TEST(xp_parser, detect_unclosed_xml) { int res; int i; const char *buffers[] = { ("\r\n" "\r\n" "\r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" " \r\n" "\r\n"), /* 0th */ ("\r\n" "\r\n" "\r\n" " \r\n" /* missing slash */ " \r\n" " \r\n" " \r\n" "\r\n"), /* 1st */ ("\r\n" "\r\n" "\r\n" " \r\n" /* with slash */ " \r\n" /* and extra /recv */ " \r\n" " \r\n" " \r\n" "\r\n"), /* 2nd */ NULL }; for (i = 0; buffers[i]; ++i) { const char *elem; res = xp_set_xml_buffer_from_string(buffers[i]); EXPECT_EQ(1, res); elem = xp_open_element(0); EXPECT_STREQ("scenario", elem); xp_traverse_stack(); xp_close_element(); /* scenario */ EXPECT_EQ(!!i, xp_is_invalid()); /* all except 0 are invalid */ } } TEST(xp_unescape, empty) { char buffer[] = ""; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("", dst); } TEST(xp_unescape, noop) { char buffer[] = "no escape sequences"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("no escape sequences", dst); } TEST(xp_unescape, begin_and_end) { char buffer[] = "<xml>this && thatthis && that", dst); } TEST(xp_unescape, single_double_quote) { char buffer[] = """; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("\"", dst); } TEST(xp_unescape, escaped_escape) { char buffer[] = "&amp;"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("&", dst); } TEST(xp_unescape, unclosed_escape) { char buffer[] = "< & &"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("< & &", dst); } TEST(xp_unescape, late_closed_escape) { char buffer[] = "< & & >"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("< & & >", dst); } TEST(xp_unescape, unknown_escape) { char buffer[] = "<&garbage;>"; char dst[sizeof(buffer)]; xp_unescape(buffer, dst); EXPECT_STREQ("<&garbage;>", dst); } #endif //GTEST sipp-3.6.1/src/md5.c0000664000175000017500000003131413730472040013476 0ustar walterwalter/* * Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * L. Peter Deutsch * ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* * Independent implementation of MD5 (RFC 1321). * * This code implements the MD5 Algorithm defined in RFC 1321, whose * text is available at * http://www.ietf.org/rfc/rfc1321.txt * The code is derived from the text of the RFC, including the test suite * (section A.5) but excluding the rest of Appendix A. It does not include * any code or documentation that is identified in the RFC as being * copyrighted. * * The original and principal author of md5.c is L. Peter Deutsch * . Other authors are noted in the change history * that follows (in reverse chronological order): * * 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order * either statically or dynamically; added missing #include * in library. * 2002-03-11 lpd Corrected argument list for main(), and added int return * type, in test program and T value program. * 2002-02-21 lpd Added missing #include in test program. * 2000-07-03 lpd Patched to eliminate warnings about "constant is * unsigned in ANSI C, signed in traditional"; made test program * self-checking. * 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. * 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). * 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti) \ t = a + F(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti) \ t = a + G(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti) \ t = a + H(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti) \ t = a + I(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } sipp-3.6.1/src/auth.cpp0000664000175000017500000006315713730472040014324 0ustar walterwalter/* * 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 * * Author : F. Tarek Rogers - 01 Sept 2004 * Russell Roy * Wolfgang Beck * Dragos Vingarzan - 02 February 2006 vingarzan@gmail.com * - split in the auth architecture * - introduced AKAv1-MD5 * Frederique Aurouet */ #if defined( __FreeBSD__) || defined(__DARWIN) || defined(__SUNOS) #include #endif #include #include #include #include #include "md5.h" #include "milenage.h" #include "screen.hpp" #include "logger.hpp" #include "auth.hpp" #define MAX_HEADER_LEN 2049 #define MD5_HASH_SIZE 16 #define HASH_HEX_SIZE 2*MD5_HASH_SIZE /* AKA */ #define KLEN 16 typedef u_char K[KLEN]; #define RANDLEN 16 typedef u_char RAND[RANDLEN]; #define AUTNLEN 16 typedef u_char AUTN[AUTNLEN]; #define AKLEN 6 typedef u_char AK[AKLEN]; #define AMFLEN 2 typedef u_char AMF[AMFLEN]; #define MACLEN 8 typedef u_char MAC[MACLEN]; #define CKLEN 16 typedef u_char CK[CKLEN]; #define IKLEN 16 typedef u_char IK[IKLEN]; #define SQNLEN 6 typedef u_char SQN[SQNLEN]; #define AUTSLEN 14 typedef char AUTS[AUTSLEN]; #define AUTS64LEN 29 typedef char AUTS64[AUTS64LEN]; #define RESLEN 8 typedef unsigned char RES[RESLEN+1]; #define RESHEXLEN 17 typedef char RESHEX[RESHEXLEN]; #define OPLEN 16 typedef u_char OP[OPLEN]; AMF amfstar="\0"; SQN sqn_he= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /* end AKA */ int createAuthHeaderMD5(const char *user, const char *password, int password_len, const char *method, const char *uri, const char *msgbody, const char *auth, const char *algo, unsigned int nonce_count, char *result); int createAuthHeaderAKAv1MD5(const char *user, const char *OP, const char *AMF, const char *K, const char *method, const char *uri, const char *msgbody, const char *auth, const char *algo, unsigned int nonce_count, char * result); /* This function is from RFC 2617 Section 5 */ static void hashToHex(md5_byte_t* _b_raw, unsigned char* _h) { unsigned short i; unsigned char j; unsigned char *_b = (unsigned char *) _b_raw; for (i = 0; i < MD5_HASH_SIZE; i++) { j = (_b[i] >> 4) & 0xf; if (j <= 9) { _h[i * 2] = (j + '0'); } else { _h[i * 2] = (j + 'a' - 10); } j = _b[i] & 0xf; if (j <= 9) { _h[i * 2 + 1] = (j + '0'); } else { _h[i * 2 + 1] = (j + 'a' - 10); } }; _h[HASH_HEX_SIZE] = '\0'; } static char *stristr(const char* s1, const char* s2) { char *cp = (char*) s1; char *p1, *p2, *endp; char l, r; endp = (char*)s1 + (strlen(s1) - strlen(s2)) ; while (*cp && (cp <= endp)) { p1 = cp; p2 = (char*)s2; while (*p1 && *p2) { l = toupper(*p1); r = toupper(*p2); if (l != r) { break; } p1++; p2++; } if (*p2 == 0) { return cp; } cp++; } return 0; } int createAuthHeader(const char *user, const char *password, const char *method, const char *uri, const char *msgbody, const char *auth, const char *aka_OP, const char *aka_AMF, const char *aka_K, unsigned int nonce_count, char *result) { char algo[32]="MD5"; char *start, *end; if ((start = stristr(auth, "Digest")) == NULL) { sprintf(result, "createAuthHeader: authentication must be digest"); return 0; } if ((start = stristr(auth, "algorithm=")) != NULL) { start = start + strlen("algorithm="); if (*start == '"') { start++; } end = start + strcspn(start, " ,\"\r\n"); strncpy(algo, start, end - start); algo[end - start] ='\0'; } if (strncasecmp(algo, "MD5", 3)==0) { return createAuthHeaderMD5(user, password, strlen(password), method, uri, msgbody, auth, algo, nonce_count, result); } else if (strncasecmp(algo, "AKAv1-MD5", 9)==0) { if (!aka_K) { sprintf(result, "createAuthHeader: AKAv1-MD5 authentication requires a key"); return 0; } return createAuthHeaderAKAv1MD5(user, aka_OP, aka_AMF, aka_K, method, uri, msgbody, auth, algo, nonce_count, result); } else { sprintf(result, "createAuthHeader: authentication must use MD5 or AKAv1-MD5"); return 0; } } int getAuthParameter(const char *name, const char *header, char *result, int len) { char *start, *end; start = stristr(header, name); while (start) { // Ensure that the preceding character is "," or whitespace - this // stops us finding "cnonce" when we search for "nonce". char preceding_char = start[-1]; if ((preceding_char == ',') || isspace(preceding_char)) { break; } start = stristr(start+1, name); } if (!start) { result[0] = '\0'; return 0; } start += strlen(name); if (*start++ != '=') { return getAuthParameter(name, start, result, len); } if (*start == '"') { start++; end = start; while (*end != '"' && *end) { end++; } } else { end = start + strcspn(start, " ,\"\r\n"); } if (end - start >= len) { strncpy(result, start, len - 1); result[len - 1] = '\0'; } else { strncpy(result, start, end - start); result[end - start] = '\0'; } return end - start; } static int createAuthResponseMD5(const char* user, const char* password, int password_len, const char* method, const char* uri, const char* authtype, const char* msgbody, const char* realm, const char* nonce, const char* cnonce, const char* nc, unsigned char* result) { md5_byte_t ha1[MD5_HASH_SIZE], ha2[MD5_HASH_SIZE]; md5_byte_t resp[MD5_HASH_SIZE], body[MD5_HASH_SIZE]; unsigned char body_hex[HASH_HEX_SIZE+1]; unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1]; char tmp[MAX_HEADER_LEN]; md5_state_t Md5Ctx; // Load in A1 md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) user, strlen(user)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) realm, strlen(realm)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) password, password_len); md5_finish(&Md5Ctx, ha1); hashToHex(&ha1[0], &ha1_hex[0]); if (auth_uri) { sprintf(tmp, "sip:%s", auth_uri); } else { strncpy(tmp, uri, sizeof(tmp) - 1); } // If using Auth-Int make a hash of the body - which is NULL for REG if (stristr(authtype, "auth-int") != NULL) { md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) msgbody, strlen(msgbody)); md5_finish(&Md5Ctx, body); hashToHex(&body[0], &body_hex[0]); } // Load in A2 md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) method, strlen(method)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) tmp, strlen(tmp)); if (stristr(authtype, "auth-int") != NULL) { md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) &body_hex, HASH_HEX_SIZE); } md5_finish(&Md5Ctx, ha2); hashToHex(&ha2[0], &ha2_hex[0]); md5_init(&Md5Ctx); md5_append(&Md5Ctx, (md5_byte_t *) &ha1_hex, HASH_HEX_SIZE); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) nonce, strlen(nonce)); if (cnonce[0] != '\0') { md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) nc, strlen(nc)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) cnonce, strlen(cnonce)); md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) authtype, strlen(authtype)); } md5_append(&Md5Ctx, (md5_byte_t *) ":", 1); md5_append(&Md5Ctx, (md5_byte_t *) &ha2_hex, HASH_HEX_SIZE); md5_finish(&Md5Ctx, resp); hashToHex(&resp[0], result); return 1; } int createAuthHeaderMD5(const char *user, const char *password, int password_len, const char *method, const char *uri, const char *msgbody, const char *auth, const char *algo, unsigned int nonce_count, char *result) { unsigned char resp_hex[HASH_HEX_SIZE+1]; char tmp[MAX_HEADER_LEN], tmp2[MAX_HEADER_LEN], realm[MAX_HEADER_LEN], sipuri[MAX_HEADER_LEN], nonce[MAX_HEADER_LEN], authtype[16], cnonce[32], nc[32], opaque[64]; int has_opaque = 0; // Extract the Auth Type - If not present, using 'none' cnonce[0] = '\0'; if (getAuthParameter("qop", auth, authtype, sizeof(authtype))) { // Sloppy auth type recognition (may be "auth,auth-int") if (stristr(authtype, "auth-int")) { strncpy(authtype, "auth-int", sizeof(authtype) - 1); } else if (stristr(authtype, "auth")) { strncpy(authtype, "auth", sizeof(authtype) - 1); } sprintf(cnonce, "%x", rand()); sprintf(nc, "%08x", nonce_count); } // Extract the Opaque value - if present if (getAuthParameter("opaque", auth, opaque, sizeof(opaque))) { has_opaque = 1; } // Extract the Realm if (!getAuthParameter("realm", auth, realm, sizeof(realm))) { sprintf(result, "createAuthHeaderMD5: couldn't parse realm in '%s'", auth); return 0; } sprintf(result, "Digest username=\"%s\",realm=\"%s\"", user, realm); // Construct the URI if (auth_uri == NULL) { sprintf(sipuri, "sip:%s", uri); } else { sprintf(sipuri, "sip:%s", auth_uri); } if (cnonce[0] != '\0') { // No double quotes around nc and qop (RFC3261): // // dig-resp = username / realm / nonce / digest-uri / dresponse // / algorithm / cnonce / opaque / message-qop // message-qop = "qop" EQUAL ("auth" / "auth-int" / token) // nonce-count = "nc" EQUAL 8LHEX // // The digest challenge does have double quotes however: // // digest-cln = realm / domain / nonce / opaque / stale / algorithm // / qop-options / auth-param // qop-options = "qop" EQUAL LDQUOT qop-value *("," qop-value) RDQUOT snprintf(tmp, sizeof(tmp), ",cnonce=\"%s\",nc=%s,qop=%s", cnonce, nc, authtype); strcat(result, tmp); } snprintf(tmp, sizeof(tmp), ",uri=\"%s\"", sipuri); strcat(result, tmp); // Extract the Nonce if (!getAuthParameter("nonce", auth, nonce, sizeof(nonce))) { sprintf(result, "createAuthHeader: couldn't parse nonce"); return 0; } createAuthResponseMD5(user, password, strlen(password), method, sipuri, authtype, msgbody, realm, nonce, cnonce, nc, &resp_hex[0]); snprintf(tmp2, sizeof(tmp2), ",nonce=\"%s\",response=\"%s\",algorithm=%s", nonce, resp_hex, algo); strcat(result, tmp2); if (has_opaque) { snprintf(tmp2, sizeof(tmp2), ",opaque=\"%s\"", opaque); strcat(result, tmp2); } return 1; } int verifyAuthHeader(const char *user, const char *password, const char *method, const char *auth, const char *msgbody) { char algo[MAX_HEADER_LEN]; unsigned char result[HASH_HEX_SIZE + 1]; char response[HASH_HEX_SIZE + 1]; char realm[MAX_HEADER_LEN]; char nonce[MAX_HEADER_LEN]; char cnonce[MAX_HEADER_LEN]; char authtype[MAX_HEADER_LEN]; char nc[MAX_HEADER_LEN]; char uri[MAX_HEADER_LEN]; char *start; if ((start = stristr(auth, "Digest")) == NULL) { WARNING("verifyAuthHeader: authentication must be digest is %s", auth); return 0; } getAuthParameter("algorithm", auth, algo, sizeof(algo)); if (algo[0] == '\0') { strcpy(algo, "MD5"); } if (strncasecmp(algo, "MD5", 3)==0) { getAuthParameter("realm", auth, realm, sizeof(realm)); getAuthParameter("uri", auth, uri, sizeof(uri)); getAuthParameter("nonce", auth, nonce, sizeof(nonce)); getAuthParameter("cnonce", auth, cnonce, sizeof(cnonce)); getAuthParameter("nc", auth, nc, sizeof(nc)); getAuthParameter("qop", auth, authtype, sizeof(authtype)); createAuthResponseMD5(user, password, strlen(password), method, uri, authtype, msgbody, realm, nonce, cnonce, nc, result); getAuthParameter("response", auth, response, sizeof(response)); TRACE_CALLDEBUG("Processing verifyauth command - user %s, password %s, method %s, uri %s, realm %s, nonce %s, result expected %s, response from user %s\n", user, password, method, uri, realm, nonce, (char*)result, response); return !strcmp((char *)result, response); } else { WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5, value is '%s'", algo); return 0; } } /*" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ static int base64_val(char x) { switch(x) { case '=': return -1; case 'A': return 0; case 'B': return 1; case 'C': return 2; case 'D': return 3; case 'E': return 4; case 'F': return 5; case 'G': return 6; case 'H': return 7; case 'I': return 8; case 'J': return 9; case 'K': return 10; case 'L': return 11; case 'M': return 12; case 'N': return 13; case 'O': return 14; case 'P': return 15; case 'Q': return 16; case 'R': return 17; case 'S': return 18; case 'T': return 19; case 'U': return 20; case 'V': return 21; case 'W': return 22; case 'X': return 23; case 'Y': return 24; case 'Z': return 25; case 'a': return 26; case 'b': return 27; case 'c': return 28; case 'd': return 29; case 'e': return 30; case 'f': return 31; case 'g': return 32; case 'h': return 33; case 'i': return 34; case 'j': return 35; case 'k': return 36; case 'l': return 37; case 'm': return 38; case 'n': return 39; case 'o': return 40; case 'p': return 41; case 'q': return 42; case 'r': return 43; case 's': return 44; case 't': return 45; case 'u': return 46; case 'v': return 47; case 'w': return 48; case 'x': return 49; case 'y': return 50; case 'z': return 51; case '0': return 52; case '1': return 53; case '2': return 54; case '3': return 55; case '4': return 56; case '5': return 57; case '6': return 58; case '7': return 59; case '8': return 60; case '9': return 61; case '+': return 62; case '/': return 63; } return 0; } static char* base64_decode_string(const char* buf, unsigned int len, int* newlen) { unsigned long i; int j, x1, x2, x3, x4; char *out; out = (char *)malloc( ( len * 3/4 ) + 8 ); for(i=0, j=0; i + 3 < len; i += 4) { x1=base64_val(buf[i]); x2=base64_val(buf[i+1]); x3=base64_val(buf[i+2]); x4=base64_val(buf[i+3]); out[j++]=(x1<<2) | ((x2 & 0x30)>>4); out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2); out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F); } if (i>4); if (x3==-1) { out[j++]=((x2 & 0x0F)<<4) | ((x3 & 0x3C)>>2); if (x4==-1) { out[j++]=((x3 & 0x03)<<6) | (x4 & 0x3F); } } } } out[j++] = 0; *newlen=j; return out; } char base64[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char hexa[17]="0123456789abcdef"; int createAuthHeaderAKAv1MD5(const char *user, const char *aka_OP, const char *aka_AMF, const char *aka_K, const char *method, const char *uri, const char *msgbody, const char *auth, const char *algo, unsigned int nonce_count, char *result) { char tmp[MAX_HEADER_LEN]; char *start, *end; int has_auts = 0, resuf = 1; char *nonce64, *nonce; int noncelen; AMF amf; OP op; RAND rnd; AUTS auts_bin; AUTS64 auts_hex; MAC mac, xmac; SQN sqn, sqnxoraka, sqn_ms; K k; RES res; CK ck; IK ik; AK ak; int i; // Extract the Nonce if ((start = stristr(auth, "nonce=")) == NULL) { sprintf(result, "createAuthHeaderAKAv1MD5: couldn't parse nonce"); return 0; } start = start + strlen("nonce="); if (*start == '"') { start++; } end = start + strcspn(start, " ,\"\r\n"); strncpy(tmp, start, end - start); tmp[end - start] ='\0'; /* Compute the AKA RES */ nonce64 = tmp; nonce = base64_decode_string(nonce64, end-start, &noncelen); if (noncelen < RANDLEN + AUTNLEN) { sprintf(result, "createAuthHeaderAKAv1MD5 : Nonce is too short %d < %d expected \n", noncelen, RANDLEN + AUTNLEN); if (nonce) free(nonce); return 0; } memcpy(rnd, nonce, RANDLEN); memcpy(sqnxoraka, nonce + RANDLEN, SQNLEN); memcpy(mac, nonce + RANDLEN + SQNLEN + AMFLEN, MACLEN); memcpy(k, aka_K, KLEN); memcpy(amf, aka_AMF, AMFLEN); memcpy(op, aka_OP, OPLEN); /* Compute the AK, response and keys CK IK */ f2345(k, rnd, res, ck, ik, ak, op); res[RESLEN] = '\0'; /* Compute sqn encoded in AUTN */ for (i=0; i < SQNLEN; i++) sqn[i] = sqnxoraka[i] ^ ak[i]; /* compute XMAC */ f1(k, rnd, sqn, (unsigned char *) aka_AMF, xmac, op); if (memcmp(mac, xmac, MACLEN) != 0) { free(nonce); sprintf(result, "createAuthHeaderAKAv1MD5 : MAC != eXpectedMAC -> Server might not know the secret (man-in-the-middle attack?) \n"); return 0; } /* Check SQN, compute AUTS if needed and authorization parameter */ /* the condition below is wrong. * Should trigger synchronization when sqn_ms>>3!=sqn_he>>3 for example. * Also, we need to store the SQN per user or put it as auth parameter. */ if (1/*sqn[5] > sqn_he[5]*/) { sqn_he[5] = sqn[5]; has_auts = 0; /* RES has to be used as password to compute response */ resuf = createAuthHeaderMD5(user, (char *) res, RESLEN, method, uri, msgbody, auth, algo, nonce_count, result); if (resuf == 0) { sprintf(result, "createAuthHeaderAKAv1MD5 : Unexpected return value from createAuthHeaderMD5\n"); free(nonce); return 0; } } else { sqn_ms[5] = sqn_he[5] + 1; f5star(k, rnd, ak, op); for(i=0; i>4]; auts_hex[2*i+1]=hexa[auts_bin[i]&0x0F]; } auts_hex[AUTS64LEN-1]=0; sprintf(tmp, "%s,auts=\"%s\"", result, auts_hex); strcat(result, tmp); } free(nonce); return 1; } #ifdef GTEST #include "gtest/gtest.h" TEST(DigestAuth, nonce) { char nonce[40]; getAuthParameter("nonce", " Authorization: Digest cnonce=\"c7e1249f\",nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", nonce, sizeof(nonce)); EXPECT_STREQ("a6ca2bf13de1433183f7c48781bd9304", nonce); getAuthParameter("nonce", " Authorization: Digest nonce=\"a6ca2bf13de1433183f7c48781bd9304\", cnonce=\"c7e1249f\"", nonce, sizeof(nonce)); EXPECT_STREQ("a6ca2bf13de1433183f7c48781bd9304", nonce); } TEST(DigestAuth, cnonce) { char cnonce[10]; getAuthParameter("cnonce", " Authorization: Digest cnonce=\"c7e1249f\",nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", cnonce, sizeof(cnonce)); EXPECT_STREQ("c7e1249f", cnonce); getAuthParameter("cnonce", " Authorization: Digest nonce=\"a6ca2bf13de1433183f7c48781bd9304\", cnonce=\"c7e1249f\"", cnonce, sizeof(cnonce)); EXPECT_STREQ("c7e1249f", cnonce); } TEST(DigestAuth, MissingParameter) { char cnonce[10]; getAuthParameter("cnonce", " Authorization: Digest nonce=\"a6ca2bf13de1433183f7c48781bd9304\"", cnonce, sizeof(cnonce)); EXPECT_EQ('\0', cnonce[0]); } TEST(DigestAuth, BasicVerification) { char* header = strdup(("Digest \r\n" " realm=\"testrealm@host.com\",\r\n" " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"\r\n," " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")); char result[255]; createAuthHeader("testuser", "secret", "REGISTER", "sip:example.com", "hello world", header, NULL, NULL, NULL, 1, result); EXPECT_STREQ("Digest username=\"testuser\",realm=\"testrealm@host.com\",uri=\"sip:sip:example.com\",nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\",response=\"db94e01e92f2b09a52a234eeca8b90f7\",algorithm=MD5,opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"", result); EXPECT_EQ(1, verifyAuthHeader("testuser", "secret", "REGISTER", result, "hello world")); free(header); } TEST(DigestAuth, qop) { char result[1024]; char* header = strdup(("Digest \r\n" "\trealm=\"testrealm@host.com\",\r\n" "\tqop=\"auth,auth-int\",\r\n" "\tnonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\"\r\n," "\topaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")); createAuthHeader("testuser", "secret", "REGISTER", "sip:example.com", "hello world", header, NULL, NULL, NULL, 1, result); EXPECT_EQ(1, !!strstr(result, ",qop=auth-int,")); // no double quotes around qop-value EXPECT_EQ(1, verifyAuthHeader("testuser", "secret", "REGISTER", result, "hello world")); free(header); } #endif //GTEST sipp-3.6.1/src/variables.cpp0000664000175000017500000002150513730472040015322 0ustar walterwalter/* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. * */ #include "sipp.hpp" /* __________________________________________________________________________ C L A S S C C a l l V a r i a b l e __________________________________________________________________________ */ bool CCallVariable::isSet() { if (M_type == E_VT_REGEXP) { if(M_nbOfMatchingValue >= 1) return(true); else return(false); } else if (M_type == E_VT_BOOL) { return M_bool; } else if (M_type == E_VT_DOUBLE) { return M_double; } return (M_type != E_VT_UNDEFINED); } bool CCallVariable::isDouble() { return (M_type == E_VT_DOUBLE); } bool CCallVariable::isBool() { return (M_type == E_VT_BOOL); } bool CCallVariable::isRegExp() { return (M_type == E_VT_REGEXP); } bool CCallVariable::isString() { return (M_type == E_VT_STRING); } // WARNING : setMatchingValue does't allocate the memory for the matching value // but the destructor free the memory void CCallVariable::setMatchingValue(char* P_matchingVal) { M_type = E_VT_REGEXP; if(M_matchingValue != NULL) { delete [] M_matchingValue; } M_matchingValue = P_matchingVal; M_nbOfMatchingValue++; } char* CCallVariable::getMatchingValue() { if (M_type != E_VT_REGEXP) { return NULL; } return(M_matchingValue); } void CCallVariable::setDouble(double val) { M_type = E_VT_DOUBLE; M_double = val; } double CCallVariable::getDouble() { if (M_type != E_VT_DOUBLE) { return 0.0; } return(M_double); } void CCallVariable::setString(char *P_val) { M_type = E_VT_STRING; free(M_stringValue); M_stringValue = P_val; } char *CCallVariable::getString() { if (M_type == E_VT_STRING) { return(M_stringValue); } else if (M_type == E_VT_REGEXP && M_matchingValue) { return(M_matchingValue); } else { return const_cast(""); /* BUG BUT NOT SO SERIOUS */ } } /* Convert this variable to a double. Returns true on success, false on failure. */ bool CCallVariable::toDouble(double *newValue) { char *p; switch(M_type) { case E_VT_REGEXP: if(M_nbOfMatchingValue < 1) { return false; } *newValue = strtod(M_matchingValue, &p); if (*p) { return false; } break; case E_VT_STRING: *newValue = strtod(M_stringValue, &p); if (*p) { return false; } break; case E_VT_DOUBLE: *newValue = getDouble(); break; case E_VT_BOOL: *newValue = (double)getBool(); break; default: return false; } return true; } void CCallVariable::setBool(bool val) { M_type = E_VT_BOOL; M_bool = val; } bool CCallVariable::getBool() { if (M_type != E_VT_BOOL) { return false; } return(M_bool); } // Constuctor and destructor CCallVariable::CCallVariable() { M_matchingValue = NULL; M_stringValue = NULL; M_nbOfMatchingValue = 0; M_type = E_VT_UNDEFINED; } CCallVariable::~CCallVariable() { if(M_matchingValue != NULL) { delete [] M_matchingValue; } M_matchingValue = NULL; free(M_stringValue); } #define LEVEL_BITS 8 VariableTable::VariableTable(VariableTable *parent, int size) { if (parent) { level = parent->level + 1; assert(level < (1 << LEVEL_BITS)); this->parent = parent->getTable(); } else { level = 0; this->parent = NULL; } count = 1; this->size = size; if (size == 0) { variableTable = NULL; return; } variableTable = (CCallVariable **)malloc(size * sizeof(CCallVariable *)); if (!variableTable) { ERROR("Could not allocate variable table!"); } for (int i = 0; i < size; i++) { variableTable[i] = new CCallVariable(); if (variableTable[i] == NULL) { ERROR ("Call variable allocation failed"); } } } VariableTable::VariableTable(AllocVariableTable *src) { count = 1; this->level = src->level; if (src->parent) { this->parent = src->parent->getTable(); } else { this->parent = NULL; } if (level > 0) { assert(this->parent); } this->size = src->size; if (size == 0) { variableTable = NULL; return; } variableTable = (CCallVariable **)malloc(size * sizeof(CCallVariable *)); if (!variableTable) { ERROR("Could not allocate variable table!"); } for (int i = 0; i < size; i++) { variableTable[i] = new CCallVariable(); if (variableTable[i] == NULL) { ERROR ("Call variable allocation failed"); } } } void VariableTable::expand(int size) { assert(size > this->size); if (size == this->size) { return; } variableTable = (CCallVariable **)realloc(variableTable, size * sizeof(CCallVariable *)); if (!variableTable) { ERROR("Could not expand variable table!"); } for (int i = this->size; i < size; i++) { variableTable[i] = new CCallVariable(); if (variableTable[i] == NULL) { ERROR ("Call variable allocation failed"); } } this->size = size; } VariableTable::~VariableTable() { if (parent) { parent->putTable(); } for (int i = 0; i < size; i++) { delete variableTable[i]; } free(variableTable); } VariableTable *VariableTable::getTable() { count++; return this; } void VariableTable::putTable() { if (--count == 0) { delete this; } } CCallVariable *VariableTable::getVar(int i) { int thisLevel = i & ((1 << LEVEL_BITS) - 1); assert(thisLevel <= level); if (thisLevel == level) { i = i >> LEVEL_BITS; assert(i > 0); assert(i <= size ); return variableTable[i - 1]; } assert(parent); return parent->getVar(i); } AllocVariableTable::AllocVariableTable(AllocVariableTable *av_parent) : VariableTable((VariableTable *)av_parent, 0) { this->av_parent = av_parent; } AllocVariableTable::~AllocVariableTable() { clear_str_int(variableMap); clear_int_str(variableRevMap); clear_int_int(variableReferences); } int AllocVariableTable::find(const char *varName, bool allocate) { /* If this variable has already been used, then we have nothing to do. */ str_int_map::iterator var_it = variableMap.find(varName); if (var_it != variableMap.end()) { variableReferences[var_it->second]++; return var_it->second; } if (av_parent) { int ret = av_parent->find(varName, false); if (ret > 0) { return ret; } } if (allocate) { int varNum = size + 1; expand(varNum); varNum = (varNum << LEVEL_BITS) | level; variableMap[varName] = varNum; variableReferences[varNum] = 1; variableRevMap[varNum] = strdup(varName); return varNum; } return -1; } char *AllocVariableTable::getName(int i) { int thisLevel = i & ((1 << LEVEL_BITS) - 1); assert(thisLevel <= level); if (thisLevel == level) { return variableRevMap[i]; } assert(av_parent); return av_parent->getName(i); } void AllocVariableTable::dump() { if (av_parent) { av_parent->dump(); } WARNING("%zu level %d variables:", variableMap.size(), level); for (str_int_map::iterator i = variableMap.begin(); i != variableMap.end(); i++) { WARNING("%s", i->first.c_str()); } } void AllocVariableTable::validate() { for (str_int_map::iterator var_it = variableMap.begin(); var_it != variableMap.end(); var_it++) { if (variableReferences[var_it->second] < 2) { const char *varName = var_it->first.c_str(); int varRef = variableReferences[var_it->second]; if (strcmp(varName, "_") != 0) { ERROR("Variable $%s is referenced %d times!", varName, varRef); } } } if (av_parent) { av_parent->validate(); } } sipp-3.6.1/src/infile.cpp0000664000175000017500000002560213730472040014622 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. * Charles P. Wright from IBM Research */ #include "sipp.hpp" #include "screen.hpp" #include "stat.hpp" #include "infile.hpp" #include #include /* Read MAX_CHAR_BUFFER_SIZE size lines from the "fileName" and populate it in * the fileContents vector. Each line should be terminated with a '\n' */ FileContents::FileContents(const char *fileName) { ifstream *inFile = new ifstream(fileName); int virtualLines = 0; if (!inFile->good()) { ERROR("Unable to open file %s", fileName); } this->fileName = fileName; realLinesInFile = lineCounter = numLinesInFile = 0; /* Initialize printf info. */ printfFile = false; printfOffset = 0; printfMultiple = 1; std::string lineStr; std::getline(*inFile, lineStr); const char* line = lineStr.c_str(); if (NULL != strstr(line, "RANDOM")) { usage = InputFileRandomOrder; } else if (NULL != strstr(line, "SEQUENTIAL")) { usage = InputFileSequentialOrder; } else if (NULL != strstr(line, "USER")) { usage = InputFileUser; } else { ERROR("Unknown file type (valid values are RANDOM, SEQUENTIAL, and USER) for %s:%s", fileName, line); } const char *useprintf; if ((useprintf = strstr(line, "PRINTF"))) { /* We are going to operate in printf mode, which uses the line as a format * string for printf with the line number. */ useprintf += strlen("PRINTF"); if (*useprintf != '=') { ERROR("Invalid file printf specification (requires =) for %s:%s", fileName, line); } useprintf++; char *endptr; virtualLines = strtoul(useprintf, &endptr, 0); if (*endptr && *endptr != '\r' && *endptr != '\n' && *endptr != ',') { ERROR("Invalid file printf specification for (invalid end character '%c') %s:%s", *endptr, fileName, line); } if (virtualLines == 0) { ERROR("A printf file must have at least one virtual line %s:%s", fileName, line); } printfFile = true; } if ((useprintf = strstr(line, "PRINTFOFFSET"))) { useprintf += strlen("PRINTFOFFSET"); if (*useprintf != '=') { ERROR("Invalid file PRINTFOFFSET specification (requires =) for %s:%s", fileName, line); } useprintf++; char *endptr; printfOffset = strtoul(useprintf, &endptr, 0); if (*endptr && *endptr != '\n' && *endptr != ',') { ERROR("Invalid PRINTFOFFSET specification for (invalid end character '%c') %s:%s", *endptr, fileName, line); } } if ((useprintf = strstr(line, "PRINTFMULTIPLE"))) { useprintf += strlen("PRINTFMULTIPLE"); if (*useprintf != '=') { ERROR("Invalid PRINTFMULTIPLE specification (requires =) for %s:%s", fileName, line); } useprintf++; char *endptr; printfMultiple = strtoul(useprintf, &endptr, 0); if (*endptr && *endptr != '\n' && *endptr != ',') { ERROR("Invalid PRINTFOFFSET specification for (invalid end character '%c') %s:%s", *endptr, fileName, line); } } while (!inFile->eof()) { lineStr.clear(); std::getline(*inFile, lineStr); if (!lineStr.empty()) { if ('#' != lineStr[0]) { fileLines.push_back(lineStr); realLinesInFile++; /* this counts number of valid data lines */ } } else { break; } } if (realLinesInFile == 0) { ERROR("Input file has zero lines: %s", fileName); } if (printfFile) { numLinesInFile = virtualLines; } else { numLinesInFile = realLinesInFile; } delete inFile; indexMap = NULL; indexField = -1; } int FileContents::getLine(int line, char *dest, int len) { if (printfFile) { line %= realLinesInFile; } return snprintf(dest, len, "%s", fileLines[line].c_str()); } int FileContents::getField(int lineNum, int field, char *dest, int len) { int curfield = field; int curline = lineNum; dest[0] = '\0'; if (lineNum >= numLinesInFile) { return 0; } if (printfFile) { curline %= realLinesInFile; } const string & line = fileLines[curline]; size_t pos(0), oldpos(0); do { oldpos = pos; size_t localpos = line.find(';', oldpos); if (localpos != string::npos) { pos = localpos + 1; } else { pos = localpos; break; } if (curfield == 0) { break; } curfield --; } while (oldpos != string::npos); if (curfield) { WARNING("Field %d not found in the file %s", field, fileName); return 0; } if (string::npos == oldpos) { return 0; } if (string::npos != pos) { // should not be decremented for fieldN pos -= (oldpos + 1); } string x = line.substr(oldpos, pos); if (x.length()) { if (printfFile) { const char *s = x.c_str(); int l = strlen(s); int copied = 0; for (int i = 0; i < l; i++) { if (s[i] == '%') { if (s[i + 1] == '%') { dest[copied++] = s[i]; } else { const char *format = s + i; i++; while (s[i] != 'd') { if (i == l) { ERROR("Invalid printf injection field (ran off end of line): %s", s); } if (!(isdigit(s[i]) || s[i] == '.' || s[i] == '-')) { ERROR("Invalid printf injection field (only decimal values allowed '%c'): %s", s[i], s); } i++; } assert(s[i] == 'd'); char *tmp = (char *)malloc(s + i + 2 - format); if (!tmp) { ERROR("Out of memory!"); } memcpy(tmp, format, s + i + 1 - format); tmp[s + i + 1 - format] = '\0'; copied += sprintf(dest + copied, tmp, printfOffset + (lineNum * printfMultiple)); free(tmp); } } else { dest[copied++] = s[i]; } } dest[copied] = '\0'; return copied; } else { return snprintf(dest, len, "%s", x.c_str()); } } else { return 0; } } int FileContents::numLines() { return numLinesInFile; } int FileContents::nextLine(int userId) { switch(usage) { case InputFileRandomOrder: return rand() % numLinesInFile; case InputFileSequentialOrder: { int ret = lineCounter; lineCounter = (lineCounter + 1) % numLinesInFile; return ret; } case InputFileUser: if (userId == 0) { return -1; } if ((userId - 1) >= numLinesInFile) { ERROR("%s has only %d lines, yet user %d was requested.", fileName, numLinesInFile, userId); } return userId - 1; default: ERROR("Internal error: unknown file usage mode!"); return -1; } } void FileContents::dump(void) { WARNING("Line choosing strategy is [%s]. m_counter [%d] numLinesInFile [%d] realLinesInFile [%d]", usage == InputFileSequentialOrder ? "SEQUENTIAL" : usage == InputFileRandomOrder ? "RANDOM" : usage == InputFileUser ? "USER" : "UNKNOWN", lineCounter, numLinesInFile, realLinesInFile); for (int i = 0; i < realLinesInFile && fileLines[i][0]; i++) { WARNING("%s:%d reads [%s]", fileName, i, fileLines[i].c_str()); } } void FileContents::index(int field) { this->indexField = field; indexMap = new str_int_map; for (int line = 0; line < numLines(); line++) { reIndex(line); } } int FileContents::lookup(char *key) { if (indexField == -1) { ERROR("Invalid Index File: %s", fileName); } if (!indexMap) { ERROR("Invalid Index File: %s", fileName); } str_int_map::iterator index_it = indexMap->find(key); if (index_it == indexMap->end()) { return -1; } return index_it->second; } void FileContents::insert(char *value) { if (printfFile) { ERROR("Can not insert or replace into a printf file: %s", fileName); } fileLines.push_back(value); realLinesInFile++; numLinesInFile++; if (indexField != -1) { reIndex(realLinesInFile - 1); } char line[1024]; getLine(realLinesInFile - 1, line, sizeof(line)); char tmp[1024]; getField(realLinesInFile - 1, 0, tmp, sizeof(tmp)); } void FileContents::replace(int line, char *value) { if (printfFile) { ERROR("Can not insert or replace into a printf file: %s", fileName); } if (line >= realLinesInFile || line < 0) { ERROR("Invalid line number (%d) for file: %s (%d lines)", line, fileName, realLinesInFile); } deIndex(line); fileLines[line] = value; reIndex(line); } void FileContents::reIndex(int line) { if (indexField == -1) { return; } assert(line >= 0); assert(line < realLinesInFile); char tmp[SIPP_MAX_MSG_SIZE]; getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE); str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp)); if (index_it != indexMap->end()) { indexMap->erase(index_it); } indexMap->insert(pair(str_int_map::key_type(tmp), line)); } void FileContents::deIndex(int line) { if (indexField == -1) { return; } assert(line >= 0); assert(line < realLinesInFile); char tmp[SIPP_MAX_MSG_SIZE]; getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE); str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp)); if (index_it != indexMap->end()) { if (index_it->second == line) { indexMap->erase(index_it); } } } sipp-3.6.1/src/sip_parser.cpp0000664000175000017500000004502313730472040015522 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Stefan Esser * Andy Aicken * Walter Doekes */ #ifndef _GNU_SOURCE #define _GNU_SOURCE /* needed for strcasestr on cygwin */ #endif #include #include #include "screen.hpp" #include "sip_parser.hpp" /*************************** Mini SIP parser (internals) ***************/ /* * SIP ABNF can be found here: * http://tools.ietf.org/html/rfc3261#section-25 * In 2014, there is a very helpful site that lets you browse the ABNF * easily: * http://www.in2eps.com/fo-abnf/tk-fo-abnf-sip.html */ static const char* internal_find_param(const char* ptr, const char* name); static const char* internal_find_header(const char* msg, const char* name, const char* shortname, bool content); static const char* internal_skip_lws(const char* ptr); /* Search for a character, but only inside this header. Returns NULL if * not found. */ static const char* internal_hdrchr(const char* ptr, const char needle); /* Seek to end of this header. Returns the position the next character, * which must be at the header-delimiting-CRLF or, if the message is * broken, at the ASCIIZ NUL. */ static const char* internal_hdrend(const char* ptr); /*************************** Mini SIP parser (externals) ***************/ char* get_peer_tag(const char* msg) { static char tag[MAX_HEADER_LEN]; const char * to_hdr; const char * ptr; int tag_i = 0; /* Find start of header */ to_hdr = internal_find_header(msg, "To", "t", true); if (!to_hdr) { WARNING("No valid To: header in reply"); return NULL; } /* Skip past display-name */ /* FIXME */ /* Skip past LA/RA-quoted addr-spec if any */ ptr = internal_hdrchr(to_hdr, '>'); if (!ptr) { /* Maybe an addr-spec without quotes */ ptr = to_hdr; } /* Find tag in this header */ ptr = internal_find_param(ptr, "tag"); if (!ptr) { return NULL; } while (*ptr && *ptr != ' ' && *ptr != ';' && *ptr != '\t' && *ptr != '\r' && *ptr != '\n') { tag[tag_i++] = *(ptr++); } tag[tag_i] = '\0'; return tag; } char* get_header_content(const char* message, const char* name) { return get_header(message, name, true); } /* If content is true, we only return the header's contents. */ char* get_header(const char* message, const char* name, bool content) { /* non reentrant. consider accepting char buffer as param */ static char last_header[MAX_HEADER_LEN * 10]; const char *cptr; char *src, *src_orig, *dest, *start, *ptr; /* Are we searching for a short form header? */ bool short_form = false; bool first_time = true; char header_with_newline[MAX_HEADER_LEN + 1]; /* returns empty string in case of error */ last_header[0] = '\0'; if (!message || !*message) { return last_header; } /* for safety's sake */ if (!name || !strrchr(name, ':')) { WARNING("Can not search for header (no colon): %s", name ? name : "(null)"); return last_header; } /* find end of SIP headers - perform search only until that */ cptr = strstr(message, "\r\n\r\n"); if (!cptr) { src_orig = strdup(message); } else if ((src_orig = (char*)malloc(cptr - message + 1))) { src_orig[cptr - message] = '\0'; memcpy(src_orig, message, cptr - message); } if (!src_orig) { ERROR("Out of memory"); return last_header; } do { /* We want to start from the beginning of the message each time * through this loop, because we may be searching for a short form. */ src = src_orig; snprintf(header_with_newline, MAX_HEADER_LEN, "\n%s", name); dest = last_header; while ((src = strcasestr(src, header_with_newline))) { if (content || !first_time) { /* Just want the header's content, so skip over the header * and newline */ src += strlen(name) + 1; /* Skip over leading spaces. */ while (*src == ' ') { src++; } } else { /* Just skip the newline */ src++; } first_time = false; ptr = strchr(src, '\n'); /* Multiline headers always begin with a tab or a space * on the subsequent lines. Skip those lines. */ while (ptr && (*(ptr+1) == ' ' || *(ptr+1) == '\t')) { ptr = strchr(ptr + 1, '\n'); } if (ptr) { *ptr = 0; } // Add ", " when several headers are present if (dest != last_header) { /* Remove trailing whitespaces, tabs, and CRs */ while (dest > last_header && (*(dest-1) == ' ' || *(dest-1) == '\r' || *(dest-1) == '\n' || *(dest-1) == '\t')) { *(--dest) = 0; } dest += sprintf(dest, ", "); } dest += sprintf(dest, "%s", src); if (ptr) { *ptr = '\n'; } src++; } /* We found the header. */ if (dest != last_header) { break; } /* We didn't find the header, even in its short form. */ if (short_form) { free(src_orig); return last_header; } /* We should retry with the short form. */ short_form = true; if (!strcasecmp(name, "call-id:")) { name = "i:"; } else if (!strcasecmp(name, "contact:")) { name = "m:"; } else if (!strcasecmp(name, "content-encoding:")) { name = "e:"; } else if (!strcasecmp(name, "content-length:")) { name = "l:"; } else if (!strcasecmp(name, "content-type:")) { name = "c:"; } else if (!strcasecmp(name, "from:")) { name = "f:"; } else if (!strcasecmp(name, "to:")) { name = "t:"; } else if (!strcasecmp(name, "via:")) { name = "v:"; } else { /* There is no short form to try. */ free(src_orig); return last_header; } } while (1); *(dest--) = 0; /* Remove trailing whitespaces, tabs, and CRs */ while (dest > last_header && (*dest == ' ' || *dest == '\r' || *dest == '\t')) { *(dest--) = 0; } /* Remove leading whitespaces */ for (start = last_header; *start == ' '; start++); /* remove enclosed CRs in multilines */ /* don't remove enclosed CRs for multiple headers (e.g. Via) (Rhys) */ while ((ptr = strstr(last_header, "\r\n")) != NULL && (*(ptr + 2) == ' ' || *(ptr + 2) == '\r' || *(ptr + 2) == '\t')) { /* Use strlen(ptr) to include trailing zero */ memmove(ptr, ptr+1, strlen(ptr)); } /* Remove illegal double CR characters */ while ((ptr = strstr(last_header, "\r\r")) != NULL) { memmove(ptr, ptr+1, strlen(ptr)); } /* Remove illegal double Newline characters */ while ((ptr = strstr(last_header, "\n\n")) != NULL) { memmove(ptr, ptr+1, strlen(ptr)); } free(src_orig); return start; } char* get_first_line(const char* message) { /* non reentrant. consider accepting char buffer as param */ static char last_header[MAX_HEADER_LEN * 10]; const char* src; /* returns empty string in case of error */ memset(last_header, 0, sizeof(last_header)); if (!message || !*message) { return last_header; } src = message; int i=0; while (*src) { if (*src == '\n' || *src == '\r') { break; } last_header[i] = *src; i++; src++; } return last_header; } char* get_call_id(const char* msg) { static char call_id[MAX_HEADER_LEN]; const char *content, *end_of_header; unsigned length; call_id[0] = '\0'; content = internal_find_header(msg, "Call-ID", "i", true); if (!content) { WARNING("(1) No valid Call-ID: header in reply '%s'", msg); return call_id; } /* Always returns something */ end_of_header = internal_hdrend(content); length = end_of_header - content; if (length + 1 > MAX_HEADER_LEN) { WARNING("(1) Call-ID: header too long in reply '%s'", msg); return call_id; } memcpy(call_id, content, length); call_id[length] = '\0'; return call_id; } unsigned long int get_cseq_value(const char* msg) { const char* ptr1; // no short form for CSeq: ptr1 = strstr(msg, "\r\nCSeq:"); if (!ptr1) { ptr1 = strstr(msg, "\r\nCSEQ:"); } if (!ptr1) { ptr1 = strstr(msg, "\r\ncseq:"); } if (!ptr1) { ptr1 = strstr(msg, "\r\nCseq:"); } if (!ptr1) { WARNING("No valid Cseq header in request %s", msg); return 0; } ptr1 += 7; while (*ptr1 == ' ' || *ptr1 == '\t') { ++ptr1; } if (!*ptr1) { WARNING("No valid Cseq data in header"); return 0; } return strtoul(ptr1, NULL, 10); } unsigned long get_reply_code(const char* msg) { while (msg && *msg != ' ' && *msg != '\t') ++msg; while (msg && (*msg == ' ' || *msg == '\t')) ++msg; if (msg && strlen(msg) > 0) { return atol(msg); } return 0; } static const char* internal_find_header(const char* msg, const char* name, const char* shortname, bool content) { const char *ptr = msg; int namelen = strlen(name); int shortnamelen = shortname ? strlen(shortname) : 0; while (1) { int is_short = 0; /* RFC3261, 7.3.1: When comparing header fields, field names * are always case-insensitive. Unless otherwise stated in * the definition of a particular header field, field values, * parameter names, and parameter values are case-insensitive. * Tokens are always case-insensitive. Unless specified * otherwise, values expressed as quoted strings are case- * sensitive. * * Ergo, strcasecmp, because: * To:...;tag=bla == TO:...;TAG=BLA * But: * Warning: "something" != Warning: "SoMeThInG" */ if (strncasecmp(ptr, name, namelen) == 0 || (shortname && (is_short = 1) && strncasecmp(ptr, shortname, shortnamelen) == 0)) { const char *tmp = ptr + (is_short ? strlen(shortname) : strlen(name)); while (*tmp == ' ' || *tmp == '\t') { ++tmp; } if (*tmp == ':') { /* Found */ if (content) { /* We just want the content */ ptr = internal_skip_lws(tmp + 1); } break; } } /* Seek to next line, but not past EOH */ ptr = strchr(ptr, '\n'); if (!ptr || ptr[-1] != '\r' || (ptr[1] == '\r' && ptr[2] == '\n')) { if (ptr && ptr[-1] != '\r') { WARNING("Missing CR during header scan at pos %ld", ptr - msg); /* continue? */ } return NULL; } ++ptr; } return ptr; } static const char* internal_hdrchr(const char* ptr, const char needle) { if (*ptr == '\n') { return NULL; /* stray LF */ } while (1) { if (*ptr == '\0') { return NULL; } else if (*ptr == needle) { return ptr; } else if (*ptr == '\n') { if (ptr[-1] == '\r' && ptr[1] != ' ' && ptr[1] != '\t') { return NULL; /* end of header */ } } ++ptr; } return NULL; /* never gets here */ } static const char* internal_hdrend(const char* ptr) { const char *p = ptr; while (*p) { if (p[0] == '\r' && p[1] == '\n' && (p[2] != ' ' && p[2] != '\t')) { return p; } ++p; } return p; } static const char* internal_find_param(const char* ptr, const char* name) { int namelen = strlen(name); while (1) { ptr = internal_hdrchr(ptr, ';'); if (!ptr) { return NULL; } ++ptr; ptr = internal_skip_lws(ptr); if (!ptr || !*ptr) { return NULL; } /* Case insensitive, see RFC 3261 7.3.1 notes above. */ if (strncasecmp(ptr, name, namelen) == 0 && *(ptr + namelen) == '=') { ptr += namelen + 1; return ptr; } } return NULL; /* never gets here */ } static const char* internal_skip_lws(const char* ptr) { while (1) { while (*ptr == ' ' || *ptr == '\t') { ++ptr; } if (ptr[0] == '\r' && ptr[1] == '\n') { if (ptr[2] == ' ' || ptr[2] == '\t') { ptr += 3; continue; } return NULL; /* end of this header */ } return ptr; } return NULL; /* never gets here */ } #ifdef GTEST #include "gtest/gtest.h" TEST(Parser, internal_find_header) { char data[] = "OPTIONS sip:server SIP/2.0\r\n" "Took: abc1\r\n" "To k: abc2\r\n" "To\t :\r\n abc3\r\n" "From: def\r\n" "\r\n"; const char *eq = strstr(data, "To\t :"); EXPECT_STREQ(eq, internal_find_header(data, "To", "t", false)); EXPECT_STREQ(eq + 8, internal_find_header(data, "To", "t", true)); } TEST(Parser, internal_find_header_no_callid_missing_cr_in_to) { char data[4096]; const char *pos; const char *p; /* If you remove the CR ("\r") from any header before the Call-ID, * the Call-ID will not be found. */ strncpy(data, "INVITE sip:3136455552@85.12.1.1:5065 SIP/2.0\r\n\ Via: SIP/2.0/UDP 85.55.55.12:5060;branch=z9hG4bK831a.2bb3de85.0\r\n\ From: \"3136456666\" ;tag=b62e0d72-be14-4d3c-bd6a-b4da593b6b17\r\n\ To: \n\ Contact: \r\n\ Call-ID: DLGCH_K0IEXzVwYzJiQlwKMGRkMX5GSAxiKmJ+exQADWYsZ2QsFQFb\r\n\ CSeq: 6476 INVITE\r\n\ Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER\r\n\ Supported: 100rel, timer, replaces, norefersub\r\n\ Session-Expires: 1800\r\n\ Min-SE: 90\r\n\ Max-Forwards: 70\r\n\ Content-Type: application/sdp\r\n\ Content-Length: 278\r\n\ \r\n\ v=0\r\n\ o=- 592907310 592907310 IN IP4 85.55.55.30\r\n\ s=Centrex v.1.0\r\n\ c=IN IP4 85.55.55.30\r\n\ t=0 0\r\n\ m=audio 41604 RTP/AVP 8 0 101\r\n\ a=rtpmap:8 PCMA/8000\r\n\ a=rtpmap:0 PCMU/8000\r\n\ a=rtpmap:101 telephone-event/8000\r\n\ a=fmtp:101 0-16\r\n\ a=ptime:20\r\n\ a=maxptime:150\r\n\ a=sendrecv\r\n\ a=rtcp:41605\r\n\ ", sizeof(data) - 1); if ((pos = internal_find_header(data, "Call-ID", "i", false)) && (p = strchr(pos, '\r'))) { data[p - data] = '\0'; /* Unexpected.. */ ASSERT_FALSE(1); EXPECT_STREQ(pos, "Call-ID: DLGCH_K0IEXzVwYzJiQlwKMGRkMX5GSAxiKmJ+exQADWYsZ2QsFQFb"); } else { /* Not finding any, because of missing CR. */ ASSERT_TRUE(1); } } TEST(Parser, get_peer_tag__notag) { EXPECT_STREQ(NULL, get_peer_tag("...\r\nTo: \r\n;tag=notag\r\n\r\n")); } TEST(Parser, get_peer_tag__normal) { EXPECT_STREQ("normal", get_peer_tag("...\r\nTo: ;t2=x;tag=normal;t3=y\r\n\r\n")); } TEST(Parser, get_peer_tag__upper) { EXPECT_STREQ("upper", get_peer_tag("...\r\nTo: ;t2=x;TAG=upper;t3=y\r\n\r\n")); } TEST(Parser, get_peer_tag__normal_2) { EXPECT_STREQ("normal2", get_peer_tag("...\r\nTo: abc;tag=normal2\r\n\r\n")); } TEST(Parser, get_peer_tag__folded) { EXPECT_STREQ("folded", get_peer_tag("...\r\nTo: \r\n ;tag=folded\r\n\r\n")); } TEST(Parser, get_peer_tag__space) { EXPECT_STREQ("space", get_peer_tag("...\r\nTo: ;tag=space\r\n\r\n")); } TEST(Parser, get_peer_tag__space_2) { EXPECT_STREQ("space2", get_peer_tag("...\r\nTo \t:\r\n abc\r\n ;tag=space2\r\n\r\n")); } TEST(Parser, get_call_id_1) { EXPECT_STREQ("test1", get_call_id("...\r\nCall-ID: test1\r\n\r\n")); } TEST(Parser, get_call_id_2) { EXPECT_STREQ("test2", get_call_id("...\r\nCALL-ID:\r\n test2\r\n\r\n")); } TEST(Parser, get_call_id_3) { EXPECT_STREQ("test3", get_call_id("...\r\ncall-id:\r\n\t test3\r\n\r\n")); } TEST(Parser, get_call_id_short_1) { EXPECT_STREQ("testshort1", get_call_id("...\r\ni: testshort1\r\n\r\n")); } TEST(Parser, get_call_id_short_2) { /* The WS surrounding the colon belongs with HCOLON, but the * trailing WS does not. */ EXPECT_STREQ("testshort2 \t ", get_call_id("...\r\nI:\r\n \r\n \t testshort2 \t \r\n\r\n")); } /* The 3pcc-A script sends "invalid" SIP that is parsed by this * sip_parser. We must accept headers without any leading request/ * response line: * * * * */ TEST(Parser, get_call_id_github_0101) { // github-#0101 const char *input = "Call-ID: 1-18220@127.0.0.1\r\n" "Content-Type: application/sdp\r\n" "Content-Length: 129\r\n\r\n" "v=0\r\no=user1 53655765 2353687637 IN IP4 127.0.0.1\r\n" "s=-\r\nc=IN IP4 127.0.0.1\r\nt=0 0\r\n" "m=audio 6000 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000"; EXPECT_STREQ("1-18220@127.0.0.1", get_call_id(input)); } #endif //GTEST sipp-3.6.1/src/socket.cpp0000664000175000017500000027452613730472040014657 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #ifndef _GNU_SOURCE #define _GNU_SOURCE /* needed for strcasestr on cygwin */ #endif #include #include #include #include #include "config.h" #include "sipp.hpp" #include "socket.hpp" #include "logger.hpp" /* Older non C++11 gcc (4.6) does not have nullptr */ #define const_char_nullptr (reinterpret_cast(0)) extern bool do_hide; extern bool show_index; SIPpSocket *ctrl_socket = NULL; SIPpSocket *stdin_socket = NULL; static int stdin_fileno = -1; static int stdin_mode; /******************** Recv Poll Processing *********************/ unsigned pollnfds; #ifdef HAVE_EPOLL int epollfd; struct epoll_event epollfiles[SIPP_MAXFDS]; struct epoll_event* epollevents; #else struct pollfd pollfiles[SIPP_MAXFDS]; #endif SIPpSocket *sockets[SIPP_MAXFDS]; int pending_messages = 0; map map_perip_fd; int gai_getsockaddr(struct sockaddr_storage* ss, const char* host, const char *service, int flags, int family) { const struct addrinfo hints = {flags, family,}; struct addrinfo* res; int error = getaddrinfo(host, service, &hints, &res); if (error == 0) { memcpy(ss, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } else { WARNING("getaddrinfo failed: %s", gai_strerror(error)); } return error; } int gai_getsockaddr(struct sockaddr_storage* ss, const char* host, unsigned short port, int flags, int family) { if (port) { char service[NI_MAXSERV + 1]; snprintf(service, sizeof(service), "%d", port); return gai_getsockaddr(ss, host, service, flags, family); } else { return gai_getsockaddr(ss, host, const_char_nullptr, flags, family); } } void sockaddr_update_port(struct sockaddr_storage* ss, short port) { switch (ss->ss_family) { case AF_INET: _RCAST(struct sockaddr_in*, ss)->sin_port = htons(port); break; case AF_INET6: _RCAST(struct sockaddr_in6*, ss)->sin6_port = htons(port); break; default: ERROR("Unsupported family type"); } } static void process_set(char* what) { char *rest = strchr(what, ' '); if (rest) { *rest++ = '\0'; trim(rest); } else { WARNING("The set command requires two arguments (attribute and value)"); return; } if (!strcmp(what, "rate")) { char *end; double drest = strtod(rest, &end); if (users >= 0) { WARNING("Rates can not be set in a user-based benchmark."); } else if (*end) { WARNING("Invalid rate value: \"%s\"", rest); } else { CallGenerationTask::set_rate(drest); } } else if (!strcmp(what, "rate-scale")) { char *end; double drest = strtod(rest, &end); if (*end) { WARNING("Invalid rate-scale value: \"%s\"", rest); } else { rate_scale = drest; } } else if (!strcmp(what, "users")) { char *end; int urest = strtol(rest, &end, 0); if (users < 0) { WARNING("Users can not be changed at run time for a rate-based benchmark."); } else if (*end) { WARNING("Invalid users value: \"%s\"", rest); } else if (urest < 0) { WARNING("Invalid users value: \"%s\"", rest); } else { CallGenerationTask::set_users(urest); } } else if (!strcmp(what, "limit")) { char *end; unsigned long lrest = strtoul(rest, &end, 0); if (users >= 0) { WARNING("Can not set call limit for a user-based benchmark."); } else if (*end) { WARNING("Invalid limit value: \"%s\"", rest); } else { open_calls_allowed = lrest; open_calls_user_setting = 1; } } else if (!strcmp(what, "display")) { if (!strcmp(rest, "main")) { display_scenario = main_scenario; } else if (!strcmp(rest, "ooc")) { display_scenario = ooc_scenario; } else { WARNING("Unknown display scenario: %s", rest); } } else if (!strcmp(what, "hide")) { if (!strcmp(rest, "true")) { do_hide = true; } else if (!strcmp(rest, "false")) { do_hide = false; } else { WARNING("Invalid bool: %s", rest); } } else if (!strcmp(what, "index")) { if (!strcmp(rest, "true")) { show_index = true; } else if (!strcmp(rest, "false")) { show_index = false; } else { WARNING("Invalid bool: %s", rest); } } else { WARNING("Unknown set attribute: %s", what); } } static void process_trace(char* what) { bool on = false; char *rest = strchr(what, ' '); if (rest) { *rest++ = '\0'; trim(rest); } else { WARNING("The trace command requires two arguments (log and [on|off])"); return; } if (!strcmp(rest, "on")) { on = true; } else if (!strcmp(rest, "off")) { on = false; } else if (!strcmp(rest, "true")) { on = true; } else if (!strcmp(rest, "false")) { on = false; } else { WARNING("The trace command's second argument must be on or off."); return; } if (!strcmp(what, "error")) { if (on == !!print_all_responses) { return; } if (on) { print_all_responses = 1; } else { print_all_responses = 0; log_off(&error_lfi); } } else if (!strcmp(what, "logs")) { if (on == !!log_lfi.fptr) { return; } if (on) { useLogf = 1; rotate_logfile(); } else { useLogf = 0; log_off(&log_lfi); } } else if (!strcmp(what, "messages")) { if (on == !!message_lfi.fptr) { return; } if (on) { useMessagef = 1; rotate_logfile(); } else { useMessagef = 0; log_off(&message_lfi); } } else if (!strcmp(what, "shortmessages")) { if (on == !!shortmessage_lfi.fptr) { return; } if (on) { useShortMessagef = 1; rotate_shortmessagef(); } else { useShortMessagef = 0; log_off(&shortmessage_lfi); } } else { WARNING("Unknown log file: %s", what); } } static void process_dump(char* what) { if (!strcmp(what, "tasks")) { dump_tasks(); } else if (!strcmp(what, "variables")) { display_scenario->allocVars->dump(); } else { WARNING("Unknown dump type: %s", what); } } static void process_reset(char* what) { if (!strcmp(what, "stats")) { main_scenario->stats->computeStat(CStat::E_RESET_C_COUNTERS); } else { WARNING("Unknown reset type: %s", what); } } static bool process_command(char* command) { trim(command); char *rest = strchr(command, ' '); if (rest) { *rest++ = '\0'; trim(rest); } if (!rest) { WARNING("The %s command requires at least one argument", command); } else if (!strcmp(command, "set")) { process_set(rest); } else if (!strcmp(command, "trace")) { process_trace(rest); } else if (!strcmp(command, "dump")) { process_dump(rest); } else if (!strcmp(command, "reset")) { process_reset(rest); } else { WARNING("Unrecognized command: \"%s\"", command); } return false; } int command_mode = 0; char *command_buffer = NULL; extern bool sipMsgCheck (const char *P_msg, SIPpSocket *socket); static const char* get_trimmed_call_id(const char* msg) { /* A call_id identifies a call and is generated by SIPp for each * new call. In client mode, it is mandatory to use the value * generated by SIPp in the "Call-ID" header. Otherwise, SIPp will * not recognise the answer to the message sent as being part of an * existing call. * * Note: [call_id] can be pre-pended with an arbitrary string using * '///'. * Example: Call-ID: ABCDEFGHIJ///[call_id] * - it will still be recognized by SIPp as part of the same call. */ const char *call_id = get_call_id(msg); const char *slashes = strstr(call_id, "///"); if ((!callidSlash) && slashes) { return slashes + 3; } return call_id; } static char* get_inet_address(const struct sockaddr_storage* addr, char* dst, int len) { if (getnameinfo(_RCAST(struct sockaddr*, addr), socklen_from_addr(addr), dst, len, NULL, 0, NI_NUMERICHOST) != 0) { snprintf(dst, len, "addr not supported"); } return dst; } static bool process_key(int c) { switch (c) { case '1': currentScreenToDisplay = DISPLAY_SCENARIO_SCREEN; print_statistics(0); break; case '2': currentScreenToDisplay = DISPLAY_STAT_SCREEN; print_statistics(0); break; case '3': currentScreenToDisplay = DISPLAY_REPARTITION_SCREEN; print_statistics(0); break; case '4': currentScreenToDisplay = DISPLAY_VARIABLE_SCREEN; print_statistics(0); break; case '5': if (use_tdmmap) { currentScreenToDisplay = DISPLAY_TDM_MAP_SCREEN; print_statistics(0); } break; /* Screens 6, 7, 8, 9 are for the extra RTD repartitions. */ case '6': case '7': case '8': case '9': currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN; currentRepartitionToDisplay = (c - '6') + 2; print_statistics(0); break; case '+': if (users >= 0) { CallGenerationTask::set_users((int)(users + 1 * rate_scale)); } else { CallGenerationTask::set_rate(rate + 1 * rate_scale); } print_statistics(0); break; case '-': if (users >= 0) { CallGenerationTask::set_users((int)(users - 1 * rate_scale)); } else { CallGenerationTask::set_rate(rate - 1 * rate_scale); } print_statistics(0); break; case '*': if (users >= 0) { CallGenerationTask::set_users((int)(users + 10 * rate_scale)); } else { CallGenerationTask::set_rate(rate + 10 * rate_scale); } print_statistics(0); break; case '/': if (users >= 0) { CallGenerationTask::set_users((int)(users - 10 * rate_scale)); } else { CallGenerationTask::set_rate(rate - 10 * rate_scale); } print_statistics(0); break; case 'p': if (paused) { CallGenerationTask::set_paused(false); } else { CallGenerationTask::set_paused(true); } print_statistics(0); break; case 's': if (screenf) { print_screens(); } break; case 'q': quitting+=10; print_statistics(0); break; case 'Q': /* We are going to break, so we never have a chance to press q twice. */ quitting+=20; print_statistics(0); break; } return false; } int handle_ctrl_socket() { unsigned char bufrcv [SIPP_MAX_MSG_SIZE]; int ret = recv(ctrl_socket->ss_fd, bufrcv, sizeof(bufrcv) - 1, 0); if (ret <= 0) { return ret; } if (bufrcv[0] == 'c') { /* No 'c', but we need one for '\0'. */ char *command = (char *)malloc(ret); if (!command) { ERROR("Out of memory allocated command buffer."); } memcpy(command, bufrcv + 1, ret - 1); command[ret - 1] = '\0'; process_command(command); free(command); } else { process_key(bufrcv[0]); } return 0; } void setup_ctrl_socket() { int port, firstport; int try_counter = 60; struct sockaddr_storage ctl_sa; int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock == -1) { ERROR_NO("Unable to create remote control socket!"); } if (control_port) { port = control_port; /* If the user specified the control port, then we must assume they know * what they want, and should not cycle. */ try_counter = 1; } else { /* Allow 60 control sockets on the same system */ /* (several SIPp instances) */ port = DEFAULT_CTRL_SOCKET_PORT; } firstport = port; memset(&ctl_sa, 0, sizeof(struct sockaddr_storage)); if (control_ip[0]) { if (gai_getsockaddr(&ctl_sa, control_ip, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown control address '%s'.\n" "Use 'sipp -h' for details", control_ip); } } else { ((struct sockaddr_in *)&ctl_sa)->sin_family = AF_INET; ((struct sockaddr_in *)&ctl_sa)->sin_addr.s_addr = INADDR_ANY; } while (try_counter) { ((struct sockaddr_in *)&ctl_sa)->sin_port = htons(port); if (!::bind(sock, (struct sockaddr *)&ctl_sa, sizeof(struct sockaddr_in))) { /* Bind successful */ break; } try_counter--; port++; } if (try_counter == 0) { if (control_port) { ERROR_NO("Unable to bind remote control socket to UDP port %d", control_port); } else { WARNING("Unable to bind remote control socket (tried UDP ports %d-%d): %s", firstport, port - 1, strerror(errno)); } return; } ctrl_socket = new SIPpSocket(0, T_UDP, sock, 0); if (!ctrl_socket) { ERROR_NO("Could not setup control socket!"); } } void reset_stdin() { fcntl(stdin_fileno, F_SETFL, stdin_mode); } void setup_stdin_socket() { stdin_fileno = fileno(stdin); stdin_mode = fcntl(stdin_fileno, F_GETFL); atexit(reset_stdin); fcntl(stdin_fileno, F_SETFL, stdin_mode | O_NONBLOCK); stdin_socket = new SIPpSocket(0, T_TCP, stdin_fileno, 0); if (!stdin_socket) { ERROR_NO("Could not setup keyboard (stdin) socket!"); } } #define SIPP_ENDL "\r\n" void handle_stdin_socket() { int c; int chars = 0; if (feof(stdin)) { stdin_socket->close(); stdin_socket = NULL; return; } while (((c = screen_readkey()) != -1)) { chars++; if (command_mode) { if (c == '\n') { bool quit = process_command(command_buffer); if (quit) { return; } command_buffer[0] = '\0'; command_mode = 0; } #ifndef __SUNOS else if (c == key_backspace || c == key_dc) #else else if (c == 14) #endif { int command_len = strlen(command_buffer); if (command_len > 0) { command_buffer[command_len--] = '\0'; } } else { int command_len = strlen(command_buffer); char *realloc_ptr = (char *)realloc(command_buffer, command_len + 2); if (realloc_ptr) { command_buffer = realloc_ptr; } else { free(command_buffer); ERROR("Out of memory"); return; } command_buffer[command_len++] = c; command_buffer[command_len] = '\0'; putchar(c); fflush(stdout); } } else if (c == 'c') { command_mode = 1; char *realloc_ptr = (char *)realloc(command_buffer, 1); if (realloc_ptr) { command_buffer = realloc_ptr; } else { free(command_buffer); ERROR("Out of memory"); return; } command_buffer[0] = '\0'; } else { process_key(c); } } if (chars == 0) { /* We did not read any characters, even though we should have. */ stdin_socket->close(); stdin_socket = NULL; } } /****************************** Network Interface *******************/ /* Our message detection states: */ #define CFM_NORMAL 0 /* No CR Found, searchign for \r\n\r\n. */ #define CFM_CONTROL 1 /* Searching for 27 */ #define CFM_CR 2 /* CR Found, Searching for \n\r\n */ #define CFM_CRLF 3 /* CRLF Found, Searching for \r\n */ #define CFM_CRLFCR 4 /* CRLFCR Found, Searching for \n */ #define CFM_CRLFCRLF 5 /* We've found the end of the headers! */ static void merge_socketbufs(struct socketbuf* socketbuf) { struct socketbuf *next = socketbuf->next; int newsize; char *newbuf; if (!next) { return; } if (next->offset) { ERROR("Internal error: can not merge a socketbuf with a non-zero offset."); } if (socketbuf->offset) { memmove(socketbuf->buf, socketbuf->buf + socketbuf->offset, socketbuf->len - socketbuf->offset); socketbuf->len -= socketbuf->offset; socketbuf->offset = 0; } newsize = socketbuf->len + next->len; newbuf = (char *)realloc(socketbuf->buf, newsize); if (!newbuf) { ERROR("Could not allocate memory to merge socket buffers!"); } memcpy(newbuf + socketbuf->len, next->buf, next->len); socketbuf->buf = newbuf; socketbuf->len = newsize; socketbuf->next = next->next; free_socketbuf(next); } /* Check for a message in the socket and return the length of the first * message. If this is UDP, the only check is if we have buffers. If this is * TCP or TLS we need to parse out the content-length. */ int SIPpSocket::check_for_message() { struct socketbuf *socketbuf = ss_in; int state = ss_control ? CFM_CONTROL : CFM_NORMAL; const char *l; if (!socketbuf) return 0; if (ss_transport == T_UDP || ss_transport == T_SCTP) { return socketbuf->len; } int len = 0; while (socketbuf->offset + len < socketbuf->len) { char c = socketbuf->buf[socketbuf->offset + len]; switch(state) { case CFM_CONTROL: /* For CMD Message the escape char is the end of message */ if (c == 27) { return len + 1; /* The plus one includes the control character. */ } break; case CFM_NORMAL: if (c == '\r') { state = CFM_CR; } break; case CFM_CR: if (c == '\n') { state = CFM_CRLF; } else { state = CFM_NORMAL; } break; case CFM_CRLF: if (c == '\r') { state = CFM_CRLFCR; } else { state = CFM_NORMAL; } break; case CFM_CRLFCR: if (c == '\n') { state = CFM_CRLFCRLF; } else { state = CFM_NORMAL; } break; } /* Head off failing because the buffer does not contain the whole header. */ if (socketbuf->offset + len == socketbuf->len - 1) { merge_socketbufs(socketbuf); } if (state == CFM_CRLFCRLF) { break; } len++; } /* We did not find the end-of-header marker. */ if (state != CFM_CRLFCRLF) { return 0; } char saved = socketbuf->buf[socketbuf->offset + len]; socketbuf->buf[socketbuf->offset + len] = '\0'; /* Find the content-length header. */ if ((l = strcasestr(socketbuf->buf + socketbuf->offset, "\r\nContent-Length:"))) { l += strlen("\r\nContent-Length:"); } else if ((l = strcasestr(socketbuf->buf + socketbuf->offset, "\r\nl:"))) { l += strlen("\r\nl:"); } socketbuf->buf[socketbuf->offset + len] = saved; /* There is no header, so the content-length is zero. */ if (!l) return len + 1; /* Skip spaces. */ while (isspace(*l)) { if (*l == '\r' || *l == '\n') { /* We ran into an end-of-line, so there is no content-length. */ return len + 1; } l++; } /* Do the integer conversion, we only allow '\r' or spaces after the integer. */ char *endptr; int content_length = strtol(l, &endptr, 10); if (*endptr != '\r' && !isspace(*endptr)) { content_length = 0; } /* Now that we know how large this message is, we make sure we have the whole thing. */ do { /* It is in this buffer. */ if (socketbuf->offset + len + content_length < socketbuf->len) { return len + content_length + 1; } if (socketbuf->next == NULL) { /* There is no buffer to merge, so we fail. */ return 0; } /* We merge ourself with the next buffer. */ merge_socketbufs(socketbuf); } while (1); } #ifdef USE_SCTP int SIPpSocket::handleSCTPNotify(char* buffer) { union sctp_notification *notifMsg; notifMsg = (union sctp_notification *)buffer; TRACE_MSG("SCTP Notification: %d\n", ntohs(notifMsg->sn_header.sn_type)); if (notifMsg->sn_header.sn_type == SCTP_ASSOC_CHANGE) { TRACE_MSG("SCTP_ASSOC_CHANGE\n"); if (notifMsg->sn_assoc_change.sac_state == SCTP_COMM_UP) { TRACE_MSG("SCTP_COMM_UP\n"); sctpstate = SCTP_UP; sipp_sctp_peer_params(); /* Send SCTP message right after association is up */ ss_congested = false; flush(); return -2; } else { TRACE_MSG("else: %d\n", notifMsg->sn_assoc_change.sac_state); return 0; } } else if (notifMsg->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { TRACE_MSG("SCTP_SHUTDOWN_EVENT\n"); return 0; } return -2; } void set_multihome_addr(SIPpSocket* socket, int port) { if (strlen(multihome_ip)>0) { struct sockaddr_storage secondaryaddress; if (gai_getsockaddr(&secondaryaddress, multihome_ip, port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Can't get multihome IP address in getaddrinfo, multihome_ip='%s'", multihome_ip); } int ret = sctp_bindx(socket->ss_fd, (struct sockaddr *) &secondaryaddress, 1, SCTP_BINDX_ADD_ADDR); if (ret < 0) { WARNING("Can't bind to multihome address, errno='%d'", errno); } } } #endif /* Pull up to tcp_readsize data bytes out of the socket into our local buffer. */ int SIPpSocket::empty() { int readsize=0; if (ss_transport == T_UDP || ss_transport == T_SCTP) { readsize = SIPP_MAX_MSG_SIZE; } else { readsize = tcp_readsize; } struct socketbuf *socketbuf; char *buffer; int ret = -1; /* Where should we start sending packets to, ideally we should begin to parse * the Via, Contact, and Route headers. But for now SIPp always sends to the * host specified on the command line; or for UAS mode to the address that * sent the last message. */ sipp_socklen_t addrlen = sizeof(struct sockaddr_storage); buffer = (char *)malloc(readsize); if (!buffer) { ERROR("Could not allocate memory for read!"); } socketbuf = alloc_socketbuf(buffer, readsize, NO_COPY, NULL); switch(ss_transport) { case T_TCP: case T_UDP: ret = recvfrom(ss_fd, buffer, readsize, 0, (struct sockaddr *)&socketbuf->addr, &addrlen); break; case T_TLS: #ifdef USE_OPENSSL ret = SSL_read(ss_ssl, buffer, readsize); /* XXX: Check for clean shutdown. */ #else ERROR("TLS support is not enabled!"); #endif break; case T_SCTP: #ifdef USE_SCTP struct sctp_sndrcvinfo recvinfo; memset(&recvinfo, 0, sizeof(recvinfo)); int msg_flags = 0; ret = sctp_recvmsg(ss_fd, (void*)buffer, readsize, (struct sockaddr *) &socketbuf->addr, &addrlen, &recvinfo, &msg_flags); if (MSG_NOTIFICATION & msg_flags) { errno = 0; handleSCTPNotify(buffer); ret = -2; } #else ERROR("SCTP support is not enabled!"); #endif break; } if (ret <= 0) { free_socketbuf(socketbuf); return ret; } socketbuf->len = ret; buffer_read(socketbuf); /* Do we have a complete SIP message? */ if (!ss_msglen) { if (int msg_len = check_for_message()) { ss_msglen = msg_len; pending_messages++; } } return ret; } void SIPpSocket::invalidate() { unsigned pollidx; if (ss_invalid) { return; } #ifdef USE_OPENSSL if (SSL *ssl = ss_ssl) { SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); SSL_free(ssl); } #endif /* In some error conditions, the socket FD has already been closed - if it hasn't, do so now. */ if (ss_fd != -1) { #ifdef HAVE_EPOLL int rc = epoll_ctl(epollfd, EPOLL_CTL_DEL, ss_fd, NULL); if (rc == -1) { WARNING_NO("Failed to delete FD from epoll"); } #endif } if (ss_fd != -1 && ss_fd != stdin_fileno) { if (ss_transport != T_UDP) { if (shutdown(ss_fd, SHUT_RDWR) < 0) { WARNING_NO("Failed to shutdown socket %d", ss_fd); } } #ifdef USE_SCTP if (ss_transport == T_SCTP && !gracefulclose) { struct linger ling = {1, 0}; if (setsockopt(ss_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)) < 0) { WARNING("Unable to set SO_LINGER option for SCTP close"); } } #endif if (::close(ss_fd) < 0) { WARNING_NO("Failed to close socket %d", ss_fd); } } if ((pollidx = ss_pollidx) >= pollnfds) { ERROR("Pollset error: index %d is greater than number of fds %d!", pollidx, pollnfds); } ss_fd = -1; ss_invalid = true; ss_pollidx = -1; /* Adds call sockets in the array */ assert(pollnfds > 0); pollnfds--; #ifdef HAVE_EPOLL if (pollidx < pollnfds) { epollfiles[pollidx] = epollfiles[pollnfds]; epollfiles[pollidx].data.u32 = pollidx; if (sockets[pollnfds]->ss_fd != -1) { int rc = epoll_ctl(epollfd, EPOLL_CTL_MOD, sockets[pollnfds]->ss_fd, &epollfiles[pollidx]); if ((rc == -1) && (errno != EPERM)) { // Ignore "Operation not supported" errors - // otherwise we get log spam when redirecting stdout // to /dev/null WARNING_NO("Failed to update FD within epoll"); } } } #else pollfiles[pollidx] = pollfiles[pollnfds]; #endif /* If unequal, move the last valid socket here. */ if (pollidx != pollnfds) { sockets[pollidx] = sockets[pollnfds]; sockets[pollidx]->ss_pollidx = pollidx; } sockets[pollnfds] = NULL; if (ss_msglen) { pending_messages--; } #ifdef USE_SCTP if (ss_transport == T_SCTP) { sctpstate = SCTP_DOWN; } #endif } void SIPpSocket::abort() { /* Disable linger - we'll send a RST when we close. */ struct linger flush; flush.l_onoff = 1; flush.l_linger = 0; setsockopt(ss_fd, SOL_SOCKET, SO_LINGER, &flush, sizeof(flush)); /* Mark the socket as non-blocking. It's not clear whether this is required but can't hurt. */ int flags = fcntl(ss_fd, F_GETFL, 0); fcntl(ss_fd, F_SETFL, flags | O_NONBLOCK); int count = --ss_count; if (count == 0) { invalidate(); sockets_pending_reset.erase(this); delete this; } else { ss_fd = -1; } } void SIPpSocket::close() { int count = --ss_count; if (count == 0) { invalidate(); sockets_pending_reset.erase(this); delete this; } } ssize_t SIPpSocket::read_message(char *buf, size_t len, struct sockaddr_storage *src) { size_t avail; if (!ss_msglen) return 0; if (ss_msglen > len) ERROR("There is a message waiting in sockfd(%d) that is bigger (%zu bytes) than the read size.", ss_fd, ss_msglen); len = ss_msglen; avail = ss_in->len - ss_in->offset; if (avail > len) { avail = len; } memcpy(buf, ss_in->buf + ss_in->offset, avail); memcpy(src, &ss_in->addr, sizeof(ss_in->addr)); /* Update our buffer and return value. */ buf[avail] = '\0'; /* For CMD Message the escape char is the end of message */ if ((ss_control) && buf[avail-1] == 27) buf[avail-1] = '\0'; ss_in->offset += avail; /* Have we emptied the buffer? */ if (ss_in->offset == ss_in->len) { struct socketbuf *next = ss_in->next; free_socketbuf(ss_in); ss_in = next; } if (int msg_len = check_for_message()) { ss_msglen = msg_len; } else { ss_msglen = 0; pending_messages--; } return avail; } void process_message(SIPpSocket *socket, char *msg, ssize_t msg_size, struct sockaddr_storage *src) { // TRACE_MSG(" msg_size %d and pollset_index is %d \n", msg_size, pollset_index)); if (msg_size <= 0) { return; } if (sipMsgCheck(msg, socket) == false) { if (msg_size == 4 && (memcmp(msg, "\r\n\r\n", 4) == 0 || memcmp(msg, "\x00\x00\x00\x00", 4) == 0)) { /* Common keepalives */; } else { WARNING("non SIP message discarded: \"%.*s\" (%zu)", (int)msg_size, msg, msg_size); } return; } const char *call_id = get_trimmed_call_id(msg); if (call_id[0] == '\0') { WARNING("SIP message without Call-ID discarded"); return; } listener *listener_ptr = get_listener(call_id); struct timeval currentTime; GET_TIME (¤tTime); if (useShortMessagef == 1) { TRACE_SHORTMSG("%s\tR\t%s\tCSeq:%s\t%s\n", CStat::formatTime(¤tTime), call_id, get_header_content(msg, "CSeq:"), get_first_line(msg)); } if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "%s %smessage received [%zu] bytes :\n\n%s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(socket->ss_transport), socket->ss_control ? "control " : "", msg_size, msg); } if (!listener_ptr) { if (thirdPartyMode == MODE_3PCC_CONTROLLER_B || thirdPartyMode == MODE_3PCC_A_PASSIVE || thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_SLAVE) { // Adding a new OUTGOING call ! main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL); call *new_ptr = new call(call_id, local_ip_is_ipv6, 0, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr); if (!new_ptr) { ERROR("Out of memory allocating a call!"); } outbound_congestion = false; if ((socket != main_socket) && (socket != tcp_multiplex) && (socket != localTwinSippSocket) && (socket != twinSippSocket) && (!is_a_local_socket(socket))) { new_ptr->associate_socket(socket); socket->ss_count++; } else { /* We need to hook this call up to a real *call* socket. */ if (!multisocket) { switch(transport) { case T_UDP: new_ptr->associate_socket(main_socket); main_socket->ss_count++; break; case T_TCP: case T_SCTP: case T_TLS: new_ptr->associate_socket(tcp_multiplex); tcp_multiplex->ss_count++; break; } } } listener_ptr = new_ptr; } else if (creationMode == MODE_SERVER) { if (quitting >= 1) { CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); TRACE_MSG("Discarded message for new calls while quitting\n"); return; } // Adding a new INCOMING call ! main_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); listener_ptr = new call(call_id, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src); if (!listener_ptr) { ERROR("Out of memory allocating a call!"); } } else { // mode != from SERVER and 3PCC Controller B // This is a message that is not relating to any known call if (ooc_scenario) { if (!get_reply_code(msg)) { char *msg_start = strdup(msg); char *msg_start_end = msg_start; while (!isspace(*msg_start_end) && (*msg_start_end != '\0')) { msg_start_end++; } *msg_start_end = '\0'; ooc_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); WARNING("Received out-of-call %s message, using the out-of-call scenario", msg_start); free(msg_start); /* This should have the real address that the message came from. */ call *call_ptr = new call(ooc_scenario, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src, call_id, 0 /* no user. */, socket->ss_ipv6, true, false); if (!call_ptr) { ERROR("Out of memory allocating a call!"); } CStat::globalStat(CStat::E_AUTO_ANSWERED); call_ptr->process_incoming(msg, src); } else { /* We received a response not relating to any known call */ /* Do nothing, even if in auto answer mode */ CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); } } else if (auto_answer && ((strstr(msg, "INFO") == msg) || (strstr(msg, "NOTIFY") == msg) || (strstr(msg, "OPTIONS") == msg) || (strstr(msg, "UPDATE") == msg))) { // If auto answer mode, try to answer the incoming message // with automaticResponseMode // call is discarded before exiting the block if (!get_reply_code(msg)) { aa_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL); /* This should have the real address that the message came from. */ call *call_ptr = new call(aa_scenario, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src, call_id, 0 /* no user. */, socket->ss_ipv6, true, false); if (!call_ptr) { ERROR("Out of memory allocating a call!"); } CStat::globalStat(CStat::E_AUTO_ANSWERED); call_ptr->process_incoming(msg, src); } else { fprintf(stderr, "%s", msg); /* We received a response not relating to any known call */ /* Do nothing, even if in auto answer mode */ CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); } } else { CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS); WARNING("Discarding message which can't be mapped to a known SIPp call:\n%s", msg); } } } /* If the call was not created above, we just drop this message. */ if (!listener_ptr) { return; } if ((socket == localTwinSippSocket) || (socket == twinSippSocket) || (is_a_local_socket(socket))) { listener_ptr -> process_twinSippCom(msg); } else { listener_ptr -> process_incoming(msg, src); } } SIPpSocket::SIPpSocket(bool use_ipv6, int transport, int fd, int accepting): ss_count(1), ss_ipv6(use_ipv6), ss_transport(transport), ss_control(false), ss_fd(fd), ss_bind_port(0), ss_comp_state(NULL), ss_changed_dest(false), ss_congested(false), ss_invalid(false), ss_in(NULL), ss_out(NULL), ss_out_tail(NULL), ss_msglen(0) { /* Initialize all sockets with our destination address. */ memcpy(&ss_dest, &remote_sockaddr, sizeof(ss_dest)); #ifdef USE_OPENSSL ss_ssl = NULL; if (transport == T_TLS) { if ((ss_bio = BIO_new_socket(fd, BIO_NOCLOSE)) == NULL) { ERROR("Unable to create BIO object:Problem with BIO_new_socket()"); } if (!(ss_ssl = (accepting ? SSL_new_server() : SSL_new_client()))) { ERROR("Unable to create SSL object : Problem with SSL_new()"); } SSL_set_bio(ss_ssl, ss_bio, ss_bio); } #endif /* Store this socket in the tables. */ ss_pollidx = pollnfds++; sockets[ss_pollidx] = this; #ifdef HAVE_EPOLL epollfiles[ss_pollidx].data.u32 = ss_pollidx; epollfiles[ss_pollidx].events = EPOLLIN; int rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, ss_fd, &epollfiles[ss_pollidx]); if (rc == -1) { if (errno == EPERM) { // Attempted to use epoll on a file that does not support // it - this may happen legitimately when stdin/stdout is // redirected to /dev/null, so don't warn } else { ERROR_NO("Failed to add FD to epoll"); } } #else pollfiles[ss_pollidx].fd = ss_fd; pollfiles[ss_pollidx].events = POLLIN | POLLERR; pollfiles[ss_pollidx].revents = 0; #endif } static SIPpSocket* sipp_allocate_socket(bool use_ipv6, int transport, int fd) { return new SIPpSocket(use_ipv6, transport, fd, 0); } static int socket_fd(bool use_ipv6, int transport) { int socket_type = -1; int protocol = 0; int fd; switch(transport) { case T_UDP: socket_type = SOCK_DGRAM; protocol = IPPROTO_UDP; break; case T_SCTP: #ifndef USE_SCTP ERROR("You do not have SCTP support enabled!"); #else socket_type = SOCK_STREAM; protocol = IPPROTO_SCTP; #endif break; case T_TLS: #ifndef USE_OPENSSL ERROR("You do not have TLS support enabled!"); #endif case T_TCP: socket_type = SOCK_STREAM; protocol = IPPROTO_TCP; break; } if ((fd = socket(use_ipv6 ? AF_INET6 : AF_INET, socket_type, protocol))== -1) { ERROR_NO("Unable to get a %s socket (3)", TRANSPORT_TO_STRING(transport)); } return fd; } SIPpSocket *new_sipp_socket(bool use_ipv6, int transport) { SIPpSocket *ret; int fd = socket_fd(use_ipv6, transport); ret = sipp_allocate_socket(use_ipv6, transport, fd); if (!ret) { close(fd); ERROR("Could not allocate new socket structure!"); } return ret; } SIPpSocket* SIPpSocket::new_sipp_call_socket(bool use_ipv6, int transport, bool *existing) { SIPpSocket *sock = NULL; static int next_socket; if (pollnfds >= max_multi_socket) { // we must take the main socket into account /* Find an existing socket that matches transport and ipv6 parameters. */ int first = next_socket; do { int test_socket = next_socket; next_socket = (next_socket + 1) % pollnfds; if (sockets[test_socket]->ss_call_socket) { /* Here we need to check that the address is the default. */ if (sockets[test_socket]->ss_ipv6 != use_ipv6) { continue; } if (sockets[test_socket]->ss_transport != transport) { continue; } if (sockets[test_socket]->ss_changed_dest) { continue; } sock = sockets[test_socket]; sock->ss_count++; *existing = true; break; } } while (next_socket != first); if (next_socket == first) { ERROR("Could not find an existing call socket to re-use!"); } } else { sock = new_sipp_socket(use_ipv6, transport); sock->ss_call_socket = true; *existing = false; } return sock; } SIPpSocket* SIPpSocket::accept() { SIPpSocket *ret; struct sockaddr_storage remote_sockaddr; int fd; sipp_socklen_t addrlen = sizeof(remote_sockaddr); if ((fd = ::accept(ss_fd, (struct sockaddr *)&remote_sockaddr, &addrlen))== -1) { ERROR("Unable to accept on a %s socket: %s", TRANSPORT_TO_STRING(transport), strerror(errno)); } #if defined(__SUNOS) if (fd < 256) { int newfd = fcntl(fd, F_DUPFD, 256); if (newfd <= 0) { // Typically, (24)(Too many open files) is the error here WARNING("Unable to get a different %s socket, errno=%d(%s)", TRANSPORT_TO_STRING(transport), errno, strerror(errno)); // Keep the original socket fd. newfd = fd; } else { ::close(fd); } fd = newfd; } #endif ret = new SIPpSocket(ss_ipv6, ss_transport, fd, 1); if (!ret) { ::close(fd); ERROR_NO("Could not allocate new socket!"); } /* We should connect back to the address which connected to us if we * experience a TCP failure. */ memcpy(&ret->ss_dest, &remote_sockaddr, sizeof(ret->ss_dest)); if (ret->ss_transport == T_TLS) { #ifdef USE_OPENSSL int rc; int i = 0; while ((rc = SSL_accept(ret->ss_ssl)) < 0) { int err = SSL_get_error(ret->ss_ssl, rc); if ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) && i < SIPP_SSL_MAX_RETRIES) { /* These errors are benign we just need to wait for the socket * to be readable/writable again. */ WARNING("SSL_accept failed with error: %s. Attempt %d. " "Retrying...", SSL_error_string(err, rc), ++i); sipp_usleep(SIPP_SSL_RETRY_TIMEOUT); continue; } ERROR("Error in SSL_accept: %s", SSL_error_string(err, rc)); break; } #else ERROR("You need to compile SIPp with TLS support"); #endif } return ret; } int sipp_bind_socket(SIPpSocket *socket, struct sockaddr_storage *saddr, int *port) { int ret; int len; #ifdef USE_SCTP if (transport == T_SCTP && multisocket == 1 && port && *port == -1) { sockaddr_update_port(saddr, 0); } #endif if (socket->ss_ipv6) { len = sizeof(struct sockaddr_in6); } else { len = sizeof(struct sockaddr_in); } if ((ret = ::bind(socket->ss_fd, (sockaddr *)saddr, len))) { return ret; } if (!port) { return 0; } if ((ret = getsockname(socket->ss_fd, (sockaddr *)saddr, (sipp_socklen_t *) &len))) { return ret; } if (socket->ss_ipv6) { socket->ss_port = ntohs((short)((_RCAST(struct sockaddr_in6 *, saddr))->sin6_port)); } else { socket->ss_port = ntohs((short)((_RCAST(struct sockaddr_in *, saddr))->sin_port)); } *port = socket->ss_port; #ifdef USE_SCTP if (transport == T_SCTP) { bool isany = false; if (socket->ss_ipv6) { if (memcmp(&(_RCAST(struct sockaddr_in6 *, saddr)->sin6_addr), &in6addr_any, sizeof(in6_addr)) == 0) isany = true; } else { isany = (_RCAST(struct sockaddr_in *, saddr)->sin_addr.s_addr == INADDR_ANY); } if (!isany) { set_multihome_addr(socket, *port); } } #endif return 0; } void SIPpSocket::set_bind_port(int bind_port) { ss_bind_port = bind_port; } int SIPpSocket::connect(struct sockaddr_storage* dest) { if (dest) { memcpy(&ss_dest, dest, sizeof(*dest)); } int ret; assert(ss_transport == T_TCP || ss_transport == T_TLS || ss_transport == T_SCTP); if (ss_transport == T_TCP || ss_transport == T_TLS) { struct sockaddr_storage with_optional_port; int port = -1; memcpy(&with_optional_port, &local_sockaddr, sizeof(struct sockaddr_storage)); if (local_ip_is_ipv6) { (_RCAST(struct sockaddr_in6*, &with_optional_port))->sin6_port = htons(ss_bind_port); } else { (_RCAST(struct sockaddr_in*, &with_optional_port))->sin_port = htons(ss_bind_port); } sipp_bind_socket(this, &with_optional_port, &port); #ifdef USE_SCTP } else if (ss_transport == T_SCTP) { int port = -1; sipp_bind_socket(this, &local_sockaddr, &port); #endif } int flags = fcntl(ss_fd, F_GETFL, 0); fcntl(ss_fd, F_SETFL, flags | O_NONBLOCK); errno = 0; ret = ::connect(ss_fd, _RCAST(struct sockaddr *, &ss_dest), socklen_from_addr(&ss_dest)); if (ret < 0) { if (errno == EINPROGRESS) { /* Block this socket until the connect completes - this is very similar to entering congestion, but we don't want to increment congestion statistics. */ enter_congestion(0); nb_net_cong--; } else { return ret; } } fcntl(ss_fd, F_SETFL, flags); if (ss_transport == T_TLS) { #ifdef USE_OPENSSL int rc; int i = 0; while ((rc = SSL_connect(ss_ssl)) < 0) { int err = SSL_get_error(ss_ssl, rc); if ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) && i < SIPP_SSL_MAX_RETRIES) { /* These errors are benign we just need to wait for the socket * to be readable/writable again. */ WARNING("SSL_connect failed with error: %s. Attempt %d. " "Retrying...", SSL_error_string(err, rc), ++i); sipp_usleep(SIPP_SSL_RETRY_TIMEOUT); continue; } WARNING("Error in SSL connection: %s", SSL_error_string(err, rc)); invalidate(); return err; } #else ERROR("You need to compile SIPp with TLS support"); #endif } #ifdef USE_SCTP if (ss_transport == T_SCTP) { sctpstate = SCTP_CONNECTING; } #endif return 0; } int SIPpSocket::reconnect() { if ((!ss_invalid) && (ss_fd != -1)) { WARNING("When reconnecting socket, already have file descriptor %d", ss_fd); abort(); } ss_fd = socket_fd(ss_ipv6, ss_transport); if (ss_fd == -1) { ERROR_NO("Could not obtain new socket: "); } if (ss_invalid) { #ifdef USE_OPENSSL ss_ssl = NULL; if (transport == T_TLS) { if ((ss_bio = BIO_new_socket(ss_fd, BIO_NOCLOSE)) == NULL) { ERROR("Unable to create BIO object:Problem with BIO_new_socket()"); } if (!(ss_ssl = SSL_new_client())) { ERROR("Unable to create SSL object : Problem with SSL_new()"); } SSL_set_bio(ss_ssl, ss_bio, ss_bio); } #endif /* Store this socket in the tables. */ ss_pollidx = pollnfds++; sockets[ss_pollidx] = this; #ifdef HAVE_EPOLL epollfiles[ss_pollidx].data.u32 = ss_pollidx; epollfiles[ss_pollidx].events = EPOLLIN; #else pollfiles[ss_pollidx].fd = ss_fd; pollfiles[ss_pollidx].events = POLLIN | POLLERR; pollfiles[ss_pollidx].revents = 0; #endif ss_invalid = false; } #ifdef HAVE_EPOLL int rc = epoll_ctl(epollfd, EPOLL_CTL_ADD, ss_fd, &epollfiles[ss_pollidx]); if (rc == -1) { ERROR_NO("Failed to add FD to epoll"); } #endif return connect(); } /*************************** I/O functions ***************************/ /* Allocate a socket buffer. */ struct socketbuf *alloc_socketbuf(char *buffer, size_t size, int copy, struct sockaddr_storage *dest) { struct socketbuf *socketbuf; socketbuf = (struct socketbuf *)malloc(sizeof(struct socketbuf)); if (!socketbuf) { ERROR("Could not allocate socket buffer!"); } memset(socketbuf, 0, sizeof(struct socketbuf)); if (copy) { socketbuf->buf = (char *)malloc(size); if (!socketbuf->buf) { ERROR("Could not allocate socket buffer data!"); } memcpy(socketbuf->buf, buffer, size); } else { socketbuf->buf = buffer; } socketbuf->len = size; socketbuf->offset = 0; if (dest) { memcpy(&socketbuf->addr, dest, sizeof(*dest)); } socketbuf->next = NULL; return socketbuf; } /* Free a poll buffer. */ void free_socketbuf(struct socketbuf *socketbuf) { free(socketbuf->buf); free(socketbuf); } #ifdef USE_SCTP void SIPpSocket::sipp_sctp_peer_params() { if (heartbeat > 0 || pathmaxret > 0) { struct sctp_paddrparams peerparam; memset(&peerparam, 0, sizeof(peerparam)); sockaddr* addresses; #ifdef __SUNOS /* Sun takes a void** instead of a struct sockaddr** */ int addresscount = sctp_getpaddrs(ss_fd, 0, (void**)&addresses); #else int addresscount = sctp_getpaddrs(ss_fd, 0, &addresses); #endif if (addresscount < 1) WARNING("sctp_getpaddrs, errno=%d", errno); for (int i = 0; i < addresscount; i++) { memset(&peerparam.spp_address, 0, sizeof(peerparam.spp_address)); struct sockaddr_storage* peeraddress = (struct sockaddr_storage*) &addresses[i]; memcpy(&peerparam.spp_address, peeraddress, sizeof(*peeraddress)); peerparam.spp_hbinterval = heartbeat; peerparam.spp_pathmaxrxt = pathmaxret; if (heartbeat > 0) peerparam.spp_flags = SPP_HB_ENABLE; if (pmtu > 0) { peerparam.spp_pathmtu = pmtu; peerparam.spp_flags |= SPP_PMTUD_DISABLE; } if (setsockopt(ss_fd, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &peerparam, sizeof(peerparam)) == -1) { sctp_freepaddrs(addresses); WARNING("setsockopt(SCTP_PEER_ADDR_PARAMS) failed, errno=%d", errno); } } sctp_freepaddrs(addresses); } } #endif void sipp_customize_socket(SIPpSocket *socket) { unsigned int buffsize = buff_size; /* Allows fast TCP reuse of the socket */ if (socket->ss_transport == T_TCP || socket->ss_transport == T_TLS || socket->ss_transport == T_SCTP) { int sock_opt = 1; if (setsockopt(socket->ss_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt)) == -1) { ERROR_NO("setsockopt(SO_REUSEADDR) failed"); } #ifdef USE_SCTP if (socket->ss_transport == T_SCTP) { struct sctp_event_subscribe event; memset(&event, 0, sizeof(event)); event.sctp_data_io_event = 1; event.sctp_association_event = 1; event.sctp_shutdown_event = 1; if (setsockopt(socket->ss_fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) == -1) { ERROR_NO("setsockopt(SCTP_EVENTS) failed, errno=%d", errno); } if (assocmaxret > 0) { struct sctp_assocparams associnfo; memset(&associnfo, 0, sizeof(associnfo)); associnfo.sasoc_asocmaxrxt = assocmaxret; if (setsockopt(socket->ss_fd, IPPROTO_SCTP, SCTP_ASSOCINFO, &associnfo, sizeof(associnfo)) == -1) { WARNING("setsockopt(SCTP_ASSOCINFO) failed, errno=%d", errno); } } if (setsockopt(socket->ss_fd, IPPROTO_SCTP, SCTP_NODELAY, (void *)&sock_opt, sizeof (sock_opt)) == -1) { WARNING("setsockopt(SCTP_NODELAY) failed, errno=%d", errno); } } #endif #ifndef SOL_TCP #define SOL_TCP 6 #endif if (socket->ss_transport != T_SCTP) { if (setsockopt(socket->ss_fd, SOL_TCP, TCP_NODELAY, (void *)&sock_opt, sizeof (sock_opt)) == -1) { { ERROR_NO("setsockopt(TCP_NODELAY) failed"); } } } { struct linger linger; linger.l_onoff = 1; linger.l_linger = 1; if (setsockopt (socket->ss_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof (linger)) < 0) { ERROR_NO("Unable to set SO_LINGER option"); } } } /* Increase buffer sizes for this sockets */ if (setsockopt(socket->ss_fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize))) { ERROR_NO("Unable to set socket sndbuf"); } buffsize = buff_size; if (setsockopt(socket->ss_fd, SOL_SOCKET, SO_RCVBUF, &buffsize, sizeof(buffsize))) { ERROR_NO("Unable to set socket rcvbuf"); } } /* This socket is congested, mark it as such and add it to the poll files. */ int SIPpSocket::enter_congestion(int again) { if (!ss_congested) { nb_net_cong++; } ss_congested = true; TRACE_MSG("Problem %s on socket %d and poll_idx is %d \n", again == EWOULDBLOCK ? "EWOULDBLOCK" : "EAGAIN", ss_fd, ss_pollidx); #ifdef HAVE_EPOLL epollfiles[ss_pollidx].events |= EPOLLOUT; int rc = epoll_ctl(epollfd, EPOLL_CTL_MOD, ss_fd, &epollfiles[ss_pollidx]); if (rc == -1) { WARNING_NO("Failed to set EPOLLOUT"); } #else pollfiles[ss_pollidx].events |= POLLOUT; #endif #ifdef USE_SCTP if (ss_transport == T_SCTP && sctpstate == SCTP_CONNECTING) return 0; #endif return -1; } int SIPpSocket::write_error(int ret) { const char *errstring = strerror(errno); #ifndef EAGAIN int again = (errno == EWOULDBLOCK) ? errno : 0; #else int again = ((errno == EAGAIN) || (errno == EWOULDBLOCK)) ? errno : 0; /* Scrub away EAGAIN from the rest of the code. */ if (errno == EAGAIN) { errno = EWOULDBLOCK; } #endif if (again) { return enter_congestion(again); } if ((ss_transport == T_TCP || ss_transport == T_SCTP) && errno == EPIPE) { nb_net_send_errors++; abort(); sockets_pending_reset.insert(this); if (reconnect_allowed()) { WARNING("Broken pipe on TCP connection, remote peer " "probably closed the socket"); } else { ERROR("Broken pipe on TCP connection, remote peer " "probably closed the socket"); } return -1; } #ifdef USE_OPENSSL if (ss_transport == T_TLS) { errstring = SSL_error_string(SSL_get_error(ss_ssl, ret), ret); } #endif WARNING("Unable to send %s message: %s", TRANSPORT_TO_STRING(ss_transport), errstring); nb_net_send_errors++; return -1; } int SIPpSocket::read_error(int ret) { const char *errstring = strerror(errno); #ifdef USE_OPENSSL if (ss_transport == T_TLS) { int err = SSL_get_error(ss_ssl, ret); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { /* This is benign - we just need to wait for the socket to be * readable/writable again, which will happen naturally as part * of the poll/epoll loop. */ WARNING("SSL_read failed with error: %s. Retrying...", SSL_error_string(err, ret)); return 1; } } #endif assert(ret <= 0); #ifdef EAGAIN /* Scrub away EAGAIN from the rest of the code. */ if (errno == EAGAIN) { errno = EWOULDBLOCK; } #endif /* We have only non-blocking reads, so this should not occur. The OpenSSL * functions don't set errno, though, so this check doesn't make sense * for TLS sockets. */ if (ret < 0 && ss_transport != T_TLS) { assert(errno != EAGAIN); } if (ss_transport == T_TCP || ss_transport == T_TLS) { if (ret == 0) { /* The remote side closed the connection. */ if (ss_control) { if (localTwinSippSocket) localTwinSippSocket->close(); if (extendedTwinSippMode) { close_peer_sockets(); close_local_sockets(); free_peer_addr_map(); WARNING("One of the twin instances has ended -> exiting"); quitting += 20; } else if (twinSippMode) { if (twinSippSocket) twinSippSocket->close(); if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { WARNING("3PCC controller A has ended -> exiting"); quitting += 20; } else { quitting = 1; } } } else { /* The socket was closed "cleanly", but we may have calls that need to * be destroyed. Also, if these calls are not complete, and attempt to * send again we may "ressurect" the socket by reconnecting it.*/ invalidate(); if (reset_close) { close_calls(); } } return 0; } abort(); sockets_pending_reset.insert(this); nb_net_recv_errors++; if (reconnect_allowed()) { WARNING("Error on TCP connection, remote peer probably closed the socket: %s", errstring); } else { ERROR("Error on TCP connection, remote peer probably closed the socket: %s", errstring); } return -1; } WARNING("Unable to receive %s message: %s", TRANSPORT_TO_STRING(ss_transport), errstring); nb_net_recv_errors++; return -1; } void SIPpSocket::buffer_write(const char *buffer, size_t len, struct sockaddr_storage *dest) { struct socketbuf *buf = ss_out; if (!buf) { ss_out = alloc_socketbuf(const_cast(buffer), len, DO_COPY, dest); /* NO BUG BECAUSE OF DO_COPY */ ss_out_tail = ss_out; TRACE_MSG("Added first buffered message to socket %d\n", ss_fd); return; } ss_out_tail->next = alloc_socketbuf(const_cast(buffer), len, DO_COPY, dest); /* NO BUG BECAUSE OF DO_COPY */ ss_out_tail = ss_out_tail->next; TRACE_MSG("Appended buffered message to socket %d\n", ss_fd); } void SIPpSocket::buffer_read(struct socketbuf *newbuf) { struct socketbuf *buf = ss_in; struct socketbuf *prev = buf; if (!buf) { ss_in = newbuf; return; } while (buf->next) { prev = buf; buf = buf->next; } prev->next = newbuf; } #ifdef USE_OPENSSL static int send_nowait_tls(SSL* ssl, const void* msg, int len, int /*flags*/) { int initial_fd_flags; int rc; int fd; int fd_flags; int i = 0; if ((fd = SSL_get_fd(ssl)) == -1) { return -1; } fd_flags = fcntl(fd, F_GETFL, NULL); initial_fd_flags = fd_flags; fd_flags |= O_NONBLOCK; fcntl(fd, F_SETFL, fd_flags); while ((rc = SSL_write(ssl, msg, len)) < 0) { int err = SSL_get_error(ssl, rc); if ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) && i < SIPP_SSL_MAX_RETRIES) { /* These errors are benign we just need to wait for the socket * to be readable/writable again. */ WARNING("SSL_write failed with error: %s. Attempt %d. " "Retrying...", SSL_error_string(err, rc), ++i); sipp_usleep(SIPP_SSL_RETRY_TIMEOUT); continue; } return rc; } if (rc == 0) { return rc; } fcntl(fd, F_SETFL, initial_fd_flags); return rc; } #endif static int send_nowait(int s, const void* msg, int len, int flags) { #if defined(MSG_DONTWAIT) && !defined(__SUNOS) return send(s, msg, len, flags | MSG_DONTWAIT); #else int fd_flags = fcntl(s, F_GETFL , NULL); int initial_fd_flags; int rc; initial_fd_flags = fd_flags; // fd_flags &= ~O_ACCMODE; // Remove the access mode from the value fd_flags |= O_NONBLOCK; fcntl(s, F_SETFL , fd_flags); rc = send(s, msg, len, flags); fcntl(s, F_SETFL , initial_fd_flags); return rc; #endif } #ifdef USE_SCTP int send_sctp_nowait(int s, const void *msg, int len, int flags) { struct sctp_sndrcvinfo sinfo; memset(&sinfo, 0, sizeof(sinfo)); sinfo.sinfo_flags = SCTP_UNORDERED; // according to RFC4168 5.1 sinfo.sinfo_stream = 0; #if defined(MSG_DONTWAIT) && !defined(__SUNOS) return sctp_send(s, msg, len, &sinfo, flags | MSG_DONTWAIT); #else int fd_flags = fcntl(s, F_GETFL, NULL); int initial_fd_flags; int rc; initial_fd_flags = fd_flags; fd_flags |= O_NONBLOCK; fcntl(s, F_SETFL , fd_flags); rc = sctp_send(s, msg, len, &sinfo, flags); fcntl(s, F_SETFL, initial_fd_flags); return rc; #endif } #endif ssize_t SIPpSocket::write_primitive(const char* buffer, size_t len, struct sockaddr_storage* dest) { ssize_t rc; /* Refuse to write to invalid sockets. */ if (ss_invalid) { WARNING("Returning EPIPE on invalid socket: %p (%d)", _RCAST(void*, this), ss_fd); errno = EPIPE; return -1; } /* Always check congestion before sending. */ if (ss_congested) { errno = EWOULDBLOCK; return -1; } switch(ss_transport) { case T_TLS: #ifdef USE_OPENSSL rc = send_nowait_tls(ss_ssl, buffer, len, 0); #else errno = EOPNOTSUPP; rc = -1; #endif break; case T_SCTP: #ifdef USE_SCTP TRACE_MSG("socket_write_primitive %d\n", sctpstate); if (sctpstate == SCTP_DOWN) { errno = EPIPE; return -1; } else if (sctpstate == SCTP_CONNECTING) { errno = EWOULDBLOCK; return -1; } rc = send_sctp_nowait(ss_fd, buffer, len, 0); #else errno = EOPNOTSUPP; rc = -1; #endif break; case T_TCP: rc = send_nowait(ss_fd, buffer, len, 0); break; case T_UDP: if (compression) { static char comp_msg[SIPP_MAX_MSG_SIZE]; strncpy(comp_msg, buffer, sizeof(comp_msg) - 1); if (comp_compress(&ss_comp_state, comp_msg, (unsigned int *) &len) != COMP_OK) { ERROR("Compression plugin error"); } buffer = (char *)comp_msg; TRACE_MSG("---\nCompressed message len: %zu\n", len); } rc = sendto(ss_fd, buffer, len, 0, _RCAST(struct sockaddr*, dest), socklen_from_addr(dest)); break; default: ERROR("Internal error, unknown transport type %d", ss_transport); } return rc; } /* Flush any output buffers for this socket. */ int SIPpSocket::flush() { struct socketbuf *buf; int ret; while ((buf = ss_out)) { ssize_t size = buf->len - buf->offset; ret = write_primitive(buf->buf + buf->offset, size, &buf->addr); TRACE_MSG("Wrote %d of %zu bytes in an output buffer.\n", ret, size); if (ret == size) { /* Everything is great, throw away this buffer. */ ss_out = buf->next; free_socketbuf(buf); } else if (ret <= 0) { /* Handle connection closes and errors. */ return write_error(ret); } else { /* We have written more of the partial buffer. */ buf->offset += ret; errno = EWOULDBLOCK; enter_congestion(EWOULDBLOCK); return -1; } } return 0; } /* Write data to a socket. */ int SIPpSocket::write(const char *buffer, ssize_t len, int flags, struct sockaddr_storage *dest) { int rc; if (ss_out) { rc = flush(); TRACE_MSG("Attempted socket flush returned %d\r\n", rc); if (rc < 0) { if ((errno == EWOULDBLOCK) && (flags & WS_BUFFER)) { buffer_write(buffer, len, dest); return len; } else { return rc; } } } rc = write_primitive(buffer, len, dest); struct timeval currentTime; GET_TIME (¤tTime); if (rc == len) { /* Everything is great. */ if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "%s %smessage sent (%zu bytes):\n\n%.*s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(ss_transport), ss_control ? "control " : "", len, (int)len, buffer); } if (useShortMessagef == 1) { char *msg = strdup(buffer); const char *call_id = get_trimmed_call_id(msg); TRACE_SHORTMSG("%s\tS\t%s\tCSeq:%s\t%s\n", CStat::formatTime(¤tTime), call_id, get_header_content(msg, "CSeq:"), get_first_line(msg)); free(msg); } } else if (rc <= 0) { if ((errno == EWOULDBLOCK) && (flags & WS_BUFFER)) { buffer_write(buffer, len, dest); enter_congestion(errno); return len; } if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "Error sending %s message:\n\n%.*s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(ss_transport), (int)len, buffer); } return write_error(errno); } else { /* We have a truncated message, which must be handled internally to the write function. */ if (useMessagef == 1) { TRACE_MSG("----------------------------------------------- %s\n" "Truncation sending %s message (%d of %zu sent):\n\n%.*s\n", CStat::formatTime(¤tTime, true), TRANSPORT_TO_STRING(ss_transport), rc, len, (int)len, buffer); } buffer_write(buffer + rc, len - rc, dest); enter_congestion(errno); } return rc; } bool reconnect_allowed() { if (reset_number == -1) { return true; } return (reset_number > 0); } void SIPpSocket::reset_connection() { if (!reconnect_allowed()) { ERROR_NO("Max number of reconnections reached"); } if (reset_number != -1) { reset_number--; } if (reset_close) { WARNING("Closing calls, because of TCP reset or close!"); close_calls(); } /* Sleep for some period of time before the reconnection. */ usleep(1000 * reset_sleep); if (reconnect() < 0) { WARNING_NO("Could not reconnect TCP socket"); close_calls(); } else { WARNING("Socket required a reconnection."); } } /* Close just those calls for a given socket (e.g., if the remote end closes * the connection. */ void SIPpSocket::close_calls() { owner_list *owners = get_owners_for_socket(this); owner_list::iterator owner_it; socketowner *owner_ptr = NULL; for (owner_it = owners->begin(); owner_it != owners->end(); owner_it++) { owner_ptr = *owner_it; if (owner_ptr) { owner_ptr->tcpClose(); } } delete owners; } int open_connections() { int status=0; int family_hint = PF_UNSPEC; local_port = 0; if (!strlen(remote_host)) { if ((sendMode != MODE_SERVER)) { ERROR("Missing remote host parameter. This scenario requires it"); } } else { int temp_remote_port; get_host_and_port(remote_host, remote_host, &temp_remote_port); if (temp_remote_port != 0) { remote_port = temp_remote_port; } /* Resolving the remote IP */ { fprintf(stderr, "Resolving remote host '%s'... ", remote_host); /* FIXME: add DNS SRV support using liburli? */ if (gai_getsockaddr(&remote_sockaddr, remote_host, remote_port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown remote host '%s'.\n" "Use 'sipp -h' for details", remote_host); } get_inet_address(&remote_sockaddr, remote_ip, sizeof(remote_ip)); family_hint = remote_sockaddr.ss_family; if (remote_sockaddr.ss_family == AF_INET) { strcpy(remote_ip_escaped, remote_ip); } else { sprintf(remote_ip_escaped, "[%s]", remote_ip); } fprintf(stderr, "Done.\n"); } } { /* Yuck. Populate local_sockaddr with "our IP" first, and then * replace it with INADDR_ANY if we did not request a specific * IP to bind on. */ bool bind_specific = false; memset(&local_sockaddr, 0, sizeof(struct sockaddr_storage)); if (strlen(local_ip) || !strlen(remote_host)) { int ret; struct addrinfo * local_addr; struct addrinfo hints; memset((char*)&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = family_hint; if (strlen(local_ip)) { bind_specific = true; } else { /* Bind on gethostname() IP by default. This is actually * buggy. We should be able to bind on :: and decide on * accept() what Contact IP we use. Right now, if we do * that, we'd send [::] in the contact and :: in the RTP * as "our IP". */ if (gethostname(local_ip, sizeof(local_ip)) != 0) { ERROR_NO("Can't get local hostname"); } } /* Resolving local IP */ if ((ret = getaddrinfo(local_ip, NULL, &hints, &local_addr)) != 0) { switch (ret) { #ifdef EAI_ADDRFAMILY case EAI_ADDRFAMILY: ERROR("Network family mismatch for local and remote IP"); break; #endif default: ERROR("Can't get local IP address in getaddrinfo, " "local_ip='%s', ret=%d", local_ip, ret); } } memcpy(&local_sockaddr, local_addr->ai_addr, local_addr->ai_addrlen); freeaddrinfo(local_addr); if (!bind_specific) { get_inet_address(&local_sockaddr, local_ip, sizeof(local_ip)); } } else { /* Get temp socket on UDP to find out our local address */ int tmpsock = -1; socklen_t len = sizeof(local_sockaddr); if ((tmpsock = socket(remote_sockaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) < 0 || ::connect(tmpsock, _RCAST(struct sockaddr*, &remote_sockaddr), socklen_from_addr(&remote_sockaddr)) < 0 || getsockname(tmpsock, _RCAST(struct sockaddr*, &local_sockaddr), &len) < 0) { if (tmpsock >= 0) { close(tmpsock); } ERROR_NO("Failed to find our local ip"); } close(tmpsock); get_inet_address(&local_sockaddr, local_ip, sizeof(local_ip)); } /* Store local addr info for rsa option */ memcpy(&local_addr_storage, &local_sockaddr, sizeof(local_sockaddr)); if (local_sockaddr.ss_family == AF_INET) { strcpy(local_ip_escaped, local_ip); if (!bind_specific) { _RCAST(struct sockaddr_in*, &local_sockaddr)->sin_addr.s_addr = INADDR_ANY; } } else { local_ip_is_ipv6 = true; sprintf(local_ip_escaped, "[%s]", local_ip); if (!bind_specific) { memcpy(&_RCAST(struct sockaddr_in6*, &local_sockaddr)->sin6_addr, &in6addr_any, sizeof(in6addr_any)); } } } /* Creating and binding the local socket */ if ((main_socket = new_sipp_socket(local_ip_is_ipv6, transport)) == NULL) { ERROR_NO("Unable to get the local socket"); } sipp_customize_socket(main_socket); /* Trying to bind local port */ char peripaddr[256]; if (!user_port) { unsigned short l_port; for (l_port = DEFAULT_PORT; l_port < (DEFAULT_PORT + 60); l_port++) { // Bind socket to local_ip if (bind_local || peripsocket) { if (peripsocket) { // On some machines it fails to bind to the self computed local // IP address. // For the socket per IP mode, bind the main socket to the // first IP address specified in the inject file. inFiles[ip_file]->getField(0, peripfield, peripaddr, sizeof(peripaddr)); if (gai_getsockaddr(&local_sockaddr, peripaddr, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } else { if (gai_getsockaddr(&local_sockaddr, local_ip, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } } sockaddr_update_port(&local_sockaddr, l_port); if (sipp_bind_socket(main_socket, &local_sockaddr, &local_port) == 0) { break; } } } if (!local_port) { /* Not already bound, use user_port of 0 to leave * the system choose a port. */ if (bind_local || peripsocket) { if (peripsocket) { // On some machines it fails to bind to the self computed local // IP address. // For the socket per IP mode, bind the main socket to the // first IP address specified in the inject file. inFiles[ip_file]->getField(0, peripfield, peripaddr, sizeof(peripaddr)); if (gai_getsockaddr(&local_sockaddr, peripaddr, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } else { if (gai_getsockaddr(&local_sockaddr, local_ip, const_char_nullptr, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } } } sockaddr_update_port(&local_sockaddr, user_port); if (sipp_bind_socket(main_socket, &local_sockaddr, &local_port)) { ERROR_NO("Unable to bind main socket"); } } if (peripsocket) { // Add the main socket to the socket per subscriber map map_perip_fd[peripaddr] = main_socket; } // Create additional server sockets when running in socket per // IP address mode. if (peripsocket && sendMode == MODE_SERVER) { struct sockaddr_storage server_sockaddr; char peripaddr[256]; SIPpSocket *sock; unsigned int lines = inFiles[ip_file]->numLines(); for (unsigned int i = 0; i < lines; i++) { inFiles[ip_file]->getField(i, peripfield, peripaddr, sizeof(peripaddr)); map::iterator j; j = map_perip_fd.find(peripaddr); if (j == map_perip_fd.end()) { if (gai_getsockaddr(&server_sockaddr, peripaddr, local_port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown remote host '%s'.\n" "Use 'sipp -h' for details", peripaddr); } bool is_ipv6 = (server_sockaddr.ss_family == AF_INET6); if ((sock = new_sipp_socket(is_ipv6, transport)) == NULL) { ERROR_NO("Unable to get server socket"); } sipp_customize_socket(sock); if (sipp_bind_socket(sock, &server_sockaddr, NULL)) { ERROR_NO("Unable to bind server socket"); } map_perip_fd[peripaddr] = sock; } } } if ((!multisocket) && (transport == T_TCP || transport == T_TLS || transport == T_SCTP) && (sendMode != MODE_SERVER)) { if ((tcp_multiplex = new_sipp_socket(local_ip_is_ipv6, transport)) == NULL) { ERROR_NO("Unable to get a TCP socket"); } /* If there is a user-supplied local port and we use a single * socket, then bind to the specified port. */ if (user_port) { tcp_multiplex->set_bind_port(local_port); } /* OJA FIXME: is it correct? */ if (use_remote_sending_addr) { remote_sockaddr = remote_sending_sockaddr; } sipp_customize_socket(tcp_multiplex); if (tcp_multiplex->connect(&remote_sockaddr)) { if (reset_number > 0) { WARNING("Failed to reconnect"); main_socket->close(); main_socket = NULL; reset_number--; return 1; } else { if (errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR_NO("Unable to connect a TCP socket, remote peer error.\n" "Use 'sipp -h' for details"); } else { ERROR_NO("Unable to connect a TCP socket.\n" "Use 'sipp -h' for details"); } } } } if (transport == T_TCP || transport == T_TLS || transport == T_SCTP) { if (listen(main_socket->ss_fd, 100)) { ERROR_NO("Unable to listen main socket"); } } /* Trying to connect to Twin Sipp in 3PCC mode */ if (twinSippMode) { if (thirdPartyMode == MODE_3PCC_CONTROLLER_A || thirdPartyMode == MODE_3PCC_A_PASSIVE) { connect_to_peer(twinSippHost, twinSippPort, &twinSipp_sockaddr, twinSippIp, &twinSippSocket); } else if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { connect_local_twin_socket(twinSippHost); } else { ERROR("TwinSipp Mode enabled but thirdPartyMode is different " "from 3PCC_CONTROLLER_B and 3PCC_CONTROLLER_A\n"); } } else if (extendedTwinSippMode) { if (thirdPartyMode == MODE_MASTER || thirdPartyMode == MODE_MASTER_PASSIVE) { strncpy(twinSippHost, get_peer_addr(master_name), sizeof(twinSippHost) - 1); get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); connect_local_twin_socket(twinSippHost); connect_to_all_peers(); } else if (thirdPartyMode == MODE_SLAVE) { strncpy(twinSippHost, get_peer_addr(slave_number), sizeof(twinSippHost) - 1); get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); connect_local_twin_socket(twinSippHost); } else { ERROR("extendedTwinSipp Mode enabled but thirdPartyMode is different " "from MASTER and SLAVE\n"); } } return status; } void connect_to_peer(char *peer_host, int peer_port, struct sockaddr_storage *peer_sockaddr, char *peer_ip, SIPpSocket **peer_socket) { /* Resolving the peer IP */ printf("Resolving peer address : %s...\n", peer_host); bool is_ipv6 = false; /* Resolving twin IP */ if (gai_getsockaddr(peer_sockaddr, peer_host, peer_port, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown peer host '%s'.\n" "Use 'sipp -h' for details", peer_host); } if (peer_sockaddr->ss_family == AF_INET6) { is_ipv6 = true; } get_inet_address(peer_sockaddr, peer_ip, sizeof(peer_ip)); if ((*peer_socket = new_sipp_socket(is_ipv6, T_TCP)) == NULL) { ERROR_NO("Unable to get a twin sipp TCP socket"); } /* Mark this as a control socket. */ (*peer_socket)->ss_control = 1; if ((*peer_socket)->connect(peer_sockaddr)) { if (errno == EINVAL) { /* This occurs sometime on HPUX but is not a true INVAL */ ERROR_NO("Unable to connect a twin sipp TCP socket\n " ", remote peer error.\n" "Use 'sipp -h' for details"); } else { ERROR_NO("Unable to connect a twin sipp socket " "\n" "Use 'sipp -h' for details"); } } sipp_customize_socket(*peer_socket); } SIPpSocket **get_peer_socket(char * peer) { peer_map::iterator peer_it; peer_it = peers.find(peer_map::key_type(peer)); if (peer_it != peers.end()) { return &peer_it->second.peer_socket; } else { ERROR("get_peer_socket: Peer %s not found", peer); } return NULL; } char * get_peer_addr(char * peer) { char * addr; peer_addr_map::iterator peer_addr_it; peer_addr_it = peer_addrs.find(peer_addr_map::key_type(peer)); if (peer_addr_it != peer_addrs.end()) { addr = peer_addr_it->second; return addr; } else { ERROR("get_peer_addr: Peer %s not found", peer); } return NULL; } bool is_a_peer_socket(SIPpSocket *peer_socket) { peer_socket_map::iterator peer_socket_it; peer_socket_it = peer_sockets.find(peer_socket_map::key_type(peer_socket)); if (peer_socket_it == peer_sockets.end()) { return false; } else { return true; } } void connect_local_twin_socket(char * twinSippHost) { /* Resolving the listener IP */ printf("Resolving listener address : %s...\n", twinSippHost); bool is_ipv6 = false; /* Resolving twin IP */ if (gai_getsockaddr(&twinSipp_sockaddr, twinSippHost, twinSippPort, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown twin host '%s'.\n" "Use 'sipp -h' for details", twinSippHost); } if (twinSipp_sockaddr.ss_family == AF_INET6) { is_ipv6 = true; } get_inet_address(&twinSipp_sockaddr, twinSippIp, sizeof(twinSippIp)); if ((localTwinSippSocket = new_sipp_socket(is_ipv6, T_TCP)) == NULL) { ERROR_NO("Unable to get a listener TCP socket "); } memset(&localTwin_sockaddr, 0, sizeof(struct sockaddr_storage)); localTwin_sockaddr.ss_family = is_ipv6 ? AF_INET6 : AF_INET; sockaddr_update_port(&localTwin_sockaddr, twinSippPort); sipp_customize_socket(localTwinSippSocket); if (sipp_bind_socket(localTwinSippSocket, &localTwin_sockaddr, 0)) { ERROR_NO("Unable to bind twin sipp socket "); } if (listen(localTwinSippSocket->ss_fd, 100)) { ERROR_NO("Unable to listen twin sipp socket in "); } } void close_peer_sockets() { peer_map::iterator peer_it, __end; for (peer_it = peers.begin(), __end = peers.end(); peer_it != __end; ++peer_it) { T_peer_infos infos = peer_it->second; infos.peer_socket->close(); infos.peer_socket = NULL; peers[std::string(peer_it->first)] = infos; } peers_connected = 0; } void close_local_sockets() { for (int i = 0; i< local_nb; i++) { local_sockets[i]->close(); local_sockets[i] = NULL; } } void connect_to_all_peers() { peer_map::iterator peer_it; T_peer_infos infos; for (peer_it = peers.begin(); peer_it != peers.end(); peer_it++) { infos = peer_it->second; get_host_and_port(infos.peer_host, infos.peer_host, &infos.peer_port); connect_to_peer(infos.peer_host, infos.peer_port, &(infos.peer_sockaddr), infos.peer_ip, &(infos.peer_socket)); peer_sockets[infos.peer_socket] = peer_it->first; peers[std::string(peer_it->first)] = infos; } peers_connected = 1; } bool is_a_local_socket(SIPpSocket *s) { for (int i = 0; i< local_nb + 1; i++) { if (local_sockets[i] == s) return true; } return (false); } void free_peer_addr_map() { peer_addr_map::iterator peer_addr_it; for (peer_addr_it = peer_addrs.begin(); peer_addr_it != peer_addrs.end(); peer_addr_it++) { free(peer_addr_it->second); } } void SIPpSocket::pollset_process(int wait) { int rs; /* Number of times to execute recv(). For TCP with 1 socket per call: no. of events returned by poll For UDP and TCP with 1 global socket: recv_count is a flag that stays up as long as there's data to read */ #ifndef HAVE_EPOLL /* What index should we try reading from? */ static size_t read_index; int loops = max_recv_loops; // If not using epoll, we have a queue of pending messages to spin through. if (read_index >= pollnfds) { read_index = 0; } /* We need to process any messages that we have left over. */ while (pending_messages && loops > 0) { getmilliseconds(); if (sockets[read_index]->ss_msglen) { struct sockaddr_storage src; char msg[SIPP_MAX_MSG_SIZE]; ssize_t len = sockets[read_index]->read_message(msg, sizeof(msg), &src); if (len > 0) { process_message(sockets[read_index], msg, len, &src); } else { assert(0); } loops--; } read_index = (read_index + 1) % pollnfds; } /* Don't read more data if we still have some left over. */ if (pending_messages) { return; } #endif /* Get socket events. */ #ifdef HAVE_EPOLL /* Ignore the wait parameter and always wait - when establishing TCP * connections, the alternative is that we tight-loop. */ rs = epoll_wait(epollfd, epollevents, max_recv_loops, 1); // If we're receiving as many epollevents as possible, flag CPU congestion cpu_max = (rs > (max_recv_loops - 2)); #else rs = poll(pollfiles, pollnfds, wait ? 1 : 0); #endif if (rs < 0 && errno == EINTR) { return; } /* We need to flush all sockets and pull data into all of our buffers. */ #ifdef HAVE_EPOLL for (int event_idx = 0; event_idx < rs; event_idx++) { int poll_idx = (int)epollevents[event_idx].data.u32; #else for (size_t poll_idx = 0; rs > 0 && poll_idx < pollnfds; poll_idx++) { #endif SIPpSocket *sock = sockets[poll_idx]; int events = 0; int ret = 0; assert(sock); #ifdef HAVE_EPOLL if (epollevents[event_idx].events & EPOLLOUT) { #else if (pollfiles[poll_idx].revents & POLLOUT) { #endif #ifdef USE_SCTP if (transport == T_SCTP && sock->sctpstate != SCTP_UP); else #endif { /* We can flush this socket. */ TRACE_MSG("Exit problem event on socket %d \n", sock->ss_fd); #ifdef HAVE_EPOLL epollfiles[poll_idx].events &= ~EPOLLOUT; int rc = epoll_ctl(epollfd, EPOLL_CTL_MOD, sock->ss_fd, &epollfiles[poll_idx]); if (rc == -1) { ERROR_NO("Failed to clear EPOLLOUT"); } #else pollfiles[poll_idx].events &= ~POLLOUT; #endif sock->ss_congested = false; sock->flush(); events++; } } #ifdef HAVE_EPOLL if (epollevents[event_idx].events & EPOLLIN) { #else if (pollfiles[poll_idx].revents & POLLIN) { #endif /* We can empty this socket. */ if ((transport == T_TCP || transport == T_TLS || transport == T_SCTP) && sock == main_socket) { SIPpSocket *new_sock = sock->accept(); if (!new_sock) { ERROR_NO("Accepting new TCP connection"); } } else if (sock == ctrl_socket) { handle_ctrl_socket(); } else if (sock == stdin_socket) { handle_stdin_socket(); } else if (sock == localTwinSippSocket) { if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) { twinSippSocket = sock->accept(); if (!twinSippMode) { ERROR_NO("Accepting new TCP connection on Twin SIPp Socket"); } twinSippSocket->ss_control = 1; } else { /* 3pcc extended mode: open a local socket which will be used for reading the infos sent by this remote twin sipp instance (slave or master) */ if (local_nb == MAX_LOCAL_TWIN_SOCKETS) { ERROR("Max number of twin instances reached"); } SIPpSocket *localSocket = sock->accept(); localSocket->ss_control = 1; local_sockets[local_nb] = localSocket; local_nb++; if (!peers_connected) { connect_to_all_peers(); } } } else { if ((ret = sock->empty()) <= 0) { #ifdef USE_SCTP if (sock->ss_transport == T_SCTP && ret == -2); else #endif { ret = sock->read_error(ret); if (ret == 0) { /* If read_error() then the poll_idx now belongs * to the newest/last socket added to the sockets[]. * Need to re-do the same poll_idx for the "new" socket. * We do this differently when using epoll. */ #ifdef HAVE_EPOLL for (int event_idx2 = event_idx + 1; event_idx2 < rs; event_idx2++) { if (epollevents[event_idx2].data.u32 == pollnfds) { epollevents[event_idx2].data.u32 = poll_idx; } } #else poll_idx--; events++; rs--; #endif continue; } } } } events++; } /* Here the logic diverges; if we're using epoll, we want to stay in the * for-each-socket loop and handle messages on that socket. If we're not using * epoll, we want to wait until after that loop, and spin through our * pending_messages queue again. */ #ifdef HAVE_EPOLL unsigned old_pollnfds = pollnfds; getmilliseconds(); /* Keep processing messages until this socket is freed (changing the number of file descriptors) or we run out of messages. */ while ((pollnfds == old_pollnfds) && (sock->message_ready())) { char msg[SIPP_MAX_MSG_SIZE]; struct sockaddr_storage src; ssize_t len; len = sock->read_message(msg, sizeof(msg), &src); if (len > 0) { process_message(sock, msg, len, &src); } else { assert(0); } } if (pollnfds != old_pollnfds) { /* Processing messages has changed the number of pollnfds, so update any remaining events */ for (int event_idx2 = event_idx + 1; event_idx2 < rs; event_idx2++) { if (epollevents[event_idx2].data.u32 == pollnfds) { epollevents[event_idx2].data.u32 = poll_idx; } } } #else if (events) { rs--; } pollfiles[poll_idx].revents = 0; #endif } #ifndef HAVE_EPOLL if (read_index >= pollnfds) { read_index = 0; } /* We need to process any new messages that we read. */ while (pending_messages && (loops > 0)) { getmilliseconds(); if (sockets[read_index]->ss_msglen) { char msg[SIPP_MAX_MSG_SIZE]; struct sockaddr_storage src; ssize_t len; len = sockets[read_index]->read_message(msg, sizeof(msg), &src); if (len > 0) { process_message(sockets[read_index], msg, len, &src); } else { assert(0); } loops--; } read_index = (read_index + 1) % pollnfds; } cpu_max = (loops <= 0); #endif } /***************** Check of the message received ***************/ bool sipMsgCheck (const char *P_msg, SIPpSocket *socket) { const char C_sipHeader[] = "SIP/2.0"; if (socket == twinSippSocket || socket == localTwinSippSocket || is_a_peer_socket(socket) || is_a_local_socket(socket)) return true; if (strstr(P_msg, C_sipHeader) != NULL) { return true; } return false; } #ifdef GTEST #include "gtest/gtest.h" TEST(get_trimmed_call_id, noslashes) { EXPECT_STREQ("abc", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: abc\r\nCall-ID: def\r\n\r\n")); } TEST(get_trimmed_call_id, withslashes) { EXPECT_STREQ("abc2", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: ///abc2\r\nCall-ID: def\r\n\r\n")); EXPECT_STREQ("abc3", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: abc2///abc3\r\nCall-ID: def\r\n\r\n")); EXPECT_STREQ("abc4///abc5", get_trimmed_call_id("OPTIONS..\r\nBla: X\r\nCall-ID: abc3///abc4///abc5\r\nCall-ID: def\r\n\r\n")); } #endif //GTEST sipp-3.6.1/src/scenario.cpp0000664000175000017500000040141013730472040015152 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Venkatesh * Lee Ballard * Guillaume TEISSIER from FTR&D * Wolfgang Beck * Marc Van Diest from Belgacom * Charles P. Wright from IBM Research * Michael Stovenour */ #include #include "config.h" #include "sipp.hpp" #ifdef HAVE_GSL #include #include #include #endif /************************ Class Constructor *************************/ message::message(int index, const char *desc) { this->index = index; this->desc = desc; pause_distribution = NULL; // delete on exit pause_variable = -1; pause_desc = NULL; // free on exit sessions = 0; bShouldRecordRoutes = 0; bShouldAuthenticate = 0; send_scheme = NULL; // delete on exit retrans_delay = 0; timeout = 0; recv_response = 0; recv_request = NULL; // free on exit optional = 0; advance_state = true; regexp_match = 0; regexp_compile = NULL; // regfree (if not NULL) and free on exit /* Anyway */ start_rtd = 0; stop_rtd = 0; repeat_rtd = 0; lost = -1; crlf = 0; ignoresdp = false; hide = 0; display_str = NULL; // free on exit test = -1; condexec = -1; condexec_inverse = false; chance = 0;/* meaning always */ next = -1; nextLabel = NULL; // free on exit on_timeout = -1; onTimeoutLabel = NULL; // free on exit timewait = false; /* 3pcc extended mode */ peer_dest = NULL; // free on exit peer_src = NULL; // free on exit /* Statistics */ nb_sent = 0; nb_recv = 0; nb_sent_retrans = 0; nb_recv_retrans = 0; nb_timeout = 0; nb_unexp = 0; nb_lost = 0; counter = 0; M_actions = NULL; // delete on exit M_type = 0; M_sendCmdData = NULL; // delete on exit M_nbCmdSent = 0; M_nbCmdRecv = 0; content_length_flag = ContentLengthNoPresent; /* How to match responses to this message. */ start_txn = 0; response_txn = 0; ack_txn = 0; recv_response_for_cseq_method_list = NULL; // free on exit } message::~message() { delete(pause_distribution); free(pause_desc); delete(send_scheme); free(recv_request); if (regexp_compile != NULL) { regfree(regexp_compile); } free(regexp_compile); free(display_str); free(nextLabel); free(onTimeoutLabel); free(peer_dest); free(peer_src); delete(M_actions); delete(M_sendCmdData); free(recv_response_for_cseq_method_list); } /******** Global variables which compose the scenario file **********/ scenario *main_scenario; scenario *ooc_scenario; scenario *aa_scenario; scenario *display_scenario; /* This mode setting refers to whether we open calls autonomously (MODE_CLIENT) * or in response to requests (MODE_SERVER). */ int creationMode = MODE_CLIENT; /* Send mode. Do we send to a fixed address or to the last one we got. */ int sendMode = MODE_CLIENT; /* This describes what our 3PCC behavior is. */ int thirdPartyMode = MODE_3PCC_NONE; /*************** Helper functions for various types *****************/ long get_long(const char *ptr, const char *what) { char *endptr; long ret; ret = strtol(ptr, &endptr, 0); if (*endptr) { ERROR("%s, \"%s\" is not a valid integer!", what, ptr); } return ret; } unsigned long long get_long_long(const char *ptr, const char *what) { char *endptr; unsigned long long ret; ret = strtoull(ptr, &endptr, 0); if (*endptr) { ERROR("%s, \"%s\" is not a valid integer!", what, ptr); } return ret; } /* This function returns a time in milliseconds from a string. * The multiplier is used to convert from the default input type into * milliseconds. For example, for seconds you should use 1000 and for * milliseconds use 1. */ long get_time(const char *ptr, const char *what, int multiplier) { char *endptr; const char *p; long ret = 0; double dret; int i; if (!isdigit(*ptr)) { ERROR("%s, \"%s\" is not a valid time!", what, ptr); } for (i = 0, p = ptr; *p; p++) { if (*p == ':') { i++; } } if (i == 1) { /* mm:ss */ ERROR("%s, \"%s\" mm:ss not implemented yet!", what, ptr); } else if (i == 2) { /* hh:mm:ss */ ERROR("%s, \"%s\" hh:mm:ss not implemented yet!", what, ptr); } else if (i != 0) { ERROR("%s, \"%s\" is not a valid time!", what, ptr); } dret = strtod(ptr, &endptr); if (*endptr) { if (!strcmp(endptr, "s")) { /* Seconds */ ret = (long)(dret * 1000); } else if (!strcmp(endptr, "ms")) { /* Milliseconds. */ ret = (long)dret; } else if (!strcmp(endptr, "m")) { /* Minutes. */ ret = (long)(dret * 60000); } else if (!strcmp(endptr, "h")) { /* Hours. */ ret = (long)(dret * 60 * 60 * 1000); } else { ERROR("%s, \"%s\" is not a valid time!", what, ptr); } } else { ret = (long)(dret * multiplier); } return ret; } double get_double(const char *ptr, const char *what) { char *endptr; double ret; ret = strtod(ptr, &endptr); if (*endptr) { ERROR("%s, \"%s\" is not a floating point number!", what, ptr); } return ret; } #if defined(PCAPPLAY) || defined(RTP_STREAM) /* If the value is enclosed in [brackets], it is assumed to be * a command-line supplied keyword value (-key). */ static char* xp_get_keyword_value(const char *name) { const char* ptr = xp_get_value(name); size_t len; if (ptr && ptr[0] == '[' && (len = strlen(ptr)) && ptr[len - 1] == ']') { int i = 0; len -= 2; /* without the brackets */ while (generic[i]) { const char* keyword = *generic[i]; if (strncmp(ptr + 1, keyword, len) == 0 && strlen(keyword) == len) { const char* value = *(generic[i] + 1); return strdup(value); } ++i; } ERROR("%s \"%s\" looks like a keyword value, but keyword not supplied!", name, ptr); } return ptr ? strdup(ptr) : NULL; } #endif static char* xp_get_string(const char *name, const char *what) { const char *ptr; char *unescaped; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } unescaped = (char *)malloc(strlen(ptr) + 1); if (!unescaped) { ERROR("Out of memory!"); } xp_unescape(ptr, unescaped); return unescaped; } static double xp_get_double(const char *name, const char *what) { const char *ptr; char *helptext; double val; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } helptext = (char *)malloc(100 + strlen(name) + strlen(what)); sprintf(helptext, "%s '%s' parameter", what, name); val = get_double(ptr, helptext); free(helptext); return val; } static long xp_get_long(const char *name, const char *what) { const char *ptr; char *helptext; long val; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } helptext = (char *)malloc(100 + strlen(name) + strlen(what)); sprintf(helptext, "%s '%s' parameter", what, name); val = get_long(ptr, helptext); free(helptext); return val; } static long xp_get_long(const char *name, const char *what, long defval) { if (!(xp_get_value(name))) { return defval; } return xp_get_long(name, what); } static bool xp_get_bool(const char *name, const char *what) { const char *ptr; char *helptext; bool val; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' parameter.", what, name); } helptext = (char *)malloc(100 + strlen(name) + strlen(what)); sprintf(helptext, "%s '%s' parameter", what, name); val = get_bool(ptr, helptext); free(helptext); return val; } static bool xp_get_bool(const char *name, const char *what, bool defval) { if (!(xp_get_value(name))) { return defval; } return xp_get_bool(name, what); } int scenario::get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck) { /* Check the name's validity. */ if (txnName[0] == '\0') { ERROR("Variable names may not be empty for %s", what); } if (strcspn(txnName, "$,") != strlen(txnName)) { ERROR("Variable names may not contain $ or , for %s", what); } /* If this transaction has already been used, then we have nothing to do. */ str_int_map::iterator txn_it = txnMap.find(txnName); if (txn_it != txnMap.end()) { if (start) { /* We need to fill in the invite field. */ transactions[txn_it->second - 1].started++; } else if (isAck) { transactions[txn_it->second - 1].acks++; } else { transactions[txn_it->second - 1].responses++; } return txn_it->second; } /* Assign this variable the next slot. */ struct txnControlInfo transaction; transaction.name = strdup(txnName); if (start) { transaction.started = 1; transaction.responses = 0; transaction.acks = 0; transaction.isInvite = isInvite; } else if (isAck) { /* Does not start or respond to this txn. */ transaction.started = 0; transaction.responses = 0; transaction.acks = 1; transaction.isInvite = false; } else { transaction.started = 0; transaction.responses = 1; transaction.acks = 0; transaction.isInvite = false; } transactions.push_back(transaction); int txnNum = transactions.size(); txnMap[txnName] = txnNum; return txnNum; } int scenario::find_var(const char *varName) { return allocVars->find(varName, false); } int scenario::get_var(const char *varName, const char *what) { /* Check the name's validity. */ if (varName[0] == '\0') { ERROR("Transaction names may not be empty for %s", what); } if (strcspn(varName, "$,") != strlen(varName)) { ERROR("Transaction names may not contain '$' or ',' for %s", what); } return allocVars->find(varName, true); } int scenario::xp_get_var(const char *name, const char *what) { const char *ptr; if (!(ptr = xp_get_value(name))) { ERROR("%s is missing the required '%s' variable parameter.", what, name); } return get_var(ptr, what); } static int xp_get_optional(const char *name, const char *what) { const char *ptr = xp_get_value(name); if (!ptr) { return OPTIONAL_FALSE; } if(!strcmp(ptr, "true")) { return OPTIONAL_TRUE; } else if(!strcmp(ptr, "global")) { return OPTIONAL_GLOBAL; } else if(!strcmp(ptr, "false")) { return OPTIONAL_FALSE; } else { ERROR("Could not understand optional value for %s: %s", what, ptr); } return OPTIONAL_FALSE; } int scenario::xp_get_var(const char *name, const char *what, int defval) { const char *ptr; if (!(ptr = xp_get_value(name))) { return defval; } return xp_get_var(name, what); } bool get_bool(const char *ptr, const char *what) { char *endptr; long ret; if (!strcasecmp(ptr, "true")) { return true; } if (!strcasecmp(ptr, "false")) { return false; } ret = strtol(ptr, &endptr, 0); if (*endptr) { ERROR("%s, \"%s\" is not a valid boolean!", what, ptr); } return ret ? true : false; } /* Pretty print a time. */ int time_string(double ms, char *res, int reslen) { if (ms < 10000) { /* Less then 10 seconds we represent accurately. */ if ((int)(ms + 0.9999) == (int)(ms)) { /* We have an integer, or close enough to it. */ return snprintf(res, reslen, "%dms", (int)ms); } else { if (ms < 1000) { return snprintf(res, reslen, "%.2lfms", ms); } else { return snprintf(res, reslen, "%.1lfms", ms); } } } else if (ms < 60000) { /* We round to 100ms for times less than a minute. */ return snprintf(res, reslen, "%.1fs", ms/1000); } else if (ms < 60 * 60000) { /* We round to 1s for times more than a minute. */ int s = (unsigned int)(ms / 1000); int m = s / 60; s %= 60; return snprintf(res, reslen, "%d:%02d", m, s); } else { int s = (unsigned int)(ms / 1000); int m = s / 60; int h = m / 60; s %= 60; m %= 60; return snprintf(res, reslen, "%d:%02d:%02d", h, m, s); } } /* For backwards compatibility, we assign "true" to slot 1, false to 0, and * allow other valid integers. */ int scenario::get_rtd(const char *ptr, bool start) { if(!strcmp(ptr, (char *)"false")) return 0; if(!strcmp(ptr, (char *)"true")) return stats->findRtd("1", start); return stats->findRtd(ptr, start); } /* Get a counter */ int scenario::get_counter(const char *ptr, const char *what) { /* Check the name's validity. */ if (ptr[0] == '\0') { ERROR("Counter names names may not be empty for %s", what); } if (strcspn(ptr, "$,") != strlen(ptr)) { ERROR("Counter names may not contain $ or , for %s", what); } return stats->findCounter(ptr, true); } /* Some validation functions. */ void scenario::validate_variable_usage() { allocVars->validate(); } void scenario::validate_txn_usage() { for (unsigned int i = 0; i < transactions.size(); i++) { if(transactions[i].started == 0) { ERROR("Transaction %s is never started!", transactions[i].name); } else if(transactions[i].responses == 0) { ERROR("Transaction %s has no responses defined!", transactions[i].name); } if (transactions[i].isInvite && transactions[i].acks == 0) { ERROR("Transaction %s is an INVITE transaction without an ACK!", transactions[i].name); } if (!transactions[i].isInvite && (transactions[i].acks > 0)) { ERROR("Transaction %s is a non-INVITE transaction with an ACK!", transactions[i].name); } } } /* Apply the next and ontimeout labels according to our map. */ void scenario::apply_labels(msgvec v, str_int_map labels) { for (unsigned int i = 0; i < v.size(); i++) { if (v[i]->nextLabel) { str_int_map::iterator label_it = labels.find(v[i]->nextLabel); if (label_it == labels.end()) { ERROR("The label '%s' was not defined (index %d, next attribute)", v[i]->nextLabel, i); } v[i]->next = label_it->second; } if (v[i]->onTimeoutLabel) { str_int_map::iterator label_it = labels.find(v[i]->onTimeoutLabel); if (label_it == labels.end()) { ERROR("The label '%s' was not defined (index %d, ontimeout attribute)", v[i]->onTimeoutLabel, i); } v[i]->on_timeout = label_it->second; } } } int get_cr_number(const char *src) { int res=0; while(*src) { if(*src == '\n') res++; src++; } return res; } static char* clean_cdata(char *ptr, int *removed_crlf = NULL) { char * msg; while((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n')) ptr++; msg = (char *) malloc(strlen(ptr) + 3); if(!msg) { ERROR("Memory Overflow"); } strcpy(msg, ptr); ptr = msg + strlen(msg); ptr--; while((ptr >= msg) && ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n'))) { if(*ptr == '\n' && removed_crlf) { (*removed_crlf)++; } *ptr-- = 0; } if(ptr == msg) { ERROR("Empty cdata in xml scenario file"); } while ((ptr = strstr(msg, "\n "))) { memmove(ptr + 1, ptr + 2, strlen(ptr) - 1); } while ((ptr = strstr(msg, " \n"))) { memmove(ptr, ptr + 1, strlen(ptr)); } while ((ptr = strstr(msg, "\n\t"))) { memmove(ptr + 1, ptr + 2, strlen(ptr) - 1); } while ((ptr = strstr(msg, "\t\n"))) { memmove(ptr, ptr + 1, strlen(ptr)); } if (!strstr(msg, "\n\n")) { strcat(msg, "\n\n"); } return msg; } /********************** Scenario File analyser **********************/ void scenario::checkOptionalRecv(char *elem, unsigned int scenario_file_cursor) { if (last_recv_optional) { ERROR(" before <%s> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", elem, scenario_file_cursor); } last_recv_optional = false; } scenario::scenario(char * filename, int deflt) { char * elem; char *method_list = NULL; unsigned int scenario_file_cursor = 0; int L_content_length = 0 ; char * peer; const char* cptr; last_recv_optional = false; if(filename) { if(!xp_set_xml_buffer_from_file(filename)) { ERROR("Unable to load or parse '%s' xml scenario file", filename); } } else { if(!xp_set_xml_buffer_from_string(default_scenario[deflt])) { ERROR("Unable to load default xml scenario file"); } } stats = new CStat(); allocVars = new AllocVariableTable(userVariables); hidedefault = false; elem = xp_open_element(0); if (!elem) { ERROR("No element in xml scenario file"); } if(strcmp("scenario", elem)) { ERROR("No 'scenario' section in xml scenario file"); } if ((cptr = xp_get_value("name"))) { name = strdup(cptr); } else { name = strdup(""); } duration = 0; found_timewait = false; scenario_file_cursor = 0; while ((elem = xp_open_element(scenario_file_cursor))) { char * ptr; scenario_file_cursor ++; if(!strcmp(elem, "CallLengthRepartition")) { ptr = xp_get_string("value", "CallLengthRepartition"); stats->setRepartitionCallLength(ptr); free(ptr); } else if(!strcmp(elem, "ResponseTimeRepartition")) { ptr = xp_get_string("value", "ResponseTimeRepartition"); stats->setRepartitionResponseTime(ptr); free(ptr); } else if(!strcmp(elem, "Global")) { ptr = xp_get_string("variables", "Global"); char ** currentTabVarName = NULL; int currentNbVarNames; createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); for (int i = 0; i < currentNbVarNames; i++) { globalVariables->find(currentTabVarName[i], true); } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } else if(!strcmp(elem, "User")) { ptr = xp_get_string("variables", "User"); char ** currentTabVarName = NULL; int currentNbVarNames; createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); for (int i = 0; i < currentNbVarNames; i++) { userVariables->find(currentTabVarName[i], true); } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } else if(!strcmp(elem, "Reference")) { ptr = xp_get_string("variables", "Reference"); char ** currentTabVarName = NULL; int currentNbVarNames; createStringTable(ptr, ¤tTabVarName, ¤tNbVarNames); for (int i = 0; i < currentNbVarNames; i++) { int id = allocVars->find(currentTabVarName[i], false); if (id == -1) { ERROR("Could not reference non-existant variable '%s'", currentTabVarName[i]); } } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } else if(!strcmp(elem, "DefaultMessage")) { char *id = xp_get_string("id", "DefaultMessage"); if(!(ptr = xp_get_cdata())) { ERROR("No CDATA in 'send' section of xml scenario file"); } char *msg = clean_cdata(ptr); set_default_message(id, msg); free(id); /* XXX: This should really be per scenario. */ } else if(!strcmp(elem, "label")) { ptr = xp_get_string("id", "label"); if (labelMap.find(ptr) != labelMap.end()) { ERROR("The label name '%s' is used twice.", ptr); } labelMap[ptr] = messages.size(); free(ptr); } else if (!strcmp(elem, "init")) { /* We have an init section, which must be full of nops or labels. */ int nop_cursor = 0; char *initelem; while ((initelem = xp_open_element(nop_cursor++))) { if (!strcmp(initelem, "nop")) { /* We should parse this. */ message *nopmsg = new message(initmessages.size(), "scenario initialization"); initmessages.push_back(nopmsg); nopmsg->M_type = MSG_TYPE_NOP; getCommonAttributes(nopmsg); } else if (!strcmp(initelem, "label")) { /* Add an init label. */ cptr = xp_get_value("id"); if (initLabelMap.find(cptr) != initLabelMap.end()) { ERROR("The label name '%s' is used twice.", cptr); } initLabelMap[cptr] = initmessages.size(); } else { ERROR("Invalid element in an init stanza: '%s'", initelem); } xp_close_element(); } } else { /** Message Case */ if (found_timewait) { ERROR(" can only be the last message in a scenario!"); } message *curmsg = new message(messages.size(), name ? name : "unknown scenario"); messages.push_back(curmsg); if(!strcmp(elem, "send")) { checkOptionalRecv(elem, scenario_file_cursor); curmsg->M_type = MSG_TYPE_SEND; /* Sent messages descriptions */ if(!(ptr = xp_get_cdata())) { ERROR("No CDATA in 'send' section of xml scenario file"); } int removed_clrf = 0; char * msg = clean_cdata(ptr, &removed_clrf); L_content_length = xp_get_content_length(msg); switch (L_content_length) { case -1 : // the msg does not contain content-length field break ; case 0 : curmsg -> content_length_flag = message::ContentLengthValueZero; // Initialize to No present break ; default : curmsg -> content_length_flag = message::ContentLengthValueNoZero; // Initialize to No present break ; } if((msg[strlen(msg) - 1] != '\n') && (removed_clrf)) { strcat(msg, "\n"); } char *tsrc = msg; while(*tsrc++); curmsg -> send_scheme = new SendingMessage(this, msg); free(msg); // If this is a request we are sending, then store our transaction/method matching information. if (!curmsg->send_scheme->isResponse()) { char *method = curmsg->send_scheme->getMethod(); bool isInvite = !strcmp(method, "INVITE"); bool isAck = !strcmp(method, "ACK"); if ((cptr = xp_get_value("start_txn"))) { if (isAck) { ERROR("An ACK message can not start a transaction!"); } curmsg->start_txn = get_txn(cptr, "start transaction", true, isInvite, false); } else if ((cptr = xp_get_value("ack_txn"))) { if (!isAck) { ERROR("The ack_txn attribute is valid only for ACK messages!"); } curmsg->ack_txn = get_txn(cptr, "ack transaction", false, false, true); } else { int len = method_list ? strlen(method_list) : 0; method_list = (char *)realloc(method_list, len + strlen(method) + 1); if (!method_list) { ERROR_NO("Out of memory allocating method_list!"); } strcpy(method_list + len, method); } } else { if (xp_get_value("start_txn")) { ERROR("Responses can not start a transaction"); } if (xp_get_value("ack_txn")) { ERROR("Responses can not ACK a transaction"); } } if (xp_get_value("response_txn")) { ERROR("response_txn can only be used for received messages."); } curmsg -> retrans_delay = xp_get_long("retrans", "retransmission timer", 0); curmsg -> timeout = xp_get_long("timeout", "message send timeout", 0); } else if (!strcmp(elem, "recv")) { curmsg->M_type = MSG_TYPE_RECV; /* Received messages descriptions */ if((cptr = xp_get_value("response"))) { curmsg ->recv_response = get_long(cptr, "response code"); if (method_list) { curmsg->recv_response_for_cseq_method_list = strdup(method_list); } if ((cptr = xp_get_value("response_txn"))) { curmsg->response_txn = get_txn(cptr, "transaction response", false, false, false); } } if ((cptr = xp_get_value("request"))) { curmsg->recv_request = strdup(cptr); if (xp_get_value("response_txn")) { ERROR("response_txn can only be used for received responses."); } } curmsg->optional = xp_get_optional("optional", "recv"); last_recv_optional = curmsg->optional; curmsg->advance_state = xp_get_bool("advance_state", "recv", true); if (!curmsg->advance_state && curmsg->optional == OPTIONAL_FALSE) { ERROR("advance_state is allowed only for optional messages (index = %zu)", messages.size() - 1); } if ((cptr = xp_get_value("regexp_match"))) { if (!strcmp(cptr, "true")) { curmsg->regexp_match = 1; } } curmsg->timeout = xp_get_long("timeout", "message timeout", 0); /* record the route set */ /* TODO disallow optional and rrs to coexist? */ if ((cptr = xp_get_value("rrs"))) { curmsg->bShouldRecordRoutes = get_bool(cptr, "record route set"); } /* record the authentication credentials */ if ((cptr = xp_get_value("auth"))) { bool temp = get_bool(cptr, "message authentication"); curmsg->bShouldAuthenticate = temp; } } else if(!strcmp(elem, "pause") || !strcmp(elem, "timewait")) { checkOptionalRecv(elem, scenario_file_cursor); curmsg->M_type = MSG_TYPE_PAUSE; if (!strcmp(elem, "timewait")) { curmsg->timewait = true; found_timewait = true; } int var; if ((var = xp_get_var("variable", "pause", -1)) != -1) { curmsg->pause_variable = var; } else { CSample *distribution = parse_distribution(true); bool sanity_check = xp_get_bool("sanity_check", "pause", true); double pause_duration = distribution->cdfInv(0.99); if (sanity_check && (pause_duration > INT_MAX)) { char percentile[100]; char desc[100]; distribution->timeDescr(desc, sizeof(desc)); time_string(pause_duration, percentile, sizeof(percentile)); ERROR("The distribution %s has a 99th percentile of %s, which is larger than INT_MAX. You should chose different parameters.", desc, percentile); } curmsg->pause_distribution = distribution; /* Update scenario duration with max duration */ duration += (int)pause_duration; } } else if(!strcmp(elem, "nop")) { checkOptionalRecv(elem, scenario_file_cursor); /* Does nothing at SIP level. This message type can be used to handle * actions, increment counters, or for RTDs. */ curmsg->M_type = MSG_TYPE_NOP; } else if(!strcmp(elem, "recvCmd")) { curmsg->M_type = MSG_TYPE_RECVCMD; curmsg->optional = xp_get_optional("optional", "recv"); last_recv_optional = curmsg->optional; /* 3pcc extended mode */ if ((cptr = xp_get_value("src"))) { curmsg->peer_src = strdup(cptr); } else if (extendedTwinSippMode) { ERROR("You must specify a 'src' for recvCmd when using extended 3pcc mode!"); } } else if(!strcmp(elem, "sendCmd")) { checkOptionalRecv(elem, scenario_file_cursor); curmsg->M_type = MSG_TYPE_SENDCMD; /* Sent messages descriptions */ /* 3pcc extended mode */ if ((cptr = xp_get_value("dest"))) { peer = strdup(cptr); curmsg->peer_dest = peer; peer_map::iterator peer_it; peer_it = peers.find(peer_map::key_type(peer)); if(peer_it == peers.end()) /* the peer (slave or master) has not been added in the map (first occurrence in the scenario) */ { T_peer_infos infos = {}; infos.peer_socket = 0; strncpy(infos.peer_host, get_peer_addr(peer), sizeof(infos.peer_host) - 1); peers[std::string(peer)] = infos; } } else if (extendedTwinSippMode) { ERROR("You must specify a 'dest' for sendCmd with extended 3pcc mode!"); } if (!(ptr = xp_get_cdata())) { ERROR("No CDATA in 'sendCmd' section of xml scenario file"); } char *msg = clean_cdata(ptr); curmsg -> M_sendCmdData = new SendingMessage(this, msg, true /* skip sanity */); free(msg); } else { ERROR("Unknown element '%s' in xml scenario file", elem); } getCommonAttributes(curmsg); } /** end * Message case */ xp_close_element(); } // end while free(method_list); /* Close scenario element */ xp_close_element(); if (xp_is_invalid()) { ERROR("Invalid XML in scenario"); } str_int_map::iterator label_it = labelMap.find("_unexp.main"); if (label_it != labelMap.end()) { unexpected_jump = label_it->second; } else { unexpected_jump = -1; } retaddr = find_var("_unexp.retaddr"); pausedaddr = find_var("_unexp.pausedaddr"); /* Patch up the labels. */ apply_labels(messages, labelMap); apply_labels(initmessages, initLabelMap); /* Some post-scenario loading validation. */ stats->validateRtds(); /* Make sure that all variables are used more than once. */ validate_variable_usage(); /* Make sure that all started transactions have responses, and vice versa. */ validate_txn_usage(); if (messages.size() == 0) { ERROR("Did not find any messages inside of scenario!"); } } void scenario::runInit() { call *initcall; if (initmessages.size() > 0) { initcall = new call(main_scenario, NULL, NULL, "///main-init", 0, false, false, true); initcall->run(); } } void clear_int_str(int_str_map m) { for(int_str_map::iterator it = m.begin(); it != m.end(); it = m.begin()) { free(it->second); m.erase(it); } } void clear_str_int(str_int_map m) { for(str_int_map::iterator it = m.begin(); it != m.end(); it = m.begin()) { m.erase(it); } } void clear_int_int(int_int_map m) { for(int_int_map::iterator it = m.begin(); it != m.end(); it = m.begin()) { m.erase(it); } } scenario::~scenario() { for (msgvec::iterator i = messages.begin(); i != messages.end(); i++) { delete *i; } messages.clear(); free(name); allocVars->putTable(); delete stats; for (unsigned int i = 0; i < transactions.size(); i++) { free(transactions[i].name); } transactions.clear(); clear_str_int(labelMap); clear_str_int(initLabelMap); clear_str_int(txnMap); } CSample *parse_distribution(bool oldstyle = false) { CSample *distribution = NULL; const char *distname; const char *ptr = 0; if(!(distname = xp_get_value("distribution"))) { if (!oldstyle) { ERROR("statistically distributed actions or pauses requires 'distribution' parameter"); } if ((ptr = xp_get_value("normal"))) { distname = "normal"; } else if ((ptr = xp_get_value("exponential"))) { distname = "exponential"; } else if ((ptr = xp_get_value("lognormal"))) { distname = "lognormal"; } else if ((ptr = xp_get_value("weibull"))) { distname = "weibull"; } else if ((ptr = xp_get_value("pareto"))) { distname = "pareto"; } else if ((ptr = xp_get_value("gamma"))) { distname = "gamma"; } else if ((ptr = xp_get_value("min"))) { distname = "uniform"; } else if ((ptr = xp_get_value("max"))) { distname = "uniform"; } else if ((ptr = xp_get_value("milliseconds"))) { double val = get_double(ptr, "Pause milliseconds"); return new CFixed(val); } else { return new CDefaultPause(); } } if (!strcmp(distname, "fixed")) { double value = xp_get_double("value", "Fixed distribution"); distribution = new CFixed(value); } else if (!strcmp(distname, "uniform")) { double min = xp_get_double("min", "Uniform distribution"); double max = xp_get_double("max", "Uniform distribution"); distribution = new CUniform(min, max); #ifdef HAVE_GSL } else if (!strcmp(distname, "normal")) { double mean = xp_get_double("mean", "Normal distribution"); double stdev = xp_get_double("stdev", "Normal distribution"); distribution = new CNormal(mean, stdev); } else if (!strcmp(distname, "lognormal")) { double mean = xp_get_double("mean", "Lognormal distribution"); double stdev = xp_get_double("stdev", "Lognormal distribution"); distribution = new CLogNormal(mean, stdev); } else if (!strcmp(distname, "exponential")) { double mean = xp_get_double("mean", "Exponential distribution"); distribution = new CExponential(mean); } else if (!strcmp(distname, "weibull")) { double lambda = xp_get_double("lambda", "Weibull distribution"); double k = xp_get_double("k", "Weibull distribution"); distribution = new CWeibull(lambda, k); } else if (!strcmp(distname, "pareto")) { double k = xp_get_double("k", "Pareto distribution"); double xsubm = xp_get_double("x_m", "Pareto distribution"); distribution = new CPareto(k, xsubm); } else if (!strcmp(distname, "gpareto")) { double shape = xp_get_double("shape", "Generalized Pareto distribution"); double scale = xp_get_double("scale", "Generalized Pareto distribution"); double location = xp_get_double("location", "Generalized Pareto distribution"); distribution = new CGPareto(shape, scale, location); } else if (!strcmp(distname, "gamma")) { double k = xp_get_double("k", "Gamma distribution"); double theta = xp_get_double("theta", "Gamma distribution"); distribution = new CGamma(k, theta); } else if (!strcmp(distname, "negbin")) { double n = xp_get_double("n", "Negative Binomial distribution"); double p = xp_get_double("p", "Negative Binomial distribution"); distribution = new CNegBin(n, p); #else } else if (!strcmp(distname, "normal") || !strcmp(distname, "lognormal") || !strcmp(distname, "exponential") || !strcmp(distname, "pareto") || !strcmp(distname, "gamma") || !strcmp(distname, "negbin") || !strcmp(distname, "weibull")) { ERROR("The distribution '%s' is only available with GSL", distname); #endif } else { ERROR("Unknown distribution: %s", ptr); } return distribution; } /* 3pcc extended mode: * get the correspondances between * slave and master names and their * addresses */ void parse_slave_cfg() { FILE * f; char line[MAX_PEER_SIZE]; char * temp_peer; char * temp_host; char * peer_host; f = fopen(slave_cfg_file, "r"); if(f) { while (fgets(line, MAX_PEER_SIZE, f) != NULL) { temp_peer = strtok(line, ";"); if (!temp_peer) continue; temp_host = strtok(NULL, ";"); if (!temp_host) continue; peer_host = strdup(temp_host); if (!peer_host) ERROR("Cannot allocate memory!"); peer_addrs[std::string(temp_peer)] = peer_host; } } else { ERROR("Can not open slave_cfg file %s", slave_cfg_file); } fclose(f); } // Determine in which mode the sipp tool has been // launched (client, server, 3pcc client, 3pcc server, 3pcc extended master or slave) void scenario::computeSippMode() { bool isRecvCmdFound = false; bool isSendCmdFound = false; creationMode = -1; sendMode = -1; thirdPartyMode = MODE_3PCC_NONE; assert(messages.size() > 0); for(unsigned int i=0; iM_type) { case MSG_TYPE_PAUSE: case MSG_TYPE_NOP: /* Allow pauses or nops to go first. */ continue; case MSG_TYPE_SEND: if (sendMode == -1) { sendMode = MODE_CLIENT; } if (creationMode == -1) { creationMode = MODE_CLIENT; } break; case MSG_TYPE_RECV: if (sendMode == -1) { sendMode = MODE_SERVER; } if (creationMode == -1) { creationMode = MODE_SERVER; } break; case MSG_TYPE_SENDCMD: isSendCmdFound = true; if (creationMode == -1) { creationMode = MODE_CLIENT; } if(!isRecvCmdFound) { if (creationMode == MODE_SERVER) { /* * If it is a server already, then start it in * 3PCC A passive mode */ if(twinSippMode) { thirdPartyMode = MODE_3PCC_A_PASSIVE; } else if (extendedTwinSippMode) { thirdPartyMode = MODE_MASTER_PASSIVE; } } else { if(twinSippMode) { thirdPartyMode = MODE_3PCC_CONTROLLER_A; } else if (extendedTwinSippMode) { thirdPartyMode = MODE_MASTER; } } if((thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_MASTER) && !master_name) { ERROR("Inconsistency between command line and scenario: master scenario but -master option not set"); } if(!twinSippMode && !extendedTwinSippMode) ERROR("sendCmd message found in scenario but no twin sipp" " address has been passed! Use -3pcc option or 3pcc extended mode"); } break; case MSG_TYPE_RECVCMD: if (creationMode == -1) { creationMode = MODE_SERVER; } isRecvCmdFound = true; if(!isSendCmdFound) { if(twinSippMode) { thirdPartyMode = MODE_3PCC_CONTROLLER_B; } else if(extendedTwinSippMode) { thirdPartyMode = MODE_SLAVE; if(!slave_number) { ERROR("Inconsistency between command line and scenario: slave scenario but -slave option not set"); } else { thirdPartyMode = MODE_SLAVE; } } if(!twinSippMode && !extendedTwinSippMode) ERROR("recvCmd message found in scenario but no " "twin sipp address has been passed! Use " "-3pcc option\n"); } break; default: break; } } if(creationMode == -1) ERROR("Unable to determine creation mode of the tool (server, client)"); if(sendMode == -1) ERROR("Unable to determine send mode of the tool (server, client)"); } void scenario::handle_rhs(CAction *tmpAction, const char *what) { if (xp_get_value("value")) { tmpAction->setDoubleValue(xp_get_double("value", what)); if (xp_get_value("variable")) { ERROR("Value and variable are mutually exclusive for %s action!", what); } } else if (xp_get_value("variable")) { tmpAction->setVarInId(xp_get_var("variable", what)); if (xp_get_value("value")) { ERROR("Value and variable are mutually exclusive for %s action!", what); } } else { ERROR("No value or variable defined for %s action!", what); } } void scenario::handle_arithmetic(CAction *tmpAction, const char *what) { tmpAction->setVarId(xp_get_var("assign_to", what)); handle_rhs(tmpAction, what); } void scenario::parseAction(CActions *actions) { char * actionElem; unsigned int recvScenarioLen = 0; char ** currentTabVarName = NULL; int currentNbVarNames; int sub_currentNbVarId; char* ptr; const char* cptr; while((actionElem = xp_open_element(recvScenarioLen))) { CAction *tmpAction = new CAction(this); if(!strcmp(actionElem, "ereg")) { ptr = xp_get_string("regexp", "ereg"); tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_REGEXP); // warning - although these are detected for both msg and hdr // they are only implemented for search_in="hdr" tmpAction->setCaseIndep(xp_get_bool("case_indep", "ereg", false)); tmpAction->setHeadersOnly(xp_get_bool("start_line", "ereg", false)); if ((cptr = xp_get_value("search_in"))) { tmpAction->setOccurrence(1); if (strcmp(cptr, "msg") == 0) { tmpAction->setLookingPlace(CAction::E_LP_MSG); tmpAction->setLookingChar (NULL); } else if (strcmp(cptr, "body") == 0) { tmpAction->setLookingPlace(CAction::E_LP_BODY); tmpAction->setLookingChar (NULL); } else if (strcmp(cptr, "var") == 0) { tmpAction->setVarInId(xp_get_var("variable", "ereg")); tmpAction->setLookingPlace(CAction::E_LP_VAR); } else if (strcmp(cptr, "hdr") == 0) { cptr = xp_get_value("header"); if (!cptr || !strlen(cptr)) { ERROR("search_in=\"hdr\" requires header field"); } tmpAction->setLookingPlace(CAction::E_LP_HDR); tmpAction->setLookingChar(cptr); if ((cptr = xp_get_value("occurrence"))) { tmpAction->setOccurrence(atol(cptr)); } else if ((cptr = xp_get_value("occurence"))) { /* old misspelling */ tmpAction->setOccurrence(atol(cptr)); } } else { ERROR("Unknown search_in value %s", cptr); } } else { tmpAction->setLookingPlace(CAction::E_LP_MSG); tmpAction->setLookingChar(NULL); } // end if-else search_in if (xp_get_value("check_it")) { tmpAction->setCheckIt(xp_get_bool("check_it", "ereg", false)); if (xp_get_value("check_it_inverse")) { ERROR("Can not have both check_it and check_it_inverse for ereg!"); } } else { tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "ereg", false)); } if (!(cptr = xp_get_value("assign_to"))) { ERROR("assign_to value is missing"); } createStringTable(cptr, ¤tTabVarName, ¤tNbVarNames); int varId = get_var(currentTabVarName[0], "assign_to"); tmpAction->setVarId(varId); tmpAction->setRegExp(ptr); if (currentNbVarNames > 1 ) { sub_currentNbVarId = currentNbVarNames - 1 ; tmpAction->setNbSubVarId(sub_currentNbVarId); for(int i=1; i<= sub_currentNbVarId; i++) { int varId = get_var(currentTabVarName[i], "sub expression assign_to"); tmpAction->setSubVarId(varId); } } freeStringTable(currentTabVarName, currentNbVarNames); free(ptr); } /* end !strcmp(actionElem, "ereg") */ else if(!strcmp(actionElem, "log")) { ptr = xp_get_string("message", "log"); tmpAction->setMessage(ptr); free(ptr); tmpAction->setActionType(CAction::E_AT_LOG_TO_FILE); } else if(!strcmp(actionElem, "warning")) { ptr = xp_get_string("message", "warning"); tmpAction->setMessage(ptr); free(ptr); tmpAction->setActionType(CAction::E_AT_LOG_WARNING); } else if(!strcmp(actionElem, "error")) { ptr = xp_get_string("message", "error"); tmpAction->setMessage(ptr); free(ptr); tmpAction->setActionType(CAction::E_AT_LOG_ERROR); } else if(!strcmp(actionElem, "assign")) { tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_VALUE); handle_arithmetic(tmpAction, "assign"); } else if(!strcmp(actionElem, "assignstr")) { tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_STRING); tmpAction->setVarId(xp_get_var("assign_to", "assignstr")); ptr = xp_get_string("value", "assignstr"); tmpAction->setMessage(ptr); free(ptr); } else if(!strcmp(actionElem, "gettimeofday")) { tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY); if (!(cptr = xp_get_value("assign_to"))) { ERROR("assign_to value is missing"); } createStringTable(cptr, ¤tTabVarName, ¤tNbVarNames); if (currentNbVarNames != 2 ) { ERROR("The gettimeofday action requires two output variables!"); } tmpAction->setNbSubVarId(1); int varId = get_var(currentTabVarName[0], "gettimeofday seconds assign_to"); tmpAction->setVarId(varId); varId = get_var(currentTabVarName[1], "gettimeofday useconds assign_to"); tmpAction->setSubVarId(varId); freeStringTable(currentTabVarName, currentNbVarNames); } else if(!strcmp(actionElem, "index")) { tmpAction->setVarId(xp_get_var("assign_to", "index")); tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_INDEX); } else if(!strcmp(actionElem, "jump")) { tmpAction->setActionType(CAction::E_AT_JUMP); handle_rhs(tmpAction, "jump"); } else if(!strcmp(actionElem, "pauserestore")) { tmpAction->setActionType(CAction::E_AT_PAUSE_RESTORE); handle_rhs(tmpAction, "pauserestore"); } else if(!strcmp(actionElem, "add")) { tmpAction->setActionType(CAction::E_AT_VAR_ADD); handle_arithmetic(tmpAction, "add"); } else if(!strcmp(actionElem, "subtract")) { tmpAction->setActionType(CAction::E_AT_VAR_SUBTRACT); handle_arithmetic(tmpAction, "subtract"); } else if(!strcmp(actionElem, "multiply")) { tmpAction->setActionType(CAction::E_AT_VAR_MULTIPLY); handle_arithmetic(tmpAction, "multiply"); } else if(!strcmp(actionElem, "divide")) { tmpAction->setActionType(CAction::E_AT_VAR_DIVIDE); handle_arithmetic(tmpAction, "divide"); if (tmpAction->getVarInId() == 0) { if (tmpAction->getDoubleValue() == 0.0) { ERROR("divide actions can not have a value of zero!"); } } } else if(!strcmp(actionElem, "sample")) { tmpAction->setVarId(xp_get_var("assign_to", "sample")); tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_SAMPLE); tmpAction->setDistribution(parse_distribution()); } else if(!strcmp(actionElem, "todouble")) { tmpAction->setActionType(CAction::E_AT_VAR_TO_DOUBLE); tmpAction->setVarId(xp_get_var("assign_to", "todouble")); tmpAction->setVarInId(xp_get_var("variable", "todouble")); } else if(!strcmp(actionElem, "test")) { if (xp_get_value("check_it")) { tmpAction->setCheckIt(xp_get_bool("check_it", "test")); if (xp_get_value("check_it_inverse")) { ERROR("Can not have both check_it and check_it_inverse for test!"); } } else if (xp_get_value("check_it_inverse")) { tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "test")); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (xp_get_value("assign_to") || (!xp_get_value("check_it") && !xp_get_value("check_it_inverse")) ) { tmpAction->setVarId(xp_get_var("assign_to", "test")); } tmpAction->setVarInId(xp_get_var("variable", "test")); if (xp_get_value("value")) { tmpAction->setDoubleValue(xp_get_double("value", "test")); if (xp_get_value("variable2")) { ERROR("Can not have both a value and a variable2 for test!"); } } else { tmpAction->setVarIn2Id(xp_get_var("variable2", "test")); } tmpAction->setActionType(CAction::E_AT_VAR_TEST); ptr = xp_get_string("compare", "test"); if (!strcmp(ptr, "equal")) { tmpAction->setComparator(CAction::E_C_EQ); } else if (!strcmp(ptr, "not_equal")) { tmpAction->setComparator(CAction::E_C_NE); } else if (!strcmp(ptr, "greater_than")) { tmpAction->setComparator(CAction::E_C_GT); } else if (!strcmp(ptr, "less_than")) { tmpAction->setComparator(CAction::E_C_LT); } else if (!strcmp(ptr, "greater_than_equal")) { tmpAction->setComparator(CAction::E_C_GEQ); } else if (!strcmp(ptr, "less_than_equal")) { tmpAction->setComparator(CAction::E_C_LEQ); } else { ERROR("Invalid 'compare' parameter: %s", ptr); } free(ptr); } else if(!strcmp(actionElem, "verifyauth")) { tmpAction->setVarId(xp_get_var("assign_to", "verifyauth")); char* username_ptr = xp_get_string("username", "verifyauth"); char* password_ptr = xp_get_string("password", "verifyauth"); tmpAction->setMessage(username_ptr, 0); tmpAction->setMessage(password_ptr, 1); tmpAction->setActionType(CAction::E_AT_VERIFY_AUTH); free(username_ptr); free(password_ptr); username_ptr = password_ptr = NULL; } else if(!strcmp(actionElem, "lookup")) { tmpAction->setVarId(xp_get_var("assign_to", "lookup")); tmpAction->setMessage(xp_get_string("file", "lookup"), 0); tmpAction->setMessage(xp_get_string("key", "lookup"), 1); tmpAction->setActionType(CAction::E_AT_LOOKUP); } else if(!strcmp(actionElem, "insert")) { tmpAction->setMessage(xp_get_string("file", "insert"), 0); tmpAction->setMessage(xp_get_string("value", "insert"), 1); tmpAction->setActionType(CAction::E_AT_INSERT); } else if(!strcmp(actionElem, "replace")) { tmpAction->setMessage(xp_get_string("file", "replace"), 0); tmpAction->setMessage(xp_get_string("line", "replace"), 1); tmpAction->setMessage(xp_get_string("value", "replace"), 2); tmpAction->setActionType(CAction::E_AT_REPLACE); } else if(!strcmp(actionElem, "setdest")) { tmpAction->setMessage(xp_get_string("host", actionElem), 0); tmpAction->setMessage(xp_get_string("port", actionElem), 1); tmpAction->setMessage(xp_get_string("protocol", actionElem), 2); tmpAction->setActionType(CAction::E_AT_SET_DEST); } else if(!strcmp(actionElem, "closecon")) { tmpAction->setActionType(CAction::E_AT_CLOSE_CON); } else if(!strcmp(actionElem, "strcmp")) { if (xp_get_value("check_it")) { tmpAction->setCheckIt(xp_get_bool("check_it", "strcmp")); if (xp_get_value("check_it_inverse")) { ERROR("Can not have both check_it and check_it_inverse for strcmp!"); } } else if (xp_get_value("check_it_inverse")) { tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "strcmp")); } // "assign_to" is optional when "check_it" or "check_it_inverse" set if (xp_get_value("assign_to") || (!xp_get_value("check_it") && !xp_get_value("check_it_inverse")) ) { tmpAction->setVarId(xp_get_var("assign_to", "strcmp")); } tmpAction->setVarInId(xp_get_var("variable", "strcmp")); if (xp_get_value("value")) { tmpAction->setStringValue(xp_get_string("value", "strcmp")); if (xp_get_value("variable2")) { ERROR("Can not have both a value and a variable2 for strcmp!"); } } else { tmpAction->setVarIn2Id(xp_get_var("variable2", "strcmp")); } tmpAction->setActionType(CAction::E_AT_VAR_STRCMP); } else if(!strcmp(actionElem, "trim")) { tmpAction->setVarId(xp_get_var("assign_to", "trim")); tmpAction->setActionType(CAction::E_AT_VAR_TRIM); } else if(!strcmp(actionElem, "exec")) { if ((cptr = xp_get_value("command"))) { tmpAction->setActionType(CAction::E_AT_EXECUTE_CMD); tmpAction->setMessage(cptr); } else if((cptr = xp_get_value("int_cmd"))) { CAction::T_IntCmdType type(CAction::E_INTCMD_STOPCALL); /* assume the default */ if (strcmp(cptr, "stop_now") == 0) { type = CAction::E_INTCMD_STOP_NOW; } else if (strcmp(cptr, "stop_gracefully") == 0) { type = CAction::E_INTCMD_STOP_ALL; } else if (strcmp(cptr, "stop_call") == 0) { type = CAction::E_INTCMD_STOPCALL; } /* the action is well formed, adding it in the */ /* tmpActionTable */ tmpAction->setActionType(CAction::E_AT_EXEC_INTCMD); tmpAction->setIntCmd(type); #ifdef PCAPPLAY } else if ((ptr = xp_get_keyword_value("play_pcap_audio"))) { tmpAction->setPcapArgs(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_AUDIO); hasMedia = 1; free(ptr); } else if ((ptr = xp_get_keyword_value("play_pcap_image"))) { tmpAction->setPcapArgs(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_IMAGE); hasMedia = 1; free(ptr); } else if ((ptr = xp_get_keyword_value("play_pcap_video"))) { tmpAction->setPcapArgs(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_VIDEO); hasMedia = 1; free(ptr); } else if ((cptr = xp_get_value("play_dtmf"))) { tmpAction->setMessage(cptr); tmpAction->setActionType(CAction::E_AT_PLAY_DTMF); hasMedia = 1; #else } else if (xp_get_value("play_pcap_audio")) { ERROR("Scenario specifies a play_pcap_audio action, but this version of SIPp does not have PCAP support"); } else if (xp_get_value("play_pcap_image")) { ERROR("Scenario specifies a play_pcap_image action, but this version of SIPp does not have PCAP support"); } else if (xp_get_value("play_pcap_video")) { ERROR("Scenario specifies a play_pcap_video action, but this version of SIPp does not have PCAP support"); } else if (xp_get_value("play_dtmf")) { ERROR("Scenario specifies a play_dtmf action, but this version of SIPp does not have PCAP support"); #endif #ifdef RTP_STREAM } else if ((ptr = xp_get_keyword_value("rtp_stream"))) { hasMedia = 1; if (strcmp(ptr, "pause") == 0) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PAUSE); } else if (strcmp(ptr, "resume") == 0) { tmpAction->setActionType(CAction::E_AT_RTP_STREAM_RESUME); } else { tmpAction->setRTPStreamActInfo(ptr); tmpAction->setActionType(CAction::E_AT_RTP_STREAM_PLAY); } free(ptr); #else } else if ((cptr = xp_get_value("rtp_stream"))) { ERROR("Scenario specifies a rtp_stream action, but this version of SIPp does not have RTP stream support"); #endif } else { ERROR("illegal in the scenario"); } } else if(!strcmp(actionElem, "rtp_echo")) { #ifdef RTP_STREAM tmpAction->setActionType(CAction::E_AT_RTP_ECHO); handle_rhs(tmpAction, "rtp_echo"); #else ERROR("Scenario specifies a rtp_echo action, but this version of SIPp does not have RTP stream support"); #endif } else { ERROR("Unknown action: %s", actionElem); } /* If the action was not well-formed, there should have already been an * ERROR declaration, thus it is safe to add it here at the end of the loop. */ actions->setAction(tmpAction); xp_close_element(); recvScenarioLen++; } // end while } // Action list for the message indexed by message_index in // the scenario void scenario::getActionForThisMessage(message *message) { char * actionElem; if (!(actionElem = xp_open_element(0))) { return; } if (strcmp(actionElem, "action")) { xp_close_element(); return; } /* We actually have an action element. */ if (message->M_actions != NULL) { ERROR("Duplicate action for %s index %d", message->desc, message->index); } message->M_actions = new CActions(); parseAction(message->M_actions); xp_close_element(); } void scenario::getBookKeeping(message *message) { const char *ptr; if ((ptr = xp_get_value("rtd"))) { message->stop_rtd = get_rtd(ptr, false); } if ((ptr = xp_get_value("repeat_rtd"))) { if (message->stop_rtd) { message->repeat_rtd = get_bool(ptr, "repeat_rtd"); } else { ERROR("There is a repeat_rtd element without an rtd element"); } } if ((ptr = xp_get_value("start_rtd"))) { message->start_rtd = get_rtd(ptr, true); } if ((ptr = xp_get_value("counter"))) { message->counter = get_counter(ptr, "counter"); } } void scenario::getCommonAttributes(message *message) { const char *ptr; getBookKeeping(message); getActionForThisMessage(message); if ((ptr = xp_get_value("lost"))) { message -> lost = get_double(ptr, "lost percentage"); lose_packets = 1; } if ((ptr = xp_get_value("crlf"))) { message -> crlf = 1; } if ((ptr = xp_get_value("ignoresdp"))) { message->ignoresdp = get_bool(ptr, "ignoresdp"); } if (xp_get_value("hiderest")) { hidedefault = xp_get_bool("hiderest", "hiderest"); } message -> hide = xp_get_bool("hide", "hide", hidedefault); if((ptr = xp_get_value((char *)"display"))) { message -> display_str = strdup(ptr); } message -> condexec = xp_get_var("condexec", "condexec variable", -1); message -> condexec_inverse = xp_get_bool("condexec_inverse", "condexec_inverse", false); if ((ptr = xp_get_value("next"))) { if (found_timewait) { ERROR("next labels are not allowed in elements."); } message->nextLabel = strdup(ptr); message->test = xp_get_var("test", "test variable", -1); if ( 0 != ( ptr = xp_get_value((char *)"chance") ) ) { float chance = get_double(ptr,"chance"); /* probability of branch to next */ if (( chance < 0.0 ) || (chance > 1.0 )) { ERROR("Chance %s not in range [0..1]", ptr); } message -> chance = (int)((1.0-chance)*RAND_MAX); } else { message -> chance = 0; /* always */ } } if ((ptr = xp_get_value((char *)"ontimeout"))) { if (found_timewait) { ERROR("ontimeout labels are not allowed in elements."); } message -> onTimeoutLabel = strdup(ptr); } } // char* manipulation : create a int[] from a char* // test first is the char* is formed by int separeted by coma // and then create the table int isWellFormed(char * P_listeStr, int * nombre) { char * ptr = P_listeStr; int sizeOf; bool isANumber; (*nombre) = 0; sizeOf = strlen(P_listeStr); // getting the number if(sizeOf > 0) { // is the string well formed ? [0-9] [,] isANumber = false; for(int i=0; i<=sizeOf; i++) { switch(ptr[i]) { case ',': if(isANumber == false) { return(0); } else { (*nombre)++; } isANumber = false; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': isANumber = true; break; case '\t': case ' ' : break; case '\0': if(isANumber == false) { return(0); } else { (*nombre)++; } break; default: return(0); } } // end for } return(1); } int createIntegerTable(char * P_listeStr, unsigned int ** listeInteger, int * sizeOfList) { int nb=0; char * ptr = P_listeStr; char * ptr_prev = P_listeStr; unsigned int current_int; if(P_listeStr) { if(isWellFormed(P_listeStr, sizeOfList) == 1) { (*listeInteger) = new unsigned int[(*sizeOfList)]; while((*ptr) != ('\0')) { if((*ptr) == ',') { sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; ptr_prev = ptr+1; } ptr++; } // Read the last sscanf(ptr_prev, "%u", ¤t_int); if (nb<(*sizeOfList)) (*listeInteger)[nb] = current_int; nb++; return(1); } return(0); } else return(0); } int createStringTable(const char* inputString, char*** stringList, int* sizeOfList) { if(!inputString) { return 0; } *stringList = NULL; *sizeOfList = 0; /* FIXME: temporary workaround: needs rewrite */ char* input = const_cast(inputString); do { char* p = strchr(input, ','); if (p) { *p++ = '\0'; } *stringList = (char **)realloc(*stringList, sizeof(char *) * (*sizeOfList + 1)); (*stringList)[*sizeOfList] = strdup(input); (*sizeOfList)++; input = p; } while (input); return 1; } void freeStringTable(char ** stringList, int sizeOfList) { for (int i = 0; i < sizeOfList; i++) { free(stringList[i]); } free(stringList); } /* These are the names of the scenarios, they must match the default_scenario table. */ const char *scenario_table[] = { "uac", "uas", "regexp", "3pcc-C-A", "3pcc-C-B", "3pcc-A", "3pcc-B", "branchc", "branchs", "uac_pcap", "ooc_default", "ooc_dummy", }; int find_scenario(const char *scenario) { int i, max; max = sizeof(scenario_table)/sizeof(scenario_table[0]); for (i = 0; i < max; i++) { if (!strcmp(scenario_table[i], scenario)) { return i; } } ERROR("Invalid default scenario name '%s'", scenario); return -1; } // TIP: to integrate an existing XML scenario, use the following sed line: // cat ../3pcc-controller-B.xml | sed -e 's/\"/\\\"/g' -e 's/\(.*\)/\"\1\\n\"/' const char * default_scenario [] = { /************* Default_scenario[0] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag00[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag00[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag00[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n" , /************* Default_scenario[1] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n", /************* Default_scenario[2] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag02[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " ;tag=[pid]SIPpTag02[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " retrievedIp: [$1]\n" " retrievedContact:[$6]\n" " retrievedSdpOrigin:[$3]\n" " retrievedSdpOrigin-username:[$4]\n" " retrievedSdpOrigin-session-id:[$5]\n" " retrievedSdpOrigin-version:[$8]\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag02[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n", /************* Default_scenario[3] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag03[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag03[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " [$2]\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag03[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" "\n" "\n", /************* Default_scenario[4] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag04[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " [$1]\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag04[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag04[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" "\n" "\n" "\n", /************* Default_scenario[5] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" "\n", /************* Default_scenario[6] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[media_ip_type] [media_ip]\n" " t=0 0\n" " m=audio [media_port] RTP/AVP 0\n" " a=rtpmap:0 PCMU/8000\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n", /************* Default_scenario[7] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " ;tag=[pid]SIPpTag07[call_number]\n" " To: ua1 \n" " Call-ID: [call_id]\n" " CSeq: 1 REGISTER\n" " Contact: sip:ua1@[local_ip]:[local_port]\n" " Content-Length: 0\n" " Expires: 300\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" "\n", /************* Default_scenario[8] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" " Expires: 300\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" "\n", /* Although this scenario will not work without pcap play enabled, there is no * harm in including it in the binary anyway, because the user could have * dumped it and passed it with -sf. */ /************* Default_scenario[9] ***************/ "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag09[call_number]\n" " To: [service] \n" " Call-ID: [call_id]\n" " CSeq: 1 INVITE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Type: application/sdp\n" " Content-Length: [len]\n" "\n" " v=0\n" " o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]\n" " s=-\n" " c=IN IP[local_ip_type] [local_ip]\n" " t=0 0\n" " m=audio [auto_media_port] RTP/AVP 8 101\n" " a=rtpmap:8 PCMA/8000\n" " a=rtpmap:101 telephone-event/8000\n" " a=fmtp:101 0-11,16\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " ;tag=[pid]SIPpTag09[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 1 ACK\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" " \n" "\n" " \n" " \n" " ;tag=[pid]SIPpTag09[call_number]\n" " To: [service] [peer_tag_param]\n" " Call-ID: [call_id]\n" " CSeq: 2 BYE\n" " Contact: sip:sipp@[local_ip]:[local_port]\n" " Max-Forwards: 70\n" " Subject: Performance Test\n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n" "\n", "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" " Content-Length: 0\n" "\n" " ]]>\n" " \n" "\n" " \n" " \n" " \n" "\n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n", "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" "\n" " \n" " \n" "\n" "\n", }; sipp-3.6.1/src/socketowner.cpp0000664000175000017500000001000213730472040015703 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Olivier Jacques * From Hewlett Packard Company. * Shriram Natarajan * Peter Higginson * Eric Miller * Venkatesh * Enrico Hartung * Nasir Khan * Lee Ballard * Guillaume Teissier from FTR&D * Wolfgang Beck * Venkatesh * Vlad Troyanker * Charles P Wright from IBM Research * Amit On from Followap * Jan Andres from Freenet * Ben Evans from Open Cloud * Marc Van Diest from Belgacom * Michael Dwyer from Cibation */ #include #include #include #include #include #include #include #include "sipp.hpp" socket_owner_map_map socket_to_owners; SIPpSocket *socketowner::associate_socket(SIPpSocket *socket) { if (socket) { this->call_socket = socket; add_owner_to_socket(socket); } return socket; } SIPpSocket *socketowner::dissociate_socket() { SIPpSocket *ret = this->call_socket; remove_owner_from_socket(this->call_socket); this->call_socket = NULL; return ret; } unsigned long socketowner::nextownerid = 1; socketowner::socketowner() { this->call_socket = NULL; this->ownerid = socketowner::nextownerid++; } socketowner::~socketowner() { if (this->call_socket) { dissociate_socket()->close(); } } void socketowner::add_owner_to_socket(SIPpSocket *socket) { socket_owner_map_map::iterator map_it = socket_to_owners.find(socket); /* No map defined for this socket. */ if (map_it == socket_to_owners.end()) { socket_to_owners.insert(socket_map_pair(socket, new owner_map)); map_it = socket_to_owners.find(socket); assert(map_it != socket_to_owners.end()); } owner_map *socket_owner_map = (owner_map *) map_it->second; socket_owner_map->insert(long_owner_pair(this->ownerid, this)); } void socketowner::remove_owner_from_socket(SIPpSocket *socket) { socket_owner_map_map::iterator map_it = socket_to_owners.find(socket); /* We must have a map for this socket. */ assert(map_it != socket_to_owners.end()); owner_map *socket_owner_map = (owner_map *) map_it->second; owner_map::iterator owner_it = socket_owner_map->find(this->ownerid); /* And our owner must exist in the map. */ assert(owner_it != socket_owner_map->end()); socket_owner_map->erase(owner_it); /* If we have no more calls, we can delete this entry. */ if (socket_owner_map->begin() == socket_owner_map->end()) { delete socket_owner_map; socket_to_owners.erase(map_it); } } /* The caller must delete this list. */ owner_list *get_owners_for_socket(SIPpSocket *socket) { owner_list *l = new owner_list; socket_owner_map_map::iterator map_it = socket_to_owners.find(socket); /* No map defined for this socket. */ if (map_it == socket_to_owners.end()) { return l; } owner_map *socket_owner_map = (owner_map *) map_it->second; owner_map::iterator owner_it; for (owner_it = socket_owner_map->begin(); owner_it != socket_owner_map->end(); owner_it++) { l->insert(l->end(), owner_it->second); } return l; } sipp-3.6.1/src/rijndael.c0000664000175000017500000007171513730472040014612 0ustar walterwalter/*------------------------------------------------------------------- * Rijndael Implementation *------------------------------------------------------------------- * * A sample 32-bit orientated implementation of Rijndael, the * suggested kernel for the example 3GPP authentication and key * agreement functions. * * This implementation draws on the description in section 5.2 of * the AES proposal and also on the implementation by * Dr B. R. Gladman 9th October 2000. * It uses a number of large (4k) lookup tables to implement the * algorithm in an efficient manner. * * Note: in this implementation the State is stored in four 32-bit * words, one per column of the State, with the top byte of the * column being the _least_ significant byte of the word. * *-----------------------------------------------------------------*/ #include "rijndael.h" #include #define LITTLE_ENDIAN /* For INTEL architecture */ /* Circular byte rotates of 32 bit values */ #define rot1(x) ((x << 8) | (x >> 24)) #define rot2(x) ((x << 16) | (x >> 16)) #define rot3(x) ((x << 24) | (x >> 8)) /* Extract a byte from a 32-bit uint32_t */ #define byte0(x) ((uint8_t)(x)) #define byte1(x) ((uint8_t)(x >> 8)) #define byte2(x) ((uint8_t)(x >> 16)) #define byte3(x) ((uint8_t)(x >> 24)) /* Put or get a 32 bit uint32_t (v) in machine order from a byte * * address in (x) */ #ifdef LITTLE_ENDIAN #define u32_in(x) (*(uint32_t*)(x)) #define u32_out(x,y) (*(uint32_t*)(x) = y) #else /* Invert byte order in a 32 bit variable */ __inline uint32_t byte_swap(const uint32_t x) { return rot1(x) & 0x00ff00ff | rot3(x) & 0xff00ff00; } __inline uint32_t u32_in(const uint8_t x[]) { return byte_swap(*(uint32_t*)x); }; __inline void u32_out(uint8_t x[], const uint32_t v) { *(uint32_t*)x = byte_swap(v); }; #endif /*--------------- The lookup tables ----------------------------*/ static uint32_t rnd_con[10] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 }; static uint32_t ft_tab[4][256] = { { 0xA56363C6,0x847C7CF8,0x997777EE,0x8D7B7BF6,0x0DF2F2FF,0xBD6B6BD6,0xB16F6FDE,0x54C5C591, 0x50303060,0x03010102,0xA96767CE,0x7D2B2B56,0x19FEFEE7,0x62D7D7B5,0xE6ABAB4D,0x9A7676EC, 0x45CACA8F,0x9D82821F,0x40C9C989,0x877D7DFA,0x15FAFAEF,0xEB5959B2,0xC947478E,0x0BF0F0FB, 0xECADAD41,0x67D4D4B3,0xFDA2A25F,0xEAAFAF45,0xBF9C9C23,0xF7A4A453,0x967272E4,0x5BC0C09B, 0xC2B7B775,0x1CFDFDE1,0xAE93933D,0x6A26264C,0x5A36366C,0x413F3F7E,0x02F7F7F5,0x4FCCCC83, 0x5C343468,0xF4A5A551,0x34E5E5D1,0x08F1F1F9,0x937171E2,0x73D8D8AB,0x53313162,0x3F15152A, 0x0C040408,0x52C7C795,0x65232346,0x5EC3C39D,0x28181830,0xA1969637,0x0F05050A,0xB59A9A2F, 0x0907070E,0x36121224,0x9B80801B,0x3DE2E2DF,0x26EBEBCD,0x6927274E,0xCDB2B27F,0x9F7575EA, 0x1B090912,0x9E83831D,0x742C2C58,0x2E1A1A34,0x2D1B1B36,0xB26E6EDC,0xEE5A5AB4,0xFBA0A05B, 0xF65252A4,0x4D3B3B76,0x61D6D6B7,0xCEB3B37D,0x7B292952,0x3EE3E3DD,0x712F2F5E,0x97848413, 0xF55353A6,0x68D1D1B9,0000000000,0x2CEDEDC1,0x60202040,0x1FFCFCE3,0xC8B1B179,0xED5B5BB6, 0xBE6A6AD4,0x46CBCB8D,0xD9BEBE67,0x4B393972,0xDE4A4A94,0xD44C4C98,0xE85858B0,0x4ACFCF85, 0x6BD0D0BB,0x2AEFEFC5,0xE5AAAA4F,0x16FBFBED,0xC5434386,0xD74D4D9A,0x55333366,0x94858511, 0xCF45458A,0x10F9F9E9,0x06020204,0x817F7FFE,0xF05050A0,0x443C3C78,0xBA9F9F25,0xE3A8A84B, 0xF35151A2,0xFEA3A35D,0xC0404080,0x8A8F8F05,0xAD92923F,0xBC9D9D21,0x48383870,0x04F5F5F1, 0xDFBCBC63,0xC1B6B677,0x75DADAAF,0x63212142,0x30101020,0x1AFFFFE5,0x0EF3F3FD,0x6DD2D2BF, 0x4CCDCD81,0x140C0C18,0x35131326,0x2FECECC3,0xE15F5FBE,0xA2979735,0xCC444488,0x3917172E, 0x57C4C493,0xF2A7A755,0x827E7EFC,0x473D3D7A,0xAC6464C8,0xE75D5DBA,0x2B191932,0x957373E6, 0xA06060C0,0x98818119,0xD14F4F9E,0x7FDCDCA3,0x66222244,0x7E2A2A54,0xAB90903B,0x8388880B, 0xCA46468C,0x29EEEEC7,0xD3B8B86B,0x3C141428,0x79DEDEA7,0xE25E5EBC,0x1D0B0B16,0x76DBDBAD, 0x3BE0E0DB,0x56323264,0x4E3A3A74,0x1E0A0A14,0xDB494992,0x0A06060C,0x6C242448,0xE45C5CB8, 0x5DC2C29F,0x6ED3D3BD,0xEFACAC43,0xA66262C4,0xA8919139,0xA4959531,0x37E4E4D3,0x8B7979F2, 0x32E7E7D5,0x43C8C88B,0x5937376E,0xB76D6DDA,0x8C8D8D01,0x64D5D5B1,0xD24E4E9C,0xE0A9A949, 0xB46C6CD8,0xFA5656AC,0x07F4F4F3,0x25EAEACF,0xAF6565CA,0x8E7A7AF4,0xE9AEAE47,0x18080810, 0xD5BABA6F,0x887878F0,0x6F25254A,0x722E2E5C,0x241C1C38,0xF1A6A657,0xC7B4B473,0x51C6C697, 0x23E8E8CB,0x7CDDDDA1,0x9C7474E8,0x211F1F3E,0xDD4B4B96,0xDCBDBD61,0x868B8B0D,0x858A8A0F, 0x907070E0,0x423E3E7C,0xC4B5B571,0xAA6666CC,0xD8484890,0x05030306,0x01F6F6F7,0x120E0E1C, 0xA36161C2,0x5F35356A,0xF95757AE,0xD0B9B969,0x91868617,0x58C1C199,0x271D1D3A,0xB99E9E27, 0x38E1E1D9,0x13F8F8EB,0xB398982B,0x33111122,0xBB6969D2,0x70D9D9A9,0x898E8E07,0xA7949433, 0xB69B9B2D,0x221E1E3C,0x92878715,0x20E9E9C9,0x49CECE87,0xFF5555AA,0x78282850,0x7ADFDFA5, 0x8F8C8C03,0xF8A1A159,0x80898909,0x170D0D1A,0xDABFBF65,0x31E6E6D7,0xC6424284,0xB86868D0, 0xC3414182,0xB0999929,0x772D2D5A,0x110F0F1E,0xCBB0B07B,0xFC5454A8,0xD6BBBB6D,0x3A16162C }, { 0x6363C6A5,0x7C7CF884,0x7777EE99,0x7B7BF68D,0xF2F2FF0D,0x6B6BD6BD,0x6F6FDEB1,0xC5C59154, 0x30306050,0x01010203,0x6767CEA9,0x2B2B567D,0xFEFEE719,0xD7D7B562,0xABAB4DE6,0x7676EC9A, 0xCACA8F45,0x82821F9D,0xC9C98940,0x7D7DFA87,0xFAFAEF15,0x5959B2EB,0x47478EC9,0xF0F0FB0B, 0xADAD41EC,0xD4D4B367,0xA2A25FFD,0xAFAF45EA,0x9C9C23BF,0xA4A453F7,0x7272E496,0xC0C09B5B, 0xB7B775C2,0xFDFDE11C,0x93933DAE,0x26264C6A,0x36366C5A,0x3F3F7E41,0xF7F7F502,0xCCCC834F, 0x3434685C,0xA5A551F4,0xE5E5D134,0xF1F1F908,0x7171E293,0xD8D8AB73,0x31316253,0x15152A3F, 0x0404080C,0xC7C79552,0x23234665,0xC3C39D5E,0x18183028,0x969637A1,0x05050A0F,0x9A9A2FB5, 0x07070E09,0x12122436,0x80801B9B,0xE2E2DF3D,0xEBEBCD26,0x27274E69,0xB2B27FCD,0x7575EA9F, 0x0909121B,0x83831D9E,0x2C2C5874,0x1A1A342E,0x1B1B362D,0x6E6EDCB2,0x5A5AB4EE,0xA0A05BFB, 0x5252A4F6,0x3B3B764D,0xD6D6B761,0xB3B37DCE,0x2929527B,0xE3E3DD3E,0x2F2F5E71,0x84841397, 0x5353A6F5,0xD1D1B968,0000000000,0xEDEDC12C,0x20204060,0xFCFCE31F,0xB1B179C8,0x5B5BB6ED, 0x6A6AD4BE,0xCBCB8D46,0xBEBE67D9,0x3939724B,0x4A4A94DE,0x4C4C98D4,0x5858B0E8,0xCFCF854A, 0xD0D0BB6B,0xEFEFC52A,0xAAAA4FE5,0xFBFBED16,0x434386C5,0x4D4D9AD7,0x33336655,0x85851194, 0x45458ACF,0xF9F9E910,0x02020406,0x7F7FFE81,0x5050A0F0,0x3C3C7844,0x9F9F25BA,0xA8A84BE3, 0x5151A2F3,0xA3A35DFE,0x404080C0,0x8F8F058A,0x92923FAD,0x9D9D21BC,0x38387048,0xF5F5F104, 0xBCBC63DF,0xB6B677C1,0xDADAAF75,0x21214263,0x10102030,0xFFFFE51A,0xF3F3FD0E,0xD2D2BF6D, 0xCDCD814C,0x0C0C1814,0x13132635,0xECECC32F,0x5F5FBEE1,0x979735A2,0x444488CC,0x17172E39, 0xC4C49357,0xA7A755F2,0x7E7EFC82,0x3D3D7A47,0x6464C8AC,0x5D5DBAE7,0x1919322B,0x7373E695, 0x6060C0A0,0x81811998,0x4F4F9ED1,0xDCDCA37F,0x22224466,0x2A2A547E,0x90903BAB,0x88880B83, 0x46468CCA,0xEEEEC729,0xB8B86BD3,0x1414283C,0xDEDEA779,0x5E5EBCE2,0x0B0B161D,0xDBDBAD76, 0xE0E0DB3B,0x32326456,0x3A3A744E,0x0A0A141E,0x494992DB,0x06060C0A,0x2424486C,0x5C5CB8E4, 0xC2C29F5D,0xD3D3BD6E,0xACAC43EF,0x6262C4A6,0x919139A8,0x959531A4,0xE4E4D337,0x7979F28B, 0xE7E7D532,0xC8C88B43,0x37376E59,0x6D6DDAB7,0x8D8D018C,0xD5D5B164,0x4E4E9CD2,0xA9A949E0, 0x6C6CD8B4,0x5656ACFA,0xF4F4F307,0xEAEACF25,0x6565CAAF,0x7A7AF48E,0xAEAE47E9,0x08081018, 0xBABA6FD5,0x7878F088,0x25254A6F,0x2E2E5C72,0x1C1C3824,0xA6A657F1,0xB4B473C7,0xC6C69751, 0xE8E8CB23,0xDDDDA17C,0x7474E89C,0x1F1F3E21,0x4B4B96DD,0xBDBD61DC,0x8B8B0D86,0x8A8A0F85, 0x7070E090,0x3E3E7C42,0xB5B571C4,0x6666CCAA,0x484890D8,0x03030605,0xF6F6F701,0x0E0E1C12, 0x6161C2A3,0x35356A5F,0x5757AEF9,0xB9B969D0,0x86861791,0xC1C19958,0x1D1D3A27,0x9E9E27B9, 0xE1E1D938,0xF8F8EB13,0x98982BB3,0x11112233,0x6969D2BB,0xD9D9A970,0x8E8E0789,0x949433A7, 0x9B9B2DB6,0x1E1E3C22,0x87871592,0xE9E9C920,0xCECE8749,0x5555AAFF,0x28285078,0xDFDFA57A, 0x8C8C038F,0xA1A159F8,0x89890980,0x0D0D1A17,0xBFBF65DA,0xE6E6D731,0x424284C6,0x6868D0B8, 0x414182C3,0x999929B0,0x2D2D5A77,0x0F0F1E11,0xB0B07BCB,0x5454A8FC,0xBBBB6DD6,0x16162C3A }, { 0x63C6A563,0x7CF8847C,0x77EE9977,0x7BF68D7B,0xF2FF0DF2,0x6BD6BD6B,0x6FDEB16F,0xC59154C5, 0x30605030,0x01020301,0x67CEA967,0x2B567D2B,0xFEE719FE,0xD7B562D7,0xAB4DE6AB,0x76EC9A76, 0xCA8F45CA,0x821F9D82,0xC98940C9,0x7DFA877D,0xFAEF15FA,0x59B2EB59,0x478EC947,0xF0FB0BF0, 0xAD41ECAD,0xD4B367D4,0xA25FFDA2,0xAF45EAAF,0x9C23BF9C,0xA453F7A4,0x72E49672,0xC09B5BC0, 0xB775C2B7,0xFDE11CFD,0x933DAE93,0x264C6A26,0x366C5A36,0x3F7E413F,0xF7F502F7,0xCC834FCC, 0x34685C34,0xA551F4A5,0xE5D134E5,0xF1F908F1,0x71E29371,0xD8AB73D8,0x31625331,0x152A3F15, 0x04080C04,0xC79552C7,0x23466523,0xC39D5EC3,0x18302818,0x9637A196,0x050A0F05,0x9A2FB59A, 0x070E0907,0x12243612,0x801B9B80,0xE2DF3DE2,0xEBCD26EB,0x274E6927,0xB27FCDB2,0x75EA9F75, 0x09121B09,0x831D9E83,0x2C58742C,0x1A342E1A,0x1B362D1B,0x6EDCB26E,0x5AB4EE5A,0xA05BFBA0, 0x52A4F652,0x3B764D3B,0xD6B761D6,0xB37DCEB3,0x29527B29,0xE3DD3EE3,0x2F5E712F,0x84139784, 0x53A6F553,0xD1B968D1,0000000000,0xEDC12CED,0x20406020,0xFCE31FFC,0xB179C8B1,0x5BB6ED5B, 0x6AD4BE6A,0xCB8D46CB,0xBE67D9BE,0x39724B39,0x4A94DE4A,0x4C98D44C,0x58B0E858,0xCF854ACF, 0xD0BB6BD0,0xEFC52AEF,0xAA4FE5AA,0xFBED16FB,0x4386C543,0x4D9AD74D,0x33665533,0x85119485, 0x458ACF45,0xF9E910F9,0x02040602,0x7FFE817F,0x50A0F050,0x3C78443C,0x9F25BA9F,0xA84BE3A8, 0x51A2F351,0xA35DFEA3,0x4080C040,0x8F058A8F,0x923FAD92,0x9D21BC9D,0x38704838,0xF5F104F5, 0xBC63DFBC,0xB677C1B6,0xDAAF75DA,0x21426321,0x10203010,0xFFE51AFF,0xF3FD0EF3,0xD2BF6DD2, 0xCD814CCD,0x0C18140C,0x13263513,0xECC32FEC,0x5FBEE15F,0x9735A297,0x4488CC44,0x172E3917, 0xC49357C4,0xA755F2A7,0x7EFC827E,0x3D7A473D,0x64C8AC64,0x5DBAE75D,0x19322B19,0x73E69573, 0x60C0A060,0x81199881,0x4F9ED14F,0xDCA37FDC,0x22446622,0x2A547E2A,0x903BAB90,0x880B8388, 0x468CCA46,0xEEC729EE,0xB86BD3B8,0x14283C14,0xDEA779DE,0x5EBCE25E,0x0B161D0B,0xDBAD76DB, 0xE0DB3BE0,0x32645632,0x3A744E3A,0x0A141E0A,0x4992DB49,0x060C0A06,0x24486C24,0x5CB8E45C, 0xC29F5DC2,0xD3BD6ED3,0xAC43EFAC,0x62C4A662,0x9139A891,0x9531A495,0xE4D337E4,0x79F28B79, 0xE7D532E7,0xC88B43C8,0x376E5937,0x6DDAB76D,0x8D018C8D,0xD5B164D5,0x4E9CD24E,0xA949E0A9, 0x6CD8B46C,0x56ACFA56,0xF4F307F4,0xEACF25EA,0x65CAAF65,0x7AF48E7A,0xAE47E9AE,0x08101808, 0xBA6FD5BA,0x78F08878,0x254A6F25,0x2E5C722E,0x1C38241C,0xA657F1A6,0xB473C7B4,0xC69751C6, 0xE8CB23E8,0xDDA17CDD,0x74E89C74,0x1F3E211F,0x4B96DD4B,0xBD61DCBD,0x8B0D868B,0x8A0F858A, 0x70E09070,0x3E7C423E,0xB571C4B5,0x66CCAA66,0x4890D848,0x03060503,0xF6F701F6,0x0E1C120E, 0x61C2A361,0x356A5F35,0x57AEF957,0xB969D0B9,0x86179186,0xC19958C1,0x1D3A271D,0x9E27B99E, 0xE1D938E1,0xF8EB13F8,0x982BB398,0x11223311,0x69D2BB69,0xD9A970D9,0x8E07898E,0x9433A794, 0x9B2DB69B,0x1E3C221E,0x87159287,0xE9C920E9,0xCE8749CE,0x55AAFF55,0x28507828,0xDFA57ADF, 0x8C038F8C,0xA159F8A1,0x89098089,0x0D1A170D,0xBF65DABF,0xE6D731E6,0x4284C642,0x68D0B868, 0x4182C341,0x9929B099,0x2D5A772D,0x0F1E110F,0xB07BCBB0,0x54A8FC54,0xBB6DD6BB,0x162C3A16 }, { 0xC6A56363,0xF8847C7C,0xEE997777,0xF68D7B7B,0xFF0DF2F2,0xD6BD6B6B,0xDEB16F6F,0x9154C5C5, 0x60503030,0x02030101,0xCEA96767,0x567D2B2B,0xE719FEFE,0xB562D7D7,0x4DE6ABAB,0xEC9A7676, 0x8F45CACA,0x1F9D8282,0x8940C9C9,0xFA877D7D,0xEF15FAFA,0xB2EB5959,0x8EC94747,0xFB0BF0F0, 0x41ECADAD,0xB367D4D4,0x5FFDA2A2,0x45EAAFAF,0x23BF9C9C,0x53F7A4A4,0xE4967272,0x9B5BC0C0, 0x75C2B7B7,0xE11CFDFD,0x3DAE9393,0x4C6A2626,0x6C5A3636,0x7E413F3F,0xF502F7F7,0x834FCCCC, 0x685C3434,0x51F4A5A5,0xD134E5E5,0xF908F1F1,0xE2937171,0xAB73D8D8,0x62533131,0x2A3F1515, 0x080C0404,0x9552C7C7,0x46652323,0x9D5EC3C3,0x30281818,0x37A19696,0x0A0F0505,0x2FB59A9A, 0x0E090707,0x24361212,0x1B9B8080,0xDF3DE2E2,0xCD26EBEB,0x4E692727,0x7FCDB2B2,0xEA9F7575, 0x121B0909,0x1D9E8383,0x58742C2C,0x342E1A1A,0x362D1B1B,0xDCB26E6E,0xB4EE5A5A,0x5BFBA0A0, 0xA4F65252,0x764D3B3B,0xB761D6D6,0x7DCEB3B3,0x527B2929,0xDD3EE3E3,0x5E712F2F,0x13978484, 0xA6F55353,0xB968D1D1,0000000000,0xC12CEDED,0x40602020,0xE31FFCFC,0x79C8B1B1,0xB6ED5B5B, 0xD4BE6A6A,0x8D46CBCB,0x67D9BEBE,0x724B3939,0x94DE4A4A,0x98D44C4C,0xB0E85858,0x854ACFCF, 0xBB6BD0D0,0xC52AEFEF,0x4FE5AAAA,0xED16FBFB,0x86C54343,0x9AD74D4D,0x66553333,0x11948585, 0x8ACF4545,0xE910F9F9,0x04060202,0xFE817F7F,0xA0F05050,0x78443C3C,0x25BA9F9F,0x4BE3A8A8, 0xA2F35151,0x5DFEA3A3,0x80C04040,0x058A8F8F,0x3FAD9292,0x21BC9D9D,0x70483838,0xF104F5F5, 0x63DFBCBC,0x77C1B6B6,0xAF75DADA,0x42632121,0x20301010,0xE51AFFFF,0xFD0EF3F3,0xBF6DD2D2, 0x814CCDCD,0x18140C0C,0x26351313,0xC32FECEC,0xBEE15F5F,0x35A29797,0x88CC4444,0x2E391717, 0x9357C4C4,0x55F2A7A7,0xFC827E7E,0x7A473D3D,0xC8AC6464,0xBAE75D5D,0x322B1919,0xE6957373, 0xC0A06060,0x19988181,0x9ED14F4F,0xA37FDCDC,0x44662222,0x547E2A2A,0x3BAB9090,0x0B838888, 0x8CCA4646,0xC729EEEE,0x6BD3B8B8,0x283C1414,0xA779DEDE,0xBCE25E5E,0x161D0B0B,0xAD76DBDB, 0xDB3BE0E0,0x64563232,0x744E3A3A,0x141E0A0A,0x92DB4949,0x0C0A0606,0x486C2424,0xB8E45C5C, 0x9F5DC2C2,0xBD6ED3D3,0x43EFACAC,0xC4A66262,0x39A89191,0x31A49595,0xD337E4E4,0xF28B7979, 0xD532E7E7,0x8B43C8C8,0x6E593737,0xDAB76D6D,0x018C8D8D,0xB164D5D5,0x9CD24E4E,0x49E0A9A9, 0xD8B46C6C,0xACFA5656,0xF307F4F4,0xCF25EAEA,0xCAAF6565,0xF48E7A7A,0x47E9AEAE,0x10180808, 0x6FD5BABA,0xF0887878,0x4A6F2525,0x5C722E2E,0x38241C1C,0x57F1A6A6,0x73C7B4B4,0x9751C6C6, 0xCB23E8E8,0xA17CDDDD,0xE89C7474,0x3E211F1F,0x96DD4B4B,0x61DCBDBD,0x0D868B8B,0x0F858A8A, 0xE0907070,0x7C423E3E,0x71C4B5B5,0xCCAA6666,0x90D84848,0x06050303,0xF701F6F6,0x1C120E0E, 0xC2A36161,0x6A5F3535,0xAEF95757,0x69D0B9B9,0x17918686,0x9958C1C1,0x3A271D1D,0x27B99E9E, 0xD938E1E1,0xEB13F8F8,0x2BB39898,0x22331111,0xD2BB6969,0xA970D9D9,0x07898E8E,0x33A79494, 0x2DB69B9B,0x3C221E1E,0x15928787,0xC920E9E9,0x8749CECE,0xAAFF5555,0x50782828,0xA57ADFDF, 0x038F8C8C,0x59F8A1A1,0x09808989,0x1A170D0D,0x65DABFBF,0xD731E6E6,0x84C64242,0xD0B86868, 0x82C34141,0x29B09999,0x5A772D2D,0x1E110F0F,0x7BCBB0B0,0xA8FC5454,0x6DD6BBBB,0x2C3A1616 } }; static uint32_t fl_tab[4][256] = { { 0x00000063,0x0000007C,0x00000077,0x0000007B,0x000000F2,0x0000006B,0x0000006F,0x000000C5, 0x00000030,0x00000001,0x00000067,0x0000002B,0x000000FE,0x000000D7,0x000000AB,0x00000076, 0x000000CA,0x00000082,0x000000C9,0x0000007D,0x000000FA,0x00000059,0x00000047,0x000000F0, 0x000000AD,0x000000D4,0x000000A2,0x000000AF,0x0000009C,0x000000A4,0x00000072,0x000000C0, 0x000000B7,0x000000FD,0x00000093,0x00000026,0x00000036,0x0000003F,0x000000F7,0x000000CC, 0x00000034,0x000000A5,0x000000E5,0x000000F1,0x00000071,0x000000D8,0x00000031,0x00000015, 0x00000004,0x000000C7,0x00000023,0x000000C3,0x00000018,0x00000096,0x00000005,0x0000009A, 0x00000007,0x00000012,0x00000080,0x000000E2,0x000000EB,0x00000027,0x000000B2,0x00000075, 0x00000009,0x00000083,0x0000002C,0x0000001A,0x0000001B,0x0000006E,0x0000005A,0x000000A0, 0x00000052,0x0000003B,0x000000D6,0x000000B3,0x00000029,0x000000E3,0x0000002F,0x00000084, 0x00000053,0x000000D1,0x00000000,0x000000ED,0x00000020,0x000000FC,0x000000B1,0x0000005B, 0x0000006A,0x000000CB,0x000000BE,0x00000039,0x0000004A,0x0000004C,0x00000058,0x000000CF, 0x000000D0,0x000000EF,0x000000AA,0x000000FB,0x00000043,0x0000004D,0x00000033,0x00000085, 0x00000045,0x000000F9,0x00000002,0x0000007F,0x00000050,0x0000003C,0x0000009F,0x000000A8, 0x00000051,0x000000A3,0x00000040,0x0000008F,0x00000092,0x0000009D,0x00000038,0x000000F5, 0x000000BC,0x000000B6,0x000000DA,0x00000021,0x00000010,0x000000FF,0x000000F3,0x000000D2, 0x000000CD,0x0000000C,0x00000013,0x000000EC,0x0000005F,0x00000097,0x00000044,0x00000017, 0x000000C4,0x000000A7,0x0000007E,0x0000003D,0x00000064,0x0000005D,0x00000019,0x00000073, 0x00000060,0x00000081,0x0000004F,0x000000DC,0x00000022,0x0000002A,0x00000090,0x00000088, 0x00000046,0x000000EE,0x000000B8,0x00000014,0x000000DE,0x0000005E,0x0000000B,0x000000DB, 0x000000E0,0x00000032,0x0000003A,0x0000000A,0x00000049,0x00000006,0x00000024,0x0000005C, 0x000000C2,0x000000D3,0x000000AC,0x00000062,0x00000091,0x00000095,0x000000E4,0x00000079, 0x000000E7,0x000000C8,0x00000037,0x0000006D,0x0000008D,0x000000D5,0x0000004E,0x000000A9, 0x0000006C,0x00000056,0x000000F4,0x000000EA,0x00000065,0x0000007A,0x000000AE,0x00000008, 0x000000BA,0x00000078,0x00000025,0x0000002E,0x0000001C,0x000000A6,0x000000B4,0x000000C6, 0x000000E8,0x000000DD,0x00000074,0x0000001F,0x0000004B,0x000000BD,0x0000008B,0x0000008A, 0x00000070,0x0000003E,0x000000B5,0x00000066,0x00000048,0x00000003,0x000000F6,0x0000000E, 0x00000061,0x00000035,0x00000057,0x000000B9,0x00000086,0x000000C1,0x0000001D,0x0000009E, 0x000000E1,0x000000F8,0x00000098,0x00000011,0x00000069,0x000000D9,0x0000008E,0x00000094, 0x0000009B,0x0000001E,0x00000087,0x000000E9,0x000000CE,0x00000055,0x00000028,0x000000DF, 0x0000008C,0x000000A1,0x00000089,0x0000000D,0x000000BF,0x000000E6,0x00000042,0x00000068, 0x00000041,0x00000099,0x0000002D,0x0000000F,0x000000B0,0x00000054,0x000000BB,0x00000016 }, { 0x00006300,0x00007C00,0x00007700,0x00007B00,0x0000F200,0x00006B00,0x00006F00,0x0000C500, 0x00003000,0x00000100,0x00006700,0x00002B00,0x0000FE00,0x0000D700,0x0000AB00,0x00007600, 0x0000CA00,0x00008200,0x0000C900,0x00007D00,0x0000FA00,0x00005900,0x00004700,0x0000F000, 0x0000AD00,0x0000D400,0x0000A200,0x0000AF00,0x00009C00,0x0000A400,0x00007200,0x0000C000, 0x0000B700,0x0000FD00,0x00009300,0x00002600,0x00003600,0x00003F00,0x0000F700,0x0000CC00, 0x00003400,0x0000A500,0x0000E500,0x0000F100,0x00007100,0x0000D800,0x00003100,0x00001500, 0x00000400,0x0000C700,0x00002300,0x0000C300,0x00001800,0x00009600,0x00000500,0x00009A00, 0x00000700,0x00001200,0x00008000,0x0000E200,0x0000EB00,0x00002700,0x0000B200,0x00007500, 0x00000900,0x00008300,0x00002C00,0x00001A00,0x00001B00,0x00006E00,0x00005A00,0x0000A000, 0x00005200,0x00003B00,0x0000D600,0x0000B300,0x00002900,0x0000E300,0x00002F00,0x00008400, 0x00005300,0x0000D100,0000000000,0x0000ED00,0x00002000,0x0000FC00,0x0000B100,0x00005B00, 0x00006A00,0x0000CB00,0x0000BE00,0x00003900,0x00004A00,0x00004C00,0x00005800,0x0000CF00, 0x0000D000,0x0000EF00,0x0000AA00,0x0000FB00,0x00004300,0x00004D00,0x00003300,0x00008500, 0x00004500,0x0000F900,0x00000200,0x00007F00,0x00005000,0x00003C00,0x00009F00,0x0000A800, 0x00005100,0x0000A300,0x00004000,0x00008F00,0x00009200,0x00009D00,0x00003800,0x0000F500, 0x0000BC00,0x0000B600,0x0000DA00,0x00002100,0x00001000,0x0000FF00,0x0000F300,0x0000D200, 0x0000CD00,0x00000C00,0x00001300,0x0000EC00,0x00005F00,0x00009700,0x00004400,0x00001700, 0x0000C400,0x0000A700,0x00007E00,0x00003D00,0x00006400,0x00005D00,0x00001900,0x00007300, 0x00006000,0x00008100,0x00004F00,0x0000DC00,0x00002200,0x00002A00,0x00009000,0x00008800, 0x00004600,0x0000EE00,0x0000B800,0x00001400,0x0000DE00,0x00005E00,0x00000B00,0x0000DB00, 0x0000E000,0x00003200,0x00003A00,0x00000A00,0x00004900,0x00000600,0x00002400,0x00005C00, 0x0000C200,0x0000D300,0x0000AC00,0x00006200,0x00009100,0x00009500,0x0000E400,0x00007900, 0x0000E700,0x0000C800,0x00003700,0x00006D00,0x00008D00,0x0000D500,0x00004E00,0x0000A900, 0x00006C00,0x00005600,0x0000F400,0x0000EA00,0x00006500,0x00007A00,0x0000AE00,0x00000800, 0x0000BA00,0x00007800,0x00002500,0x00002E00,0x00001C00,0x0000A600,0x0000B400,0x0000C600, 0x0000E800,0x0000DD00,0x00007400,0x00001F00,0x00004B00,0x0000BD00,0x00008B00,0x00008A00, 0x00007000,0x00003E00,0x0000B500,0x00006600,0x00004800,0x00000300,0x0000F600,0x00000E00, 0x00006100,0x00003500,0x00005700,0x0000B900,0x00008600,0x0000C100,0x00001D00,0x00009E00, 0x0000E100,0x0000F800,0x00009800,0x00001100,0x00006900,0x0000D900,0x00008E00,0x00009400, 0x00009B00,0x00001E00,0x00008700,0x0000E900,0x0000CE00,0x00005500,0x00002800,0x0000DF00, 0x00008C00,0x0000A100,0x00008900,0x00000D00,0x0000BF00,0x0000E600,0x00004200,0x00006800, 0x00004100,0x00009900,0x00002D00,0x00000F00,0x0000B000,0x00005400,0x0000BB00,0x00001600 }, { 0x00630000,0x007C0000,0x00770000,0x007B0000,0x00F20000,0x006B0000,0x006F0000,0x00C50000, 0x00300000,0x00010000,0x00670000,0x002B0000,0x00FE0000,0x00D70000,0x00AB0000,0x00760000, 0x00CA0000,0x00820000,0x00C90000,0x007D0000,0x00FA0000,0x00590000,0x00470000,0x00F00000, 0x00AD0000,0x00D40000,0x00A20000,0x00AF0000,0x009C0000,0x00A40000,0x00720000,0x00C00000, 0x00B70000,0x00FD0000,0x00930000,0x00260000,0x00360000,0x003F0000,0x00F70000,0x00CC0000, 0x00340000,0x00A50000,0x00E50000,0x00F10000,0x00710000,0x00D80000,0x00310000,0x00150000, 0x00040000,0x00C70000,0x00230000,0x00C30000,0x00180000,0x00960000,0x00050000,0x009A0000, 0x00070000,0x00120000,0x00800000,0x00E20000,0x00EB0000,0x00270000,0x00B20000,0x00750000, 0x00090000,0x00830000,0x002C0000,0x001A0000,0x001B0000,0x006E0000,0x005A0000,0x00A00000, 0x00520000,0x003B0000,0x00D60000,0x00B30000,0x00290000,0x00E30000,0x002F0000,0x00840000, 0x00530000,0x00D10000,0000000000,0x00ED0000,0x00200000,0x00FC0000,0x00B10000,0x005B0000, 0x006A0000,0x00CB0000,0x00BE0000,0x00390000,0x004A0000,0x004C0000,0x00580000,0x00CF0000, 0x00D00000,0x00EF0000,0x00AA0000,0x00FB0000,0x00430000,0x004D0000,0x00330000,0x00850000, 0x00450000,0x00F90000,0x00020000,0x007F0000,0x00500000,0x003C0000,0x009F0000,0x00A80000, 0x00510000,0x00A30000,0x00400000,0x008F0000,0x00920000,0x009D0000,0x00380000,0x00F50000, 0x00BC0000,0x00B60000,0x00DA0000,0x00210000,0x00100000,0x00FF0000,0x00F30000,0x00D20000, 0x00CD0000,0x000C0000,0x00130000,0x00EC0000,0x005F0000,0x00970000,0x00440000,0x00170000, 0x00C40000,0x00A70000,0x007E0000,0x003D0000,0x00640000,0x005D0000,0x00190000,0x00730000, 0x00600000,0x00810000,0x004F0000,0x00DC0000,0x00220000,0x002A0000,0x00900000,0x00880000, 0x00460000,0x00EE0000,0x00B80000,0x00140000,0x00DE0000,0x005E0000,0x000B0000,0x00DB0000, 0x00E00000,0x00320000,0x003A0000,0x000A0000,0x00490000,0x00060000,0x00240000,0x005C0000, 0x00C20000,0x00D30000,0x00AC0000,0x00620000,0x00910000,0x00950000,0x00E40000,0x00790000, 0x00E70000,0x00C80000,0x00370000,0x006D0000,0x008D0000,0x00D50000,0x004E0000,0x00A90000, 0x006C0000,0x00560000,0x00F40000,0x00EA0000,0x00650000,0x007A0000,0x00AE0000,0x00080000, 0x00BA0000,0x00780000,0x00250000,0x002E0000,0x001C0000,0x00A60000,0x00B40000,0x00C60000, 0x00E80000,0x00DD0000,0x00740000,0x001F0000,0x004B0000,0x00BD0000,0x008B0000,0x008A0000, 0x00700000,0x003E0000,0x00B50000,0x00660000,0x00480000,0x00030000,0x00F60000,0x000E0000, 0x00610000,0x00350000,0x00570000,0x00B90000,0x00860000,0x00C10000,0x001D0000,0x009E0000, 0x00E10000,0x00F80000,0x00980000,0x00110000,0x00690000,0x00D90000,0x008E0000,0x00940000, 0x009B0000,0x001E0000,0x00870000,0x00E90000,0x00CE0000,0x00550000,0x00280000,0x00DF0000, 0x008C0000,0x00A10000,0x00890000,0x000D0000,0x00BF0000,0x00E60000,0x00420000,0x00680000, 0x00410000,0x00990000,0x002D0000,0x000F0000,0x00B00000,0x00540000,0x00BB0000,0x00160000 }, { 0x63000000,0x7C000000,0x77000000,0x7B000000,0xF2000000,0x6B000000,0x6F000000,0xC5000000, 0x30000000,0x01000000,0x67000000,0x2B000000,0xFE000000,0xD7000000,0xAB000000,0x76000000, 0xCA000000,0x82000000,0xC9000000,0x7D000000,0xFA000000,0x59000000,0x47000000,0xF0000000, 0xAD000000,0xD4000000,0xA2000000,0xAF000000,0x9C000000,0xA4000000,0x72000000,0xC0000000, 0xB7000000,0xFD000000,0x93000000,0x26000000,0x36000000,0x3F000000,0xF7000000,0xCC000000, 0x34000000,0xA5000000,0xE5000000,0xF1000000,0x71000000,0xD8000000,0x31000000,0x15000000, 0x04000000,0xC7000000,0x23000000,0xC3000000,0x18000000,0x96000000,0x05000000,0x9A000000, 0x07000000,0x12000000,0x80000000,0xE2000000,0xEB000000,0x27000000,0xB2000000,0x75000000, 0x09000000,0x83000000,0x2C000000,0x1A000000,0x1B000000,0x6E000000,0x5A000000,0xA0000000, 0x52000000,0x3B000000,0xD6000000,0xB3000000,0x29000000,0xE3000000,0x2F000000,0x84000000, 0x53000000,0xD1000000,0000000000,0xED000000,0x20000000,0xFC000000,0xB1000000,0x5B000000, 0x6A000000,0xCB000000,0xBE000000,0x39000000,0x4A000000,0x4C000000,0x58000000,0xCF000000, 0xD0000000,0xEF000000,0xAA000000,0xFB000000,0x43000000,0x4D000000,0x33000000,0x85000000, 0x45000000,0xF9000000,0x02000000,0x7F000000,0x50000000,0x3C000000,0x9F000000,0xA8000000, 0x51000000,0xA3000000,0x40000000,0x8F000000,0x92000000,0x9D000000,0x38000000,0xF5000000, 0xBC000000,0xB6000000,0xDA000000,0x21000000,0x10000000,0xFF000000,0xF3000000,0xD2000000, 0xCD000000,0x0C000000,0x13000000,0xEC000000,0x5F000000,0x97000000,0x44000000,0x17000000, 0xC4000000,0xA7000000,0x7E000000,0x3D000000,0x64000000,0x5D000000,0x19000000,0x73000000, 0x60000000,0x81000000,0x4F000000,0xDC000000,0x22000000,0x2A000000,0x90000000,0x88000000, 0x46000000,0xEE000000,0xB8000000,0x14000000,0xDE000000,0x5E000000,0x0B000000,0xDB000000, 0xE0000000,0x32000000,0x3A000000,0x0A000000,0x49000000,0x06000000,0x24000000,0x5C000000, 0xC2000000,0xD3000000,0xAC000000,0x62000000,0x91000000,0x95000000,0xE4000000,0x79000000, 0xE7000000,0xC8000000,0x37000000,0x6D000000,0x8D000000,0xD5000000,0x4E000000,0xA9000000, 0x6C000000,0x56000000,0xF4000000,0xEA000000,0x65000000,0x7A000000,0xAE000000,0x08000000, 0xBA000000,0x78000000,0x25000000,0x2E000000,0x1C000000,0xA6000000,0xB4000000,0xC6000000, 0xE8000000,0xDD000000,0x74000000,0x1F000000,0x4B000000,0xBD000000,0x8B000000,0x8A000000, 0x70000000,0x3E000000,0xB5000000,0x66000000,0x48000000,0x03000000,0xF6000000,0x0E000000, 0x61000000,0x35000000,0x57000000,0xB9000000,0x86000000,0xC1000000,0x1D000000,0x9E000000, 0xE1000000,0xF8000000,0x98000000,0x11000000,0x69000000,0xD9000000,0x8E000000,0x94000000, 0x9B000000,0x1E000000,0x87000000,0xE9000000,0xCE000000,0x55000000,0x28000000,0xDF000000, 0x8C000000,0xA1000000,0x89000000,0x0D000000,0xBF000000,0xE6000000,0x42000000,0x68000000, 0x41000000,0x99000000,0x2D000000,0x0F000000,0xB0000000,0x54000000,0xBB000000,0x16000000 } }; /*----------------- The workspace ------------------------------*/ static uint32_t Ekey[44]; /* The expanded key */ /*------ The round Function. 4 table lookups and 4 Exors ------*/ #define f_rnd(x, n) \ ( ft_tab[0][byte0(x[n])] \ ^ ft_tab[1][byte1(x[(n + 1) & 3])] \ ^ ft_tab[2][byte2(x[(n + 2) & 3])] \ ^ ft_tab[3][byte3(x[(n + 3) & 3])] ) #define f_round(bo, bi, k) \ bo[0] = f_rnd(bi, 0) ^ k[0]; \ bo[1] = f_rnd(bi, 1) ^ k[1]; \ bo[2] = f_rnd(bi, 2) ^ k[2]; \ bo[3] = f_rnd(bi, 3) ^ k[3]; \ k += 4 /*--- The S Box lookup used in constructing the Key schedule ---*/ #define ls_box(x) \ ( fl_tab[0][byte0(x)] \ ^ fl_tab[1][byte1(x)] \ ^ fl_tab[2][byte2(x)] \ ^ fl_tab[3][byte3(x)] ) /*------------ The last round function (no MixColumn) ----------*/ #define lf_rnd(x, n) \ ( fl_tab[0][byte0(x[n])] \ ^ fl_tab[1][byte1(x[(n + 1) & 3])] \ ^ fl_tab[2][byte2(x[(n + 2) & 3])] \ ^ fl_tab[3][byte3(x[(n + 3) & 3])] ) /*----------------------------------------------------------- * RijndaelKeySchedule * Initialise the key schedule from a supplied key */ void RijndaelKeySchedule(uint8_t key[16]) { uint32_t t; uint32_t *ek=Ekey, /* pointer to the expanded key */ *rc=rnd_con; /* pointer to the round constant */ Ekey[0] = u32_in(key ); Ekey[1] = u32_in(key + 4); Ekey[2] = u32_in(key + 8); Ekey[3] = u32_in(key + 12); while(ek < Ekey + 40) { t = rot3(ek[3]); ek[4] = ek[0] ^ ls_box(t) ^ *rc++; ek[5] = ek[1] ^ ek[4]; ek[6] = ek[2] ^ ek[5]; ek[7] = ek[3] ^ ek[6]; ek += 4; } } /*----------------------------------------------------------- * RijndaelEncrypt * Encrypt an input block */ void RijndaelEncrypt(uint8_t in[16], uint8_t out[16]) { uint32_t b0[4], b1[4], *kp = Ekey; b0[0] = u32_in(in ) ^ *kp++; b0[1] = u32_in(in + 4) ^ *kp++; b0[2] = u32_in(in + 8) ^ *kp++; b0[3] = u32_in(in + 12) ^ *kp++; f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); f_round(b0, b1, kp); f_round(b1, b0, kp); u32_out(out, lf_rnd(b1, 0) ^ kp[0]); u32_out(out + 4, lf_rnd(b1, 1) ^ kp[1]); u32_out(out + 8, lf_rnd(b1, 2) ^ kp[2]); u32_out(out + 12, lf_rnd(b1, 3) ^ kp[3]); } sipp-3.6.1/src/logger.cpp0000664000175000017500000004112613730472040014632 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include #include #include #include #include #include #include #include "logger.hpp" unsigned long total_errors = 0; void log_off(struct logfile_info* lfi) { if (lfi->fptr) { fflush(lfi->fptr); fclose(lfi->fptr); lfi->fptr = NULL; lfi->overwrite = false; } } void print_count_file(FILE* f, int header) { char temp_str[256]; if (!main_scenario || (!header && !main_scenario->stats)) { return; } if (header) { fprintf(f, "CurrentTime%sElapsedTime%s", stat_delimiter, stat_delimiter); } else { struct timeval currentTime, startTime; GET_TIME(¤tTime); main_scenario->stats->getStartTime(&startTime); unsigned long globalElapsedTime = CStat::computeDiffTimeInMs(¤tTime, &startTime); fprintf(f, "%s%s", CStat::formatTime(¤tTime), stat_delimiter); fprintf(f, "%s%s", CStat::msToHHMMSSus(globalElapsedTime), stat_delimiter); } for (unsigned int index = 0; index < main_scenario->messages.size(); index++) { message* curmsg = main_scenario->messages[index]; if (curmsg->hide) { continue; } if (SendingMessage* src = curmsg->send_scheme) { if (header) { if (src->isResponse()) { sprintf(temp_str, "%u_%d_", index, src->getCode()); } else { sprintf(temp_str, "%u_%s_", index, src->getMethod()); } fprintf(f, "%sSent%s", temp_str, stat_delimiter); fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); if (curmsg->retrans_delay) { fprintf(f, "%sTimeout%s", temp_str, stat_delimiter); } if (lose_packets) { fprintf(f, "%sLost%s", temp_str, stat_delimiter); } } else { fprintf(f, "%lu%s", curmsg->nb_sent, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_sent_retrans, stat_delimiter); if (curmsg->retrans_delay) { fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); } if (lose_packets) { fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); } } } else if (curmsg->recv_response) { if (header) { sprintf(temp_str, "%u_%d_", index, curmsg->recv_response); fprintf(f, "%sRecv%s", temp_str, stat_delimiter); fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); fprintf(f, "%sTimeout%s", temp_str, stat_delimiter); fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); if (lose_packets) { fprintf(f, "%sLost%s", temp_str, stat_delimiter); } } else { fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); if (lose_packets) { fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); } } } else if (curmsg->recv_request) { if (header) { sprintf(temp_str, "%u_%s_", index, curmsg->recv_request); fprintf(f, "%sRecv%s", temp_str, stat_delimiter); fprintf(f, "%sRetrans%s", temp_str, stat_delimiter); fprintf(f, "%sTimeout%s", temp_str, stat_delimiter); fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); if (lose_packets) { fprintf(f, "%sLost%s", temp_str, stat_delimiter); } } else { fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); if (lose_packets) { fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter); } } } else if (curmsg->pause_distribution || curmsg->pause_variable) { if (header) { sprintf(temp_str, "%u_Pause_", index); fprintf(f, "%sSessions%s", temp_str, stat_delimiter); fprintf(f, "%sUnexp%s", temp_str, stat_delimiter); } else { fprintf(f, "%d%s", curmsg->sessions, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter); } } else if (curmsg->M_type == MSG_TYPE_NOP) { /* No output. */ } else if (curmsg->M_type == MSG_TYPE_RECVCMD) { if (header) { sprintf(temp_str, "%u_RecvCmd", index); fprintf(f, "%s%s", temp_str, stat_delimiter); fprintf(f, "%s_Timeout%s", temp_str, stat_delimiter); } else { fprintf(f, "%lu%s", curmsg->M_nbCmdRecv, stat_delimiter); fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter); } } else if (curmsg->M_type == MSG_TYPE_SENDCMD) { if (header) { sprintf(temp_str, "%u_SendCmd", index); fprintf(f, "%s%s", temp_str, stat_delimiter); } else { fprintf(f, "%lu%s", curmsg->M_nbCmdSent, stat_delimiter); } } else { ERROR("Unknown count file message type:"); } } fprintf(f, "\n"); fflush(f); } void print_error_codes_file(FILE* f) { if (!main_scenario || !main_scenario->stats) { return; } // Print time and elapsed time to file struct timeval currentTime, startTime; GET_TIME(¤tTime); main_scenario->stats->getStartTime(&startTime); unsigned long globalElapsedTime = CStat::computeDiffTimeInMs(¤tTime, &startTime); fprintf(f, "%s%s", CStat::formatTime(¤tTime), stat_delimiter); fprintf(f, "%s%s", CStat::msToHHMMSSus(globalElapsedTime), stat_delimiter); // Print comma-separated list of all error codes seen since the last time // this function was called for (; main_scenario->stats->error_codes.size() != 0;) { fprintf( f, "%d,", main_scenario->stats ->error_codes[main_scenario->stats->error_codes.size() - 1]); main_scenario->stats->error_codes.pop_back(); } fprintf(f, "\n"); fflush(f); } /* Function to dump all available screens in a file */ void print_screens(void) { int oldScreen = currentScreenToDisplay; int oldRepartition = currentRepartitionToDisplay; currentScreenToDisplay = DISPLAY_SCENARIO_SCREEN; sp->print_to_file(screen_lfi.fptr); currentScreenToDisplay = DISPLAY_STAT_SCREEN; sp->print_to_file(screen_lfi.fptr); currentScreenToDisplay = DISPLAY_REPARTITION_SCREEN; sp->print_to_file(screen_lfi.fptr); currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN; for (currentRepartitionToDisplay = 2; currentRepartitionToDisplay <= display_scenario->stats->nRtds(); currentRepartitionToDisplay++) { sp->print_to_file(screen_lfi.fptr); } currentScreenToDisplay = oldScreen; currentRepartitionToDisplay = oldRepartition; } static void rotatef(struct logfile_info* lfi) { char L_rotate_file_name[MAX_PATH]; if (!lfi->fixedname) { sprintf(lfi->file_name, "%s_%ld_%s.log", scenario_file, (long)getpid(), lfi->name); } if (ringbuffer_files > 0) { if (!lfi->ftimes) { lfi->ftimes = (struct logfile_id*)calloc(ringbuffer_files, sizeof(struct logfile_id)); } /* We need to rotate away an existing file. */ if (lfi->nfiles == ringbuffer_files) { if ((lfi->ftimes)[0].n) { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.%d.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[0].start, (lfi->ftimes)[0].n); } else { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[0].start); } unlink(L_rotate_file_name); lfi->nfiles--; memmove(lfi->ftimes, &((lfi->ftimes)[1]), sizeof(struct logfile_id) * (lfi->nfiles)); } if (lfi->starttime) { (lfi->ftimes)[lfi->nfiles].start = lfi->starttime; (lfi->ftimes)[lfi->nfiles].n = 0; /* If we have the same time, then we need to append an identifier. */ if (lfi->nfiles && ((lfi->ftimes)[lfi->nfiles].start == (lfi->ftimes)[lfi->nfiles - 1].start)) { (lfi->ftimes)[lfi->nfiles].n = (lfi->ftimes)[lfi->nfiles - 1].n + 1; } if ((lfi->ftimes)[lfi->nfiles].n) { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.%d.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[lfi->nfiles].start, (lfi->ftimes)[lfi->nfiles].n); } else { sprintf(L_rotate_file_name, "%s_%ld_%s_%lu.log", scenario_file, (long)getpid(), lfi->name, (unsigned long)(lfi->ftimes)[lfi->nfiles].start); } lfi->nfiles++; fflush(lfi->fptr); fclose(lfi->fptr); lfi->fptr = NULL; rename(lfi->file_name, L_rotate_file_name); } } time(&lfi->starttime); if (lfi->overwrite) { lfi->fptr = fopen(lfi->file_name, "w"); } else { lfi->fptr = fopen(lfi->file_name, "a"); lfi->overwrite = true; } if (lfi->check && !lfi->fptr) { /* We can not use the error functions from this function, as we may be * rotating the error log itself! */ ERROR("Unable to create '%s'", lfi->file_name); } } void rotate_screenf() { rotatef(&screen_lfi); } void rotate_calldebugf() { rotatef(&calldebug_lfi); } void rotate_messagef() { rotatef(&message_lfi); } void rotate_shortmessagef() { rotatef(&shortmessage_lfi); } void rotate_logfile() { rotatef(&log_lfi); } void rotate_errorf() { rotatef(&error_lfi); strcpy(screen_logfile, error_lfi.file_name); } static int _trace(struct logfile_info* lfi, const char* fmt, va_list ap) { int ret = 0; if (lfi->fptr) { ret = vfprintf(lfi->fptr, fmt, ap); fflush(lfi->fptr); lfi->count += ret; if (max_log_size && lfi->count > max_log_size) { fclose(lfi->fptr); lfi->fptr = NULL; } if (ringbuffer_size && lfi->count > ringbuffer_size) { rotatef(lfi); lfi->count = 0; } } return ret; } int TRACE_MSG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&message_lfi, fmt, ap); va_end(ap); return ret; } int TRACE_SHORTMSG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&shortmessage_lfi, fmt, ap); va_end(ap); return ret; } int LOG_MSG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&log_lfi, fmt, ap); va_end(ap); return ret; } int TRACE_CALLDEBUG(const char* fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = _trace(&calldebug_lfi, fmt, ap); va_end(ap); return ret; } void print_errors() { if (total_errors == 0) { return; } fprintf(stderr, "%s\n", screen_last_error); if (total_errors > 1) { if (screen_logfile[0] != '\0') { fprintf(stderr, "There were more errors, see '%s' file\n", screen_logfile); } else { fprintf(stderr, "There were more errors, enable -trace_err to log them.\n"); } } fflush(stderr); } static void _advance(char*& c, const int snprintfResult) { if (snprintfResult > 0) { c += snprintfResult; } } static void _screen_error(int fatal, bool use_errno, int error, const char *fmt, va_list ap) { static unsigned long long count = 0; struct timeval currentTime; CStat::globalStat(fatal ? CStat::E_FATAL_ERRORS : CStat::E_WARNING); GET_TIME (¤tTime); const std::size_t bufSize = sizeof(screen_last_error) / sizeof(screen_last_error[0]); const char* const bufEnd = &screen_last_error[bufSize]; char* c = screen_last_error; _advance(c, snprintf(c, bufEnd - c, "%s: ", CStat::formatTime(¤tTime))); if (c < bufEnd) { _advance(c, vsnprintf(c, bufEnd - c, fmt, ap)); } if (use_errno && c < bufEnd) { _advance(c, snprintf(c, bufEnd - c, ", errno = %d (%s)", error, strerror(error))); } total_errors++; if (!error_lfi.fptr && print_all_responses) { rotate_errorf(); if (error_lfi.fptr) { fprintf(error_lfi.fptr, "The following events occurred:\n"); fflush(error_lfi.fptr); } else { if (c < bufEnd) { _advance(c, snprintf(c, bufEnd - c, "Unable to create '%s': %s.\n", screen_logfile, strerror(errno))); } sipp_exit(EXIT_FATAL_ERROR); } } if (error_lfi.fptr) { count += fprintf(error_lfi.fptr, "%s", screen_last_error); fflush(error_lfi.fptr); if (ringbuffer_size && count > ringbuffer_size) { rotate_errorf(); count = 0; } if (max_log_size && count > max_log_size) { print_all_responses = 0; if (error_lfi.fptr) { fflush(error_lfi.fptr); fclose(error_lfi.fptr); error_lfi.fptr = NULL; error_lfi.overwrite = false; } } } else if (fatal) { fprintf(stderr, "%s\n", screen_last_error); fflush(stderr); } if (fatal) { if (error == EADDRINUSE) { sipp_exit(EXIT_BIND_ERROR); } else { sipp_exit(EXIT_FATAL_ERROR); } } } extern "C" { void ERROR(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(true, false, 0, fmt, ap); va_end(ap); assert(0); } void ERROR_NO(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(true, true, errno, fmt, ap); va_end(ap); assert(0); } void WARNING(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(false, false, 0, fmt, ap); va_end(ap); } void WARNING_NO(const char *fmt, ...) { va_list ap; va_start(ap, fmt); _screen_error(false, true, errno, fmt, ap); va_end(ap); } } sipp-3.6.1/src/sipp_unittest.cpp0000664000175000017500000000261313730472040016263 0ustar walterwalter/* * 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 * * Author : Rob Day - 11 May 2014 */ #define GLOBALS_FULL_DEFINITION #include "sipp.hpp" #include "gtest/gtest.h" #include "gmock/gmock.h" #include namespace testing { std::string FLAGS_gmock_verbose = "verbose"; } int main(int argc, char* argv[]) { globalVariables = new AllocVariableTable(NULL); userVariables = new AllocVariableTable(globalVariables); main_scenario = new scenario(0, 0); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } /* Quickfix to fix unittests that depend on sipp_exit availability, * now that sipp_exit has been moved into sipp.cpp which is not * included. */ void sipp_exit(int rc) { exit(rc); } sipp-3.6.1/src/reporttask.cpp0000664000175000017500000000553413730472040015554 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research */ #include "sipp.hpp" class stattask *stattask::instance = NULL; class screentask *screentask::instance = NULL; void stattask::initialize() { assert(instance == NULL); if (dumpInFile || useCountf || useErrorCodesf) { instance = new stattask(); } } void screentask::initialize() { assert(instance == NULL); if (report_freq) { instance = new screentask(); } } void stattask::dump() { WARNING("Statistics reporting task."); } void screentask::dump() { WARNING("Screen update task."); } void screentask::report(bool last) { print_statistics(last); display_scenario->stats->computeStat(CStat::E_RESET_PD_COUNTERS); last_report_time = getmilliseconds(); scheduling_loops = 0; } bool screentask::run() { if (quitting > 11) { delete this; return false; } if (getmilliseconds() - last_report_time >= report_freq) { report(false); } setPaused(); return true; } unsigned int screentask::wake() { return last_report_time + report_freq; } void stattask::report() { if(dumpInFile) { main_scenario->stats->dumpData(); } if (useCountf) { print_count_file(countf, 0); } if (useErrorCodesf) { print_error_codes_file(codesf); } main_scenario->stats->computeStat(CStat::E_RESET_PL_COUNTERS); last_dump_time = clock_tick; } bool stattask::run() { /* Statistics Logs. */ if((getmilliseconds() - last_dump_time) >= report_freq_dumpLog) { report(); } setPaused(); return true; } unsigned int stattask::wake() { return last_dump_time + report_freq_dumpLog; } sipp-3.6.1/src/screen.cpp0000664000175000017500000010317713730472040014637 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ #include #include "screen.hpp" #include "sipp.hpp" /* Export these so others needn't include curses.h */ int key_backspace = KEY_BACKSPACE; int key_dc = KEY_DC; int screen_inited = 0; ScreenPrinter* sp; #ifdef RTP_STREAM double last_rtpstream_rate_out = 0; double last_rtpstream_rate_in = 0; #endif /* ERR is actually -1, but this prevents us from needing to use curses.h in * sipp.cpp. */ int screen_readkey() { int c = getch(); if (c == ERR) { return -1; } return c; } void screen_exit() { if (!screen_inited) { return; } clear(); refresh(); endwin(); screen_inited = 0; } void screen_init() { if (backgroundMode || screen_inited) { return; } screen_inited = 1; setlocale(LC_ALL, ""); initscr(); cbreak(); noecho(); clear(); } void print_statistics(int last) { if (backgroundMode == false && display_scenario) { sp->redraw(); } } void ScreenPrinter::print_closing_stats() { M_last = true; get_lines(); for (auto line : lines) { printf("%s\n", line.c_str()); } if (currentScreenToDisplay != DISPLAY_STAT_SCREEN) { currentScreenToDisplay = DISPLAY_STAT_SCREEN; get_lines(); for (auto line : lines) { printf("%s\n", line.c_str()); } } } void ScreenPrinter::print_to_file(FILE* f) { get_lines(); for (auto line : lines) { fprintf(f, "%s\n", line.c_str()); } } extern int command_mode; extern char* command_buffer; void ScreenPrinter::redraw() { if (!M_headless) { get_lines(); erase(); for (auto line : lines) { printw("%s\n", line.c_str()); } if (command_mode) { printw("\nCommand: %s", command_buffer ? command_buffer : ""); } refresh(); } } void ScreenPrinter::get_lines() { lines.clear(); switch (currentScreenToDisplay) { case DISPLAY_STAT_SCREEN: lines.push_back("----------------------------- Statistics Screen " "------- [1-9]: Change Screen --"); draw_stats_screen(); break; case DISPLAY_REPARTITION_SCREEN: lines.push_back("---------------------------- Repartition Screen " "------- [1-9]: Change Screen --"); draw_repartition_screen(1); break; case DISPLAY_VARIABLE_SCREEN: lines.push_back("----------------------------- Variables Screen " "-------- [1-9]: Change Screen --"); draw_vars_screen(); break; case DISPLAY_TDM_MAP_SCREEN: lines.push_back("------------------------------ TDM map Screen " "--------- [1-9]: Change Screen --"); draw_tdm_screen(); break; case DISPLAY_SECONDARY_REPARTITION_SCREEN: lines.push_back("--------------------------- Repartition " + std::to_string(currentRepartitionToDisplay) + " Screen ------ [1-9]: Change Screen --"); draw_repartition_screen(currentRepartitionToDisplay); break; case DISPLAY_SCENARIO_SCREEN: default: lines.push_back("------------------------------ Scenario Screen " "-------- [1-9]: Change Screen --"); draw_scenario_screen(); break; } unsigned const bufsiz = 80; char buf[bufsiz]; if (!M_last && screen_last_error[0]) { char* errstart = screen_last_error; int colonsleft = 3; /* We want to skip the time. */ while (*errstart && colonsleft) { if (*errstart == ':') { colonsleft--; } errstart++; } while (isspace(*errstart)) { errstart++; } if (strlen(errstart) > 60) { snprintf(buf, bufsiz, "Last Error: %.60s...", errstart); } else { snprintf(buf, bufsiz, "Last Error: %s", errstart); } lines.push_back(buf); } if (M_last) { lines.push_back("------------------------------ Test Terminated " "--------------------------------"); } else if (quitting) { lines.push_back( "------- Waiting for active calls to end. Press [q] again " "to force exit. -------"); } else if (paused) { lines.push_back("----------------- Traffic Paused - Press [p] again to " "resume ------------------"); } else if (cpu_max) { lines.push_back("-------------------------------- CPU CONGESTED " "---------------------------------"); } else if (outbound_congestion) { lines.push_back("------------------------------ OUTBOUND CONGESTION " "-----------------------------"); } else { if (creationMode == MODE_CLIENT) { switch (thirdPartyMode) { case MODE_MASTER: lines.push_back( "-----------------------3PCC extended mode - Master " "side -------------------------"); break; case MODE_3PCC_CONTROLLER_A: lines.push_back( "----------------------- 3PCC Mode - Controller A " "side -------------------------"); break; case MODE_3PCC_NONE: lines.push_back( "------ [+|-|*|/]: Adjust rate ---- [q]: Soft exit " "---- [p]: Pause traffic -----"); break; default: ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode); } } else { assert(creationMode == MODE_SERVER); switch (thirdPartyMode) { case MODE_3PCC_A_PASSIVE: lines.push_back( "------------------ 3PCC Mode - Controller A side " "(passive) --------------------"); break; case MODE_3PCC_CONTROLLER_B: lines.push_back( "----------------------- 3PCC Mode - Controller B " "side -------------------------"); break; case MODE_MASTER_PASSIVE: lines.push_back( "------------------ 3PCC extended mode - Master " "side (passive) --------------------"); break; case MODE_SLAVE: lines.push_back( "----------------------- 3PCC extended mode - Slave " "side -------------------------"); break; case MODE_3PCC_NONE: lines.push_back( "------------------------------ SIPp Server Mode " "-------------------------------"); break; default: ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode); } } } } bool do_hide = true; bool show_index = false; void ScreenPrinter::draw_scenario_screen() { unsigned const bufsiz = 80; char buf[bufsiz]; char left_buf[40]; char right_buf[bufsiz]; int divisor; extern int pollnfds; unsigned long long total_calls = display_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + display_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated); if (creationMode == MODE_SERVER) { lines.push_back(" Port Total-time Total-calls Transport"); snprintf(buf, bufsiz, " %-5d %6lu.%02lu s %8llu %s", local_port, clock_tick / 1000, (clock_tick % 1000) / 10, total_calls, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); } else { assert(creationMode == MODE_CLIENT); if (users >= 0) { lines.push_back(" Users (length) Port Total-time " "Total-calls Remote-host"); snprintf(buf, bufsiz, " %d (%d ms) %-5d %6lu.%02lu s %8llu %s:%d(%s)", users, duration, local_port, clock_tick / 1000, (clock_tick % 1000) / 10, total_calls, remote_ip, remote_port, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); } else { lines.push_back(" Call rate (length) Port Total-time " "Total-calls Remote-host"); snprintf( buf, bufsiz, " %3.1f(%d ms)/%5.3fs %-5d %6lu.%02lu s %8llu %s:%d(%s)", rate, duration, (double)rate_period_ms / 1000.0, local_port, clock_tick / 1000, (clock_tick % 1000) / 10, total_calls, remote_ip, remote_port, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); } } lines.push_back(""); /* 1st line */ unsigned long ms_since_last_tick = clock_tick - last_report_time; if (total_calls < stop_after) { snprintf(left_buf, 40, "%llu new calls during %lu.%03lu s period", display_scenario->stats->GetStat( CStat::CPT_PD_IncomingCallCreated) + display_scenario->stats->GetStat( CStat::CPT_PD_OutgoingCallCreated), ms_since_last_tick / 1000, ms_since_last_tick % 1000); } else { snprintf(left_buf, 40, "Call limit %lu hit, %0.1f s period ", stop_after, (double)ms_since_last_tick / 100.0); } snprintf(right_buf, 40, "%lu ms scheduler resolution", ms_since_last_tick / std::max(scheduling_loops, 1ul)); snprintf(buf, bufsiz, " %-38s %-37s", left_buf, right_buf); lines.push_back(buf); /* 2nd line */ if (creationMode == MODE_SERVER) { snprintf(left_buf, 40, "%llu calls", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)); } else { snprintf(left_buf, 40, "%llu calls (limit %u)", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall), open_calls_allowed); } snprintf( buf, bufsiz, " %-38s Peak was %llu calls, after %llu s", left_buf, display_scenario->stats->GetStat(CStat::CPT_C_CurrentCallPeak), display_scenario->stats->GetStat(CStat::CPT_C_CurrentCallPeakTime)); lines.push_back(buf); snprintf(buf, bufsiz, " %d Running, %d Paused, %d Woken up", last_running_calls, last_paused_calls, last_woken_calls); last_woken_calls = 0; lines.push_back(buf); /* 3rd line dead call msgs, and optional out-of-call msg */ snprintf(left_buf, 40, "%llu dead call msg (discarded)", display_scenario->stats->GetStat(CStat::CPT_G_C_DeadCallMsgs)); if (creationMode == MODE_CLIENT) { snprintf( buf, bufsiz, " %-38s %llu out-of-call msg (discarded)", left_buf, display_scenario->stats->GetStat(CStat::CPT_G_C_OutOfCallMsgs)); } else { snprintf(buf, bufsiz, " %-38s", left_buf); } lines.push_back(buf); if (compression) { snprintf(buf, bufsiz, " Comp resync: %d sent, %d recv", resynch_send, resynch_recv); lines.push_back(buf); } /* 4th line , sockets and optional errors */ snprintf(left_buf, 40, "%d open sockets", pollnfds); snprintf(buf, bufsiz, " %-38s %lu/%lu/%lu %s errors (send/recv/cong)", left_buf, nb_net_send_errors, nb_net_recv_errors, nb_net_cong, TRANSPORT_TO_STRING(transport)); lines.push_back(buf); #ifdef PCAPPLAY /* if has media abilities */ if (hasMedia != 5) { snprintf(left_buf, 40, "%lu Total RTP pckts sent ", rtp_pckts_pcap); if (ms_since_last_tick) { snprintf(buf, bufsiz, " %-38s %lu.%03lu last period RTP rate (kB/s)", left_buf, rtp_bytes_pcap / ms_since_last_tick, rtp_bytes_pcap % ms_since_last_tick); } rtp_bytes_pcap = 0; rtp2_bytes_pcap = 0; lines.push_back(buf); } #endif #ifdef RTP_STREAM /* if we have rtp stream thread running */ if (rtpstream_numthreads) { unsigned long tempbytes; unsigned long last_tick = clock_tick; /* Saved clock_tick to last_tick and use that in calcs since clock tick */ /* can change during calculations. */ if (ms_since_last_tick) { tempbytes = rtpstream_bytes_out; /* Calculate integer and fraction parts of rtp bandwidth; this value * will be saved and reused in the case where last_tick==last_report_time */ last_rtpstream_rate_out = ((double)tempbytes) / ms_since_last_tick; /* Potential race condition betwen multiple threads updating the * rtpstream_bytes value. We subtract the saved tempbytes value * rather than setting it to zero to minimise the chances of missing * an update to rtpstream_bytes [update between printing stats and * zeroing the counter]. Ideally we would atomically subtract * tempbytes from rtpstream_bytes. */ rtpstream_bytes_out -= tempbytes; tempbytes = rtpstream_bytes_in; last_rtpstream_rate_in = ((double)tempbytes) / ms_since_last_tick; rtpstream_bytes_in -= tempbytes; } snprintf(left_buf, 40, "%lu Total RTP pckts sent", rtpstream_pckts); snprintf(buf, bufsiz," %-38s %.3f kB/s RTP OUT", left_buf, last_rtpstream_rate_out); lines.push_back(buf); snprintf(left_buf, 40, "%lu RTP sending threads active", rtpstream_numthreads); snprintf(buf, bufsiz, " %-38s %.3f kB/s RTP IN", left_buf, last_rtpstream_rate_in); lines.push_back(buf); } #endif /* 5th line, RTP echo statistics */ if (rtp_echo_enabled && media_socket_audio > 0) { snprintf(left_buf, 40, "%lu Total echo RTP pckts 1st stream", rtp_pckts); if (ms_since_last_tick) { snprintf(buf, bufsiz, " %-38s %lu.%03lu last period RTP rate (kB/s)", left_buf, rtp_bytes / ms_since_last_tick, rtp_bytes % ms_since_last_tick); lines.push_back(buf); } snprintf(left_buf, 40, "%lu Total echo RTP pckts 2nd stream", rtp2_pckts); if (ms_since_last_tick) { snprintf(buf, bufsiz, " %-38s %lu.%03lu last period RTP rate (kB/s)", left_buf, rtp2_bytes / ms_since_last_tick, rtp2_bytes % ms_since_last_tick); lines.push_back(buf); } rtp_bytes = 0; rtp2_bytes = 0; } /* Scenario counters */ lines.push_back(""); if (!lose_packets) { snprintf(buf, bufsiz, " " "Messages Retrans Timeout Unexpected-Msg"); } else { snprintf(buf, bufsiz, " " "Messages Retrans Timeout Unexp. Lost"); } lines.push_back(buf); for (unsigned long index = 0; index < display_scenario->messages.size(); index++) { buf[0] = 0; message* curmsg = display_scenario->messages[index]; if (do_hide && curmsg->hide) { continue; } int buf_len = 0; if (show_index) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, "%-2lu:", index); } if (SendingMessage* src = curmsg->send_scheme) { if (creationMode == MODE_SERVER) { if (src->isResponse()) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " <---------- %-10d ", src->getCode()); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " <---------- %-10s ", src->getMethod()); } } else { if (src->isResponse()) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10d ----------> ", src->getCode()); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10s ----------> ", src->getMethod()); } } if (curmsg->start_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " B-RTD%d ", curmsg->start_rtd); } else if (curmsg->stop_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " E-RTD%d ", curmsg->stop_rtd); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " "); } if (curmsg->retrans_delay) { buf_len += snprintf( buf + buf_len, bufsiz - buf_len, "%-9lu %-9lu %-9lu %-9s %-9s", curmsg->nb_sent, curmsg->nb_sent_retrans, curmsg->nb_timeout, "" /* Unexpected */, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } else { buf_len += snprintf( buf + buf_len, bufsiz - buf_len, "%-9lu %-9lu %-9s %-9s %-9s", curmsg->nb_sent, curmsg->nb_sent_retrans, "", /* Timeout. */ "" /* Unexpected. */, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } } else if (curmsg->recv_response) { if (creationMode == MODE_SERVER) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " ----------> %-10d ", curmsg->recv_response); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10d <---------- ", curmsg->recv_response); } if (curmsg->start_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " B-RTD%d ", curmsg->start_rtd); } else if (curmsg->stop_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " E-RTD%d ", curmsg->stop_rtd); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " "); } buf_len += snprintf(buf + buf_len, bufsiz - buf_len, "%-9ld %-9ld %-9ld %-9ld %-9s", curmsg->nb_recv, curmsg->nb_recv_retrans, curmsg->nb_timeout, curmsg->nb_unexp, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } else if (curmsg->pause_distribution || (curmsg->pause_variable != -1)) { char* desc = curmsg->pause_desc; if (!desc) { desc = (char*)malloc(24); if (curmsg->pause_distribution) { desc[0] = '\0'; curmsg->pause_distribution->timeDescr(desc, 23); } else { snprintf(desc, 23, "$%s", display_scenario->allocVars->getName( curmsg->pause_variable)); } desc[23] = '\0'; curmsg->pause_desc = desc; } int len = strlen(desc) < 9 ? 9 : strlen(desc); if (creationMode == MODE_SERVER) { snprintf(left_buf, 40, " [%9s] Pause%*s", desc, 23 - len > 0 ? 23 - len : 0, ""); } else { snprintf(left_buf, 40, " Pause [%9s]%*s", desc, 18 - len > 0 ? 18 - len : 0, ""); } snprintf(buf, bufsiz, "%s%-9d %-9lu", left_buf, curmsg->sessions, curmsg->nb_unexp); } else if (curmsg->recv_request) { if (creationMode == MODE_SERVER) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " ----------> %-10s ", curmsg->recv_request); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " %10s <---------- ", curmsg->recv_request); } if (curmsg->start_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " B-RTD%d ", curmsg->start_rtd); } else if (curmsg->stop_rtd) { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " E-RTD%d ", curmsg->stop_rtd); } else { buf_len += snprintf(buf + buf_len, bufsiz - buf_len, " "); } buf_len += snprintf(buf + buf_len, bufsiz - buf_len, "%-9ld %-9ld %-9ld %-9ld %-9s", curmsg->nb_recv, curmsg->nb_recv_retrans, curmsg->nb_timeout, curmsg->nb_unexp, (lose_packets && curmsg->nb_lost) ? std::to_string(curmsg->nb_lost).c_str() : ""); } else if (curmsg->M_type == MSG_TYPE_NOP) { if (curmsg->display_str) { snprintf(buf, bufsiz, " %s", curmsg->display_str); } else { snprintf(buf, bufsiz, " [ NOP ] "); } } else if (curmsg->M_type == MSG_TYPE_RECVCMD) { snprintf(left_buf, 40, " [ Received Command ] "); snprintf(buf, bufsiz, "%s%-9ld %-9s %-9s %-9s", left_buf, curmsg->M_nbCmdRecv, "", curmsg->retrans_delay ? std::to_string(curmsg->nb_timeout).c_str() : "", ""); } else if (curmsg->M_type == MSG_TYPE_SENDCMD) { snprintf(left_buf, 40, " [ Sent Command ] "); snprintf(buf, bufsiz, "%s%-9lu %-9s %-9s", left_buf, curmsg->M_nbCmdSent, "", ""); } else { ERROR("Scenario command not implemented in display"); } lines.push_back(buf); if (curmsg->crlf) { lines.push_back(""); } } } // Warning! All DISPLAY_ macros must be called where 'bufsiz', 'buf' and 'lines' are defined. #define DISPLAY_CROSS_LINE() \ snprintf(buf, bufsiz, "-------------------------+---------------------------+--------------------------"); \ lines.push_back(buf); #define DISPLAY_HEADER() \ snprintf(buf, bufsiz, " Counter Name | Periodic value | Cumulative value"); \ lines.push_back(buf); #define DISPLAY_TXT_COL(T1, V1, V2) \ snprintf(buf, bufsiz, " %-22.22s | %-25.25s | %-24.24s ", T1, V1, V2); \ lines.push_back(buf); #define DISPLAY_VAL_RATEF_COL(T1, V1, V2) \ snprintf(buf, bufsiz, " %-22.22s | %8.3f cps | %8.3f cps ", T1, V1, V2); \ lines.push_back(buf); #define DISPLAY_2VAL(T1, V1, V2) \ snprintf(buf, bufsiz, " %-22.22s | %8llu | %8llu ", T1, V1, V2); \ lines.push_back(buf); #define DISPLAY_CUMUL(T1, V1) \ snprintf(buf, bufsiz, " %-22.22s | | %8llu ", T1, V1); \ lines.push_back(buf); #define DISPLAY_PERIO(T1, V1) \ snprintf(buf, bufsiz, " %-22.22s | %8llu | ", T1, V1); \ lines.push_back(buf); #define DISPLAY_TXT(T1, V1) \ snprintf(buf, bufsiz, " %-22.22s | %-52.52s ", T1, V1); \ lines.push_back(buf); #define DISPLAY_INFO(T1) \ snprintf(buf, bufsiz, " %-77.77s", T1); \ lines.push_back(buf); #define DISPLAY_REPART(T1, T2, V1) \ snprintf(buf, bufsiz, " %10d ms <= n < %10d ms : %10lu", T1, T2, V1); \ lines.push_back(buf); #define DISPLAY_LAST_REPART(T1, V1) \ snprintf(buf, bufsiz, " %14.14s n >= %10d ms : %10lu", "", T1, V1); \ lines.push_back(buf); void ScreenPrinter::draw_stats_screen() { long localElapsedTime, globalElapsedTime ; struct timeval currentTime; float averageCallRate; float realInstantCallRate; unsigned long numberOfCall; CStat* s = display_scenario->stats; unsigned const bufsiz = 256; char buf[bufsiz]; GET_TIME (¤tTime); // computing the real call rate globalElapsedTime = s->computeDiffTimeInMs (¤tTime, &s->M_startTime); localElapsedTime = s->computeDiffTimeInMs (¤tTime, &s->M_pdStartTime); // the call rate is for all the call : incoming and outgoing numberOfCall = (s->M_counters[s->CPT_C_IncomingCallCreated] + s->M_counters[s->CPT_C_OutgoingCallCreated]); averageCallRate = (globalElapsedTime > 0 ? 1000*(float)numberOfCall/(float)globalElapsedTime : 0.0); numberOfCall = (s->M_counters[s->CPT_PD_IncomingCallCreated] + s->M_counters[s->CPT_PD_OutgoingCallCreated]); realInstantCallRate = (localElapsedTime > 0 ? 1000*(float)numberOfCall / (float)localElapsedTime : 0.0); // build and display header info DISPLAY_TXT ("Start Time ", s->formatTime(&s->M_startTime)); DISPLAY_TXT ("Last Reset Time", s->formatTime(&s->M_pdStartTime)); DISPLAY_TXT ("Current Time", s->formatTime(¤tTime)); // printing the header in the middle DISPLAY_CROSS_LINE(); DISPLAY_HEADER(); DISPLAY_CROSS_LINE(); DISPLAY_TXT_COL ("Elapsed Time", s->msToHHMMSSus(localElapsedTime), s->msToHHMMSSus(globalElapsedTime)); DISPLAY_VAL_RATEF_COL ("Call Rate", realInstantCallRate, averageCallRate); DISPLAY_CROSS_LINE (); DISPLAY_2VAL ("Incoming calls created", s->M_counters[s->CPT_PD_IncomingCallCreated], s->M_counters[s->CPT_C_IncomingCallCreated]); DISPLAY_2VAL ("Outgoing calls created", s->M_counters[s->CPT_PD_OutgoingCallCreated], s->M_counters[s->CPT_C_OutgoingCallCreated]); DISPLAY_CUMUL ("Total Calls created", s->M_counters[s->CPT_C_IncomingCallCreated] + s->M_counters[s->CPT_C_OutgoingCallCreated]); DISPLAY_PERIO ("Current Calls", s->M_counters[s->CPT_C_CurrentCall]); if (s->M_genericMap.size()) { DISPLAY_CROSS_LINE (); } for (unsigned int i = 1; i < s->M_genericMap.size() + 1; i++) { char *disp = (char *)malloc(20 + strlen(s->M_genericDisplay[i])); sprintf(disp, "Counter %s", s->M_genericDisplay[i]); DISPLAY_2VAL(disp, s->M_genericCounters[(i - 1)* GENERIC_TYPES + GENERIC_PD], s->M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_C]); free(disp); } DISPLAY_CROSS_LINE (); DISPLAY_2VAL ("Successful call", s->M_counters[s->CPT_PD_SuccessfulCall], s->M_counters[s->CPT_C_SuccessfulCall]); DISPLAY_2VAL ("Failed call", s->M_counters[s->CPT_PD_FailedCall], s->M_counters[s->CPT_C_FailedCall]); DISPLAY_CROSS_LINE (); for (int i = 1; i <= s->nRtds(); i++) { char buf2[80]; snprintf(buf2, 80, "Response Time %s", s->M_revRtdMap[i]); DISPLAY_TXT_COL (buf2, s->msToHHMMSSus( (unsigned long)s->computeRtdMean(i, GENERIC_PD)), s->msToHHMMSSus( (unsigned long)s->computeRtdMean(i, GENERIC_C))); } DISPLAY_TXT_COL ("Call Length", s->msToHHMMSSus( (unsigned long)s->computeMean(s->CPT_PD_AverageCallLength_Sum, s->CPT_PD_NbOfCallUsedForAverageCallLength ) ), s->msToHHMMSSus( (unsigned long)s->computeMean(s->CPT_C_AverageCallLength_Sum, s->CPT_C_NbOfCallUsedForAverageCallLength) )); } void ScreenPrinter::draw_repartition_screen(int which) { unsigned const bufsiz = 80; char buf[bufsiz]; char buf2[bufsiz]; CStat* s = display_scenario->stats; if (which > s->nRtds()) { DISPLAY_INFO (" "); return; } snprintf(buf2, bufsiz, "Average Response Time Repartition %s", s->M_revRtdMap[which]); DISPLAY_INFO(buf2); draw_repartition_detailed(s->M_ResponseTimeRepartition[which - 1], s->M_SizeOfResponseTimeRepartition); if (which == 1) { // Primary repartition screen DISPLAY_INFO("Average Call Length Repartition"); draw_repartition_detailed(s->M_CallLengthRepartition, s->M_SizeOfCallLengthRepartition); } } void ScreenPrinter::draw_repartition_detailed(CStat::T_dynamicalRepartition * tabRepartition, int sizeOfTab) { unsigned const bufsiz = 80; char buf[bufsiz]; if(tabRepartition != NULL) { for(int i=0; i<(sizeOfTab-1); i++) { if(i==0) { DISPLAY_REPART(0, tabRepartition[i].borderMax, tabRepartition[i].nbInThisBorder); } else { DISPLAY_REPART(tabRepartition[i-1].borderMax, tabRepartition[i].borderMax, tabRepartition[i].nbInThisBorder); } } DISPLAY_LAST_REPART (tabRepartition[sizeOfTab-1].borderMax, tabRepartition[sizeOfTab-1].nbInThisBorder); } else { DISPLAY_INFO (" "); } } void ScreenPrinter::draw_vars_screen() { CActions* actions; CAction* action; bool found; unsigned const bufsiz = 80; char buf[bufsiz]; lines.push_back("Action defined Per Message :"); found = false; for (unsigned int i = 0; i < display_scenario->messages.size(); i++) { message* curmsg = display_scenario->messages[i]; actions = curmsg->M_actions; if (actions != NULL) { switch (curmsg->M_type) { case MSG_TYPE_RECV: snprintf(buf, bufsiz, "=> Message[%u] (Receive Message) - " "[%d] action(s) defined :", i, actions->getActionSize()); break; case MSG_TYPE_RECVCMD: snprintf(buf, bufsiz, "=> Message[%u] (Receive Command Message) - " "[%d] action(s) defined :", i, actions->getActionSize()); break; default: snprintf(buf, bufsiz, "=> Message[%u] - [%d] action(s) defined :", i, actions->getActionSize()); break; } lines.push_back(buf); for (int j = 0; j < actions->getActionSize(); j++) { action = actions->getAction(j); if (action != NULL) { int printed = snprintf(buf, bufsiz, " --> action[%d] = ", j); action->printInfo(buf + printed, bufsiz - printed); lines.push_back(buf); found = true; } } } } if (!found) { lines.push_back("=> No action found on any messages"); } lines.push_back(""); for (unsigned int i = 0; i < (display_scenario->messages.size() + 6 - lines.size()); i++) { lines.push_back(""); } } void ScreenPrinter::draw_tdm_screen() { int i = 0; int in_use = 0; int height = (tdm_map_a + 1) * (tdm_map_b + 1); int width = (tdm_map_c + 1); int total_circuits = height * width; unsigned const bufsiz = 80; char buf[bufsiz] = {0}; lines.push_back("TDM Circuits in use:"); while (i < total_circuits) { int buf_position = std::min(79, i % width); if (tdm_map[i]) { buf[buf_position] = '*'; in_use++; } else { buf[buf_position] = '.'; } i++; if (buf_position == (width - 1)) { lines.push_back(buf); memset(buf, 0, bufsiz); } } lines.push_back(""); snprintf(buf, bufsiz, "%d/%d circuits (%d%%) in use", in_use, total_circuits, int(100 * in_use / total_circuits)); lines.push_back(buf); for (unsigned int i = 0; i < (display_scenario->messages.size() + 8 - height); i++) { lines.push_back(""); } } sipp-3.6.1/src/sipp.cpp0000664000175000017500000024532613730472040014336 0ustar walterwalter/* * 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 * * Author : Richard GAYRAUD - 04 Nov 2003 * Marc LAMBERTON * Olivier JACQUES * Herve PELLAN * David MANSUTTI * Francois-Xavier Kowalski * Gerard Lyonnaz * Francois Draperi (for dynamic_id) * From Hewlett Packard Company. * F. Tarek Rogers * Peter Higginson * Vincent Luba * Shriram Natarajan * Guillaume Teissier from FTR&D * Clement Chen * Wolfgang Beck * Charles P Wright from IBM Research * Martin Van Leeuwen * Andy Aicken * Michael Hirschbichler */ #include #include #include #include #include #ifdef __APPLE__ /* Provide OSX version of extern char **environ; */ #include #define environ (*_NSGetEnviron()) #endif extern char** environ; #define GLOBALS_FULL_DEFINITION #include "sipp.hpp" #include "sip_parser.hpp" #include "socket.hpp" #include "logger.hpp" #include "assert.h" #include "config.h" #include "version.h" extern SIPpSocket *ctrl_socket; extern SIPpSocket *stdin_socket; /* These could be local to main, but for the option processing table. */ static int argiFileName; /***************** Option Handling Table *****************/ struct sipp_option { const char *option; const char *help; int type; void *data; /* Pass 0: Help and other options that should exit immediately. */ /* Pass 1: All other options. */ /* Pass 2: Scenario parsing. */ int pass; }; #define SIPP_OPTION_HELP 1 #define SIPP_OPTION_INT 2 #define SIPP_OPTION_SETFLAG 3 #define SIPP_OPTION_UNSETFLAG 4 #define SIPP_OPTION_STRING 5 #define SIPP_OPTION_ARGI 6 #define SIPP_OPTION_TIME_SEC 7 #define SIPP_OPTION_FLOAT 8 #define SIPP_OPTION_BOOL 10 #define SIPP_OPTION_VERSION 11 #define SIPP_OPTION_TRANSPORT 12 #define SIPP_OPTION_NEED_SSL 13 #define SIPP_OPTION_IP 14 #define SIPP_OPTION_MAX_SOCKET 15 #define SIPP_OPTION_CSEQ 16 #define SIPP_OPTION_SCENARIO 17 #define SIPP_OPTION_RSA 18 #define SIPP_OPTION_LIMIT 19 #define SIPP_OPTION_USERS 20 #define SIPP_OPTION_KEY 21 #define SIPP_OPTION_3PCC 22 #define SIPP_OPTION_TDMMAP 23 #define SIPP_OPTION_TIME_MS 24 #define SIPP_OPTION_SLAVE_CFG 25 #define SIPP_OPTION_3PCC_EXTENDED 26 #define SIPP_OPTION_INPUT_FILE 27 #define SIPP_OPTION_TIME_MS_LONG 28 #define SIPP_OPTION_LONG 29 #define SIPP_OPTION_LONG_LONG 30 #define SIPP_OPTION_DEFAULTS 31 #define SIPP_OPTION_OOC_SCENARIO 32 #define SIPP_OPTION_INDEX_FILE 33 #define SIPP_OPTION_VAR 34 #define SIPP_OPTION_RTCHECK 35 #define SIPP_OPTION_LFNAME 36 #define SIPP_OPTION_LFOVERWRITE 37 #define SIPP_OPTION_PLUGIN 38 #define SIPP_OPTION_NEED_SCTP 39 #define SIPP_HELP_TEXT_HEADER 255 /* Put each option, its help text, and type in this table. */ struct sipp_option options_table[] = { {"h", NULL, SIPP_OPTION_HELP, NULL, 0}, {"help", NULL, SIPP_OPTION_HELP, NULL, 0}, {"", "Scenario file options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"sd", "Dumps a default scenario (embedded in the SIPp executable)", SIPP_OPTION_SCENARIO, NULL, 0}, {"sf", "Loads an alternate XML scenario file. To learn more about XML scenario syntax, use the -sd option to dump embedded scenarios. They contain all the necessary help.", SIPP_OPTION_SCENARIO, NULL, 2}, {"oocsf", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2}, {"oocsn", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2}, { "sn", "Use a default scenario (embedded in the SIPp executable). If this option is omitted, the Standard SipStone UAC scenario is loaded.\n" "Available values in this version:\n\n" "- 'uac' : Standard SipStone UAC (default).\n" "- 'uas' : Simple UAS responder.\n" "- 'regexp' : Standard SipStone UAC - with regexp and variables.\n" "- 'branchc' : Branching and conditional branching in scenarios - client.\n" "- 'branchs' : Branching and conditional branching in scenarios - server.\n\n" "Default 3pcc scenarios (see -3pcc option):\n\n" "- '3pcc-C-A' : Controller A side (must be started after all other 3pcc scenarios)\n" "- '3pcc-C-B' : Controller B side.\n" "- '3pcc-A' : A side.\n" "- '3pcc-B' : B side.\n", SIPP_OPTION_SCENARIO, NULL, 2 }, {"", "IP, port and protocol options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, { "t", "Set the transport mode:\n" "- u1: UDP with one socket (default),\n" "- un: UDP with one socket per call,\n" "- ui: UDP with one socket per IP address. The IP addresses must be defined in the injection file.\n" "- t1: TCP with one socket,\n" "- tn: TCP with one socket per call,\n" #ifdef USE_TLS "- l1: TLS with one socket,\n" "- ln: TLS with one socket per call,\n" #endif #ifdef USE_SCTP "- s1: SCTP with one socket,\n" "- sn: SCTP with one socket per call,\n" #endif "- c1: u1 + compression (only if compression plugin loaded),\n" "- cn: un + compression (only if compression plugin loaded). This plugin is not provided with SIPp.\n" , SIPP_OPTION_TRANSPORT, NULL, 1 }, {"i", "Set the local IP address for 'Contact:','Via:', and 'From:' headers. Default is primary host IP address.\n", SIPP_OPTION_IP, local_ip, 1}, {"p", "Set the local port number. Default is a random free port chosen by the system.", SIPP_OPTION_INT, &user_port, 1}, {"bind_local", "Bind socket to local IP address, i.e. the local IP address is used as the source IP address. If SIPp runs in server mode it will only listen on the local IP address instead of all IP addresses.", SIPP_OPTION_SETFLAG, &bind_local, 1}, {"ci", "Set the local control IP address", SIPP_OPTION_IP, control_ip, 1}, {"cp", "Set the local control port number. Default is 8888.", SIPP_OPTION_INT, &control_port, 1}, {"max_socket", "Set the max number of sockets to open simultaneously. This option is significant if you use one socket per call. Once this limit is reached, traffic is distributed over the sockets already opened. Default value is 50000", SIPP_OPTION_MAX_SOCKET, NULL, 1}, {"max_reconnect", "Set the the maximum number of reconnection.", SIPP_OPTION_INT, &reset_number, 1}, {"reconnect_close", "Should calls be closed on reconnect?", SIPP_OPTION_BOOL, &reset_close, 1}, {"reconnect_sleep", "How long (in milliseconds) to sleep between the close and reconnect?", SIPP_OPTION_TIME_MS, &reset_sleep, 1}, {"rsa", "Set the remote sending address to host:port for sending the messages.", SIPP_OPTION_RSA, NULL, 1}, #ifdef USE_TLS {"tls_cert", "Set the name for TLS Certificate file. Default is 'cacert.pem", SIPP_OPTION_STRING, &tls_cert_name, 1}, {"tls_key", "Set the name for TLS Private Key file. Default is 'cakey.pem'", SIPP_OPTION_STRING, &tls_key_name, 1}, {"tls_ca", "Set the name for TLS CA file. If not specified, X509 verification is not activated.", SIPP_OPTION_STRING, &tls_ca_name, 1}, {"tls_crl", "Set the name for Certificate Revocation List file. If not specified, X509 CRL is not activated.", SIPP_OPTION_STRING, &tls_crl_name, 1}, {"tls_version", "Set the TLS protocol version to use (1.0, 1.1, 1.2) -- default is autonegotiate", SIPP_OPTION_FLOAT, &tls_version, 1}, #else {"tls_cert", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_key", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_ca", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_crl", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, {"tls_version", NULL, SIPP_OPTION_NEED_SSL, NULL, 1}, #endif #ifdef USE_SCTP {"multihome", "Set multihome address for SCTP", SIPP_OPTION_IP, multihome_ip, 1}, {"heartbeat", "Set heartbeat interval in ms for SCTP", SIPP_OPTION_INT, &heartbeat, 1}, {"assocmaxret", "Set association max retransmit counter for SCTP", SIPP_OPTION_INT, &assocmaxret, 1}, {"pathmaxret", "Set path max retransmit counter for SCTP", SIPP_OPTION_INT, &pathmaxret, 1}, {"pmtu", "Set path MTU for SCTP", SIPP_OPTION_INT, &pmtu, 1}, {"gracefulclose", "If true, SCTP association will be closed with SHUTDOWN (default).\n If false, SCTP association will be closed by ABORT.\n", SIPP_OPTION_BOOL, &gracefulclose, 1}, #else {"multihome", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"heartbeat", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"assocmaxret", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"pathmaxret", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"pmtu", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, {"gracefulclose", NULL, SIPP_OPTION_NEED_SCTP, NULL, 1}, #endif {"", "SIPp overall behavior options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"v", "Display version and copyright information.", SIPP_OPTION_VERSION, NULL, 0}, {"bg", "Launch SIPp in background mode.", SIPP_OPTION_SETFLAG, &backgroundMode, 1}, {"nostdin", "Disable stdin.\n", SIPP_OPTION_SETFLAG, &nostdin, 1}, {"plugin", "Load a plugin.", SIPP_OPTION_PLUGIN, NULL, 1}, {"sleep", "How long to sleep for at startup. Default unit is seconds.", SIPP_OPTION_TIME_SEC, &sleeptime, 1}, {"skip_rlimit", "Do not perform rlimit tuning of file descriptor limits. Default: false.", SIPP_OPTION_SETFLAG, &skip_rlimit, 1}, {"buff_size", "Set the send and receive buffer size.", SIPP_OPTION_INT, &buff_size, 1}, {"sendbuffer_warn", "Produce warnings instead of errors on SendBuffer failures.", SIPP_OPTION_BOOL, &sendbuffer_warn, 1}, {"lost", "Set the number of packets to lose by default (scenario specifications override this value).", SIPP_OPTION_FLOAT, &global_lost, 1}, {"key", "keyword value\nSet the generic parameter named \"keyword\" to \"value\".", SIPP_OPTION_KEY, NULL, 1}, {"set", "variable value\nSet the global variable parameter named \"variable\" to \"value\".", SIPP_OPTION_VAR, NULL, 3}, {"tdmmap", "Generate and handle a table of TDM circuits.\n" "A circuit must be available for the call to be placed.\n" "Format: -tdmmap {0-3}{99}{5-8}{1-31}", SIPP_OPTION_TDMMAP, NULL, 1}, {"dynamicStart", "variable value\nSet the start offset of dynamic_id variable", SIPP_OPTION_INT, &startDynamicId, 1}, {"dynamicMax", "variable value\nSet the maximum of dynamic_id variable ", SIPP_OPTION_INT, &maxDynamicId, 1}, {"dynamicStep", "variable value\nSet the increment of dynamic_id variable", SIPP_OPTION_INT, &stepDynamicId, 1}, {"", "Call behavior options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"aa", "Enable automatic 200 OK answer for INFO, NOTIFY, OPTIONS and UPDATE.", SIPP_OPTION_SETFLAG, &auto_answer, 1}, {"base_cseq", "Start value of [cseq] for each call.", SIPP_OPTION_CSEQ, NULL, 1}, {"cid_str", "Call ID string (default %u-%p@%s). %u=call_number, %s=ip_address, %p=process_number, %%=% (in any order).", SIPP_OPTION_STRING, &call_id_string, 1}, {"d", "Controls the length of calls. More precisely, this controls the duration of 'pause' instructions in the scenario, if they do not have a 'milliseconds' section. Default value is 0 and default unit is milliseconds.", SIPP_OPTION_TIME_MS, &duration, 1}, {"deadcall_wait", "How long the Call-ID and final status of calls should be kept to improve message and error logs (default unit is ms).", SIPP_OPTION_TIME_MS, &deadcall_wait, 1}, {"auth_uri", "Force the value of the URI for authentication.\n" "By default, the URI is composed of remote_ip:remote_port.", SIPP_OPTION_STRING, &auth_uri, 1}, {"au", "Set authorization username for authentication challenges. Default is taken from -s argument", SIPP_OPTION_STRING, &auth_username, 1}, {"ap", "Set the password for authentication challenges. Default is 'password'", SIPP_OPTION_STRING, &auth_password, 1}, {"s", "Set the username part of the request URI. Default is 'service'.", SIPP_OPTION_STRING, &service, 1}, {"default_behaviors", "Set the default behaviors that SIPp will use. Possible values are:\n" "- all\tUse all default behaviors\n" "- none\tUse no default behaviors\n" "- bye\tSend byes for aborted calls\n" "- abortunexp\tAbort calls on unexpected messages\n" "- pingreply\tReply to ping requests\n" "If a behavior is prefaced with a -, then it is turned off. Example: all,-bye\n", SIPP_OPTION_DEFAULTS, &default_behaviors, 1}, {"nd", "No Default. Disable all default behavior of SIPp which are the following:\n" "- On UDP retransmission timeout, abort the call by sending a BYE or a CANCEL\n" "- On receive timeout with no ontimeout attribute, abort the call by sending a BYE or a CANCEL\n" "- On unexpected BYE send a 200 OK and close the call\n" "- On unexpected CANCEL send a 200 OK and close the call\n" "- On unexpected PING send a 200 OK and continue the call\n" "- On any other unexpected message, abort the call by sending a BYE or a CANCEL\n", SIPP_OPTION_UNSETFLAG, &default_behaviors, 1}, {"pause_msg_ign", "Ignore the messages received during a pause defined in the scenario ", SIPP_OPTION_SETFLAG, &pause_msg_ign, 1}, {"callid_slash_ign", "Don't treat a triple-slash in Call-IDs as indicating an extra SIPp prefix.", SIPP_OPTION_SETFLAG, &callidSlash, 1}, {"", "Injection file options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"inf", "Inject values from an external CSV file during calls into the scenarios.\n" "First line of this file say whether the data is to be read in sequence (SEQUENTIAL), random (RANDOM), or user (USER) order.\n" "Each line corresponds to one call and has one or more ';' delimited data fields. Those fields can be referred as [field0], [field1], ... in the xml scenario file. Several CSV files can be used simultaneously (syntax: -inf f1.csv -inf f2.csv ...)", SIPP_OPTION_INPUT_FILE, NULL, 1}, {"infindex", "file field\nCreate an index of file using field. For example -inf ../path/to/users.csv -infindex users.csv 0 creates an index on the first key.", SIPP_OPTION_INDEX_FILE, NULL, 1 }, {"ip_field", "Set which field from the injection file contains the IP address from which the client will send its messages.\n" "If this option is omitted and the '-t ui' option is present, then field 0 is assumed.\n" "Use this option together with '-t ui'", SIPP_OPTION_INT, &peripfield, 1}, {"", "RTP behaviour options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"mi", "Set the local media IP address (default: local primary host IP address)", SIPP_OPTION_IP, media_ip, 1}, {"rtp_echo", "Enable RTP echo. RTP/UDP packets received on port defined by -mp are echoed to their sender.\n" "RTP/UDP packets coming on this port + 2 are also echoed to their sender (used for sound and video echo).", SIPP_OPTION_SETFLAG, &rtp_echo_enabled, 1}, {"mb", "Set the RTP echo buffer size (default: 2048).", SIPP_OPTION_INT, &media_bufsize, 1}, {"mp", "Set the local RTP echo port number. Default is 6000.", SIPP_OPTION_INT, &user_media_port, 1}, #ifdef RTP_STREAM {"rtp_payload", "RTP default payload type.", SIPP_OPTION_INT, &rtp_default_payload, 1}, {"rtp_threadtasks", "RTP number of playback tasks per thread.", SIPP_OPTION_INT, &rtp_tasks_per_thread, 1}, {"rtp_buffsize", "Set the rtp socket send/receive buffer size.", SIPP_OPTION_INT, &rtp_buffsize, 1}, #endif {"", "Call rate options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"r", "Set the call rate (in calls per seconds). This value can be" "changed during test by pressing '+', '_', '*' or '/'. Default is 10.\n" "pressing '+' key to increase call rate by 1 * rate_scale,\n" "pressing '-' key to decrease call rate by 1 * rate_scale,\n" "pressing '*' key to increase call rate by 10 * rate_scale,\n" "pressing '/' key to decrease call rate by 10 * rate_scale.\n", SIPP_OPTION_FLOAT, &rate, 1}, {"rp", "Specify the rate period for the call rate. Default is 1 second and default unit is milliseconds. This allows you to have n calls every m milliseconds (by using -r n -rp m).\n" "Example: -r 7 -rp 2000 ==> 7 calls every 2 seconds.\n -r 10 -rp 5s => 10 calls every 5 seconds.", SIPP_OPTION_TIME_MS, &rate_period_ms, 1}, {"rate_scale", "Control the units for the '+', '-', '*', and '/' keys.", SIPP_OPTION_FLOAT, &rate_scale, 1}, {"rate_increase", "Specify the rate increase every -rate_interval units (default is seconds). This allows you to increase the load for each independent logging period.\n" "Example: -rate_increase 10 -rate_interval 10s\n" " ==> increase calls by 10 every 10 seconds.", SIPP_OPTION_INT, &rate_increase, 1}, {"rate_max", "If -rate_increase is set, then quit after the rate reaches this value.\n" "Example: -rate_increase 10 -rate_max 100\n" " ==> increase calls by 10 until 100 cps is hit.", SIPP_OPTION_INT, &rate_max, 1}, {"rate_interval", "Set the interval by which the call rate is increased. Defaults to the value of -fd.", SIPP_OPTION_TIME_SEC, &rate_increase_freq, 1}, {"no_rate_quit", "If -rate_increase is set, do not quit after the rate reaches -rate_max.", SIPP_OPTION_UNSETFLAG, &rate_quit, 1}, {"l", "Set the maximum number of simultaneous calls. Once this limit is reached, traffic is decreased until the number of open calls goes down. Default:\n" " (3 * call_duration (s) * rate).", SIPP_OPTION_LIMIT, NULL, 1}, {"m", "Stop the test and exit when 'calls' calls are processed", SIPP_OPTION_LONG, &stop_after, 1}, {"users", "Instead of starting calls at a fixed rate, begin 'users' calls at startup, and keep the number of calls constant.", SIPP_OPTION_USERS, NULL, 1}, {"", "Retransmission and timeout options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"recv_timeout", "Global receive timeout. Default unit is milliseconds. If the expected message is not received, the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_recv_timeout, 1}, {"send_timeout", "Global send timeout. Default unit is milliseconds. If a message is not sent (due to congestion), the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_send_timeout, 1}, {"timeout", "Global timeout. Default unit is seconds. If this option is set, SIPp quits after nb units (-timeout 20s quits after 20 seconds).", SIPP_OPTION_TIME_SEC, &global_timeout, 1}, {"timeout_error", "SIPp fails if the global timeout is reached is set (-timeout option required).", SIPP_OPTION_SETFLAG, &timeout_error, 1}, {"max_retrans", "Maximum number of UDP retransmissions before call ends on timeout. Default is 5 for INVITE transactions and 7 for others.", SIPP_OPTION_INT, &max_udp_retrans, 1}, {"max_invite_retrans", "Maximum number of UDP retransmissions for invite transactions before call ends on timeout.", SIPP_OPTION_INT, &max_invite_retrans, 1}, {"max_non_invite_retrans", "Maximum number of UDP retransmissions for non-invite transactions before call ends on timeout.", SIPP_OPTION_INT, &max_non_invite_retrans, 1}, {"nr", "Disable retransmission in UDP mode.", SIPP_OPTION_UNSETFLAG, &retrans_enabled, 1}, {"rtcheck", "Select the retransmission detection method: full (default) or loose.", SIPP_OPTION_RTCHECK, &rtcheck, 1}, {"T2", "Global T2-timer in milli seconds", SIPP_OPTION_TIME_MS, &global_t2, 1}, {"", "Third-party call control options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"3pcc", "Launch the tool in 3pcc mode (\"Third Party call control\"). The passed IP address depends on the 3PCC role.\n" "- When the first twin command is 'sendCmd' then this is the address of the remote twin socket. SIPp will try to connect to this address:port to send the twin command (This instance must be started after all other 3PCC scenarios).\n" " Example: 3PCC-C-A scenario.\n" "- When the first twin command is 'recvCmd' then this is the address of the local twin socket. SIPp will open this address:port to listen for twin command.\n" " Example: 3PCC-C-B scenario.", SIPP_OPTION_3PCC, NULL, 1}, {"master","3pcc extended mode: indicates the master number", SIPP_OPTION_3PCC_EXTENDED, &master_name, 1}, {"slave", "3pcc extended mode: indicates the slave number", SIPP_OPTION_3PCC_EXTENDED, &slave_number, 1}, {"slave_cfg", "3pcc extended mode: indicates the file where the master and slave addresses are stored", SIPP_OPTION_SLAVE_CFG, NULL, 1}, {"", "Performance and watchdog options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"timer_resol", "Set the timer resolution. Default unit is milliseconds. This option has an impact on timers precision." "Small values allow more precise scheduling but impacts CPU usage." "If the compression is on, the value is set to 50ms. The default value is 10ms.", SIPP_OPTION_TIME_MS, &timer_resolution, 1}, {"max_recv_loops", "Set the maximum number of messages received read per cycle. Increase this value for high traffic level. The default value is 1000.", SIPP_OPTION_INT, &max_recv_loops, 1}, {"max_sched_loops", "Set the maximum number of calls run per event loop. Increase this value for high traffic level. The default value is 1000.", SIPP_OPTION_INT, &max_sched_loops, 1}, {"watchdog_interval", "Set gap between watchdog timer firings. Default is 400.", SIPP_OPTION_TIME_MS, &watchdog_interval, 1}, {"watchdog_reset", "If the watchdog timer has not fired in more than this time period, then reset the max triggers counters. Default is 10 minutes.", SIPP_OPTION_TIME_MS, &watchdog_reset, 1}, {"watchdog_minor_threshold", "If it has been longer than this period between watchdog executions count a minor trip. Default is 500.", SIPP_OPTION_TIME_MS, &watchdog_minor_threshold, 1}, {"watchdog_major_threshold", "If it has been longer than this period between watchdog executions count a major trip. Default is 3000.", SIPP_OPTION_TIME_MS, &watchdog_major_threshold, 1}, {"watchdog_major_maxtriggers", "How many times the major watchdog timer can be tripped before the test is terminated. Default is 10.", SIPP_OPTION_INT, &watchdog_major_maxtriggers, 1}, {"watchdog_minor_maxtriggers", "How many times the minor watchdog timer can be tripped before the test is terminated. Default is 120.", SIPP_OPTION_INT, &watchdog_minor_maxtriggers, 1}, {"", "Tracing, logging and statistics options:", SIPP_HELP_TEXT_HEADER, NULL, 0}, {"f", "Set the statistics report frequency on screen. Default is 1 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq, 1}, {"trace_stat", "Dumps all statistics in _.csv file. Use the '-h stat' option for a detailed description of the statistics file content.", SIPP_OPTION_SETFLAG, &dumpInFile, 1}, {"stat_delimiter", "Set the delimiter for the statistics file", SIPP_OPTION_STRING, &stat_delimiter, 1}, {"stf", "Set the file name to use to dump statistics", SIPP_OPTION_ARGI, &argiFileName, 1}, {"fd", "Set the statistics dump log report frequency. Default is 60 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq_dumpLog, 1}, {"periodic_rtd", "Reset response time partition counters each logging interval.", SIPP_OPTION_SETFLAG, &periodic_rtd, 1}, {"trace_msg", "Displays sent and received SIP messages in __messages.log", SIPP_OPTION_SETFLAG, &useMessagef, 1}, {"message_file", "Set the name of the message log file.", SIPP_OPTION_LFNAME, &message_lfi, 1}, {"message_overwrite", "Overwrite the message log file (default true).", SIPP_OPTION_LFOVERWRITE, &message_lfi, 1}, {"trace_shortmsg", "Displays sent and received SIP messages as CSV in __shortmessages.log", SIPP_OPTION_SETFLAG, &useShortMessagef, 1}, {"shortmessage_file", "Set the name of the short message log file.", SIPP_OPTION_LFNAME, &shortmessage_lfi, 1}, {"shortmessage_overwrite", "Overwrite the short message log file (default true).", SIPP_OPTION_LFOVERWRITE, &shortmessage_lfi, 1}, {"trace_counts", "Dumps individual message counts in a CSV file.", SIPP_OPTION_SETFLAG, &useCountf, 1}, {"trace_err", "Trace all unexpected messages in __errors.log.", SIPP_OPTION_SETFLAG, &print_all_responses, 1}, {"error_file", "Set the name of the error log file.", SIPP_OPTION_LFNAME, &error_lfi, 1}, {"error_overwrite", "Overwrite the error log file (default true).", SIPP_OPTION_LFOVERWRITE, &error_lfi, 1}, {"trace_error_codes", "Dumps the SIP response codes of unexpected messages to __error_codes.log.", SIPP_OPTION_SETFLAG, &useErrorCodesf, 1}, // {"trace_timeout", "Displays call ids for calls with timeouts in __timeout.log", SIPP_OPTION_SETFLAG, &useTimeoutf, 1}, {"trace_calldebug", "Dumps debugging information about aborted calls to __calldebug.log file.", SIPP_OPTION_SETFLAG, &useCallDebugf, 1}, {"calldebug_file", "Set the name of the call debug file.", SIPP_OPTION_LFNAME, &calldebug_lfi, 1}, {"calldebug_overwrite", "Overwrite the call debug file (default true).", SIPP_OPTION_LFOVERWRITE, &calldebug_lfi, 1}, {"trace_screen", "Dump statistic screens in the __screens.log file when quitting SIPp. Useful to get a final status report in background mode (-bg option).", SIPP_OPTION_SETFLAG, &useScreenf, 1}, {"screen_file", "Set the name of the screen file.", SIPP_OPTION_LFNAME, &screen_lfi, 1}, {"screen_overwrite", "Overwrite the screen file (default true).", SIPP_OPTION_LFOVERWRITE, &screen_lfi, 1}, {"trace_rtt", "Allow tracing of all response times in __rtt.csv.", SIPP_OPTION_SETFLAG, &dumpInRtt, 1}, {"rtt_freq", "freq is mandatory. Dump response times every freq calls in the log file defined by -trace_rtt. Default value is 200.", SIPP_OPTION_LONG, &report_freq_dumpRtt, 1}, {"trace_logs", "Allow tracing of actions in __logs.log.", SIPP_OPTION_SETFLAG, &useLogf, 1}, {"log_file", "Set the name of the log actions log file.", SIPP_OPTION_LFNAME, &log_lfi, 1}, {"log_overwrite", "Overwrite the log actions log file (default true).", SIPP_OPTION_LFOVERWRITE, &log_lfi, 1}, {"ringbuffer_files", "How many error, message, shortmessage and calldebug files should be kept after rotation?", SIPP_OPTION_INT, &ringbuffer_files, 1}, {"ringbuffer_size", "How large should error, message, shortmessage and calldebug files be before they get rotated?", SIPP_OPTION_LONG_LONG, &ringbuffer_size, 1}, {"max_log_size", "What is the limit for error, message, shortmessage and calldebug file sizes.", SIPP_OPTION_LONG_LONG, &max_log_size, 1}, }; static struct sipp_option *find_option(const char* option) { int i; int max = sizeof(options_table)/sizeof(options_table[0]); /* Allow options to start with '-' or '--' */ if (option[0] != '-') { return NULL; } option++; if (option[0] == '-') { option++; } for (i = 0; i < max; i++) { if (!strcmp(options_table[i].option, option)) { return &(options_table[i]); } } return NULL; } /******************** Recv Poll Processing *********************/ extern unsigned pollnfds; #ifdef HAVE_EPOLL extern int epollfd; extern struct epoll_event* epollevents; #endif extern SIPpSocket *sockets[SIPP_MAXFDS]; /************** Statistics display & User control *************/ static void sipp_sigusr1(int /* not used */) { /* Smooth exit: do not place any new calls and exit */ quitting+=10; } static void sipp_sigusr2(int /* not used */) { if (!signalDump) { signalDump = true; } } void timeout_alarm(int /*param*/) { if (timeout_error) { ERROR("%s timed out after '%.3lf' seconds", scenario_file, ((double)clock_tick / 1000LL)); } quitting = 1; timeout_exit = true; } /* Send loop & trafic generation*/ static void traffic_thread() { /* create the file */ char L_file_name[MAX_PATH]; sprintf(L_file_name, "%s_%ld_screen.log", scenario_file, (long) getpid()); getmilliseconds(); /* Arm the global timer if needed */ if (global_timeout > 0) { signal(SIGALRM, timeout_alarm); alarm(global_timeout / 1000); } // Dump (to create file on disk) and showing screen at the beginning even if // the report period is not reached stattask::report(); screentask::report(false); while (1) { scheduling_loops++; getmilliseconds(); if (signalDump) { /* Screen dumping in a file */ if (useScreenf == 1) { print_screens(); } else { /* If the -trace_screen option has not been set, */ /* create the file at this occasion */ rotate_screenf(); print_screens(); } if (dumpInRtt) { main_scenario->stats->dumpDataRtt(); } signalDump = false; } while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { (*(sockets_pending_reset.begin()))->reset_connection(); sockets_pending_reset.erase(sockets_pending_reset.begin()); } if ((main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated)) >= stop_after) { quitting = 1; } if (quitting) { if (quitting > 11) { /* Force exit: abort all calls */ abort_all_tasks(); } /* Quitting and no more opened calls, close all */ if (!main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)) { /* We can have calls that do not count towards our open-call count (e.g., dead calls). */ abort_all_tasks(); #ifdef RTP_STREAM rtpstream_shutdown(); #endif /* Reverse order shutdown, because deleting reorders the * sockets list. */ for (int i = pollnfds - 1; i >= 0; --i) { sockets[i]->close(); if (sockets[i] == ctrl_socket) { ctrl_socket = NULL; } else if (sockets[i] == stdin_socket) { stdin_socket = NULL; } } screentask::report(true); stattask::report(); if (useScreenf == 1) { print_screens(); } return; } } getmilliseconds(); /* Schedule all pending calls and process their timers */ task_list *running_tasks; if ((clock_tick - last_timer_cycle) > timer_resolution) { /* Just for the count. */ running_tasks = get_running_tasks(); last_running_calls = running_tasks->size(); /* If we have expired paused calls, move them to the run queue. */ last_woken_calls += expire_paused_tasks(); last_paused_calls = paused_tasks_count(); last_timer_cycle = clock_tick; } /* We should never get so busy with running calls that we can't process some messages. */ int loops = max_sched_loops; /* Now we process calls that are on the run queue. */ running_tasks = get_running_tasks(); /* Workaround hpux problem with iterators. Deleting the * current object when iterating breaks the iterator and * leads to iterate again on the destroyed (deleted) * object. Thus, we have to wait ont step befere actual * deletion of the object*/ task * last = NULL; task_list::iterator iter; for (iter = running_tasks->begin(); iter != running_tasks->end(); iter++) { if (last) { last->run(); if (sockets_pending_reset.begin() != sockets_pending_reset.end()) { last = NULL; break; } } last = *iter; if (--loops <= 0) { break; } } if (last) { last->run(); } while (sockets_pending_reset.begin() != sockets_pending_reset.end()) { (*(sockets_pending_reset.begin()))->reset_connection(); sockets_pending_reset.erase(sockets_pending_reset.begin()); } /* Update the clock. */ getmilliseconds(); /* Receive incoming messages */ SIPpSocket::pollset_process(running_tasks->empty()); } assert(0); } /*************** RTP ECHO THREAD ***********************/ /* param is a pointer to RTP socket */ static void rtp_echo_thread(void* param) { char* msg = (char*)alloca(media_bufsize); size_t nr, ns; sipp_socklen_t len; struct sockaddr_storage remote_rtp_addr; int rc; sigset_t mask; sigfillset(&mask); /* Mask all allowed signals */ rc = pthread_sigmask(SIG_BLOCK, &mask, NULL); if (rc) { WARNING("pthread_sigmask returned %d", rc); return; } for (;;) { len = sizeof(remote_rtp_addr); nr = recvfrom(*(int*)param, msg, media_bufsize, 0, (sockaddr*)&remote_rtp_addr, &len); if (((long)nr) < 0) { WARNING("%s %i", "Error on RTP echo reception - stopping echo - errno=", errno); return; } #ifdef RTP_STREAM if (!rtp_echo_state) { continue; } #endif ns = sendto(*(int*)param, msg, nr, 0, (sockaddr*)&remote_rtp_addr, len); if (ns != nr) { WARNING("%s %i", "Error on RTP echo transmission - stopping echo - errno=", errno); return; } if (*(int*)param == media_socket_audio) { rtp_pckts++; rtp_bytes += ns; } else { /* packets on the second RTP stream */ rtp2_pckts++; rtp2_bytes += ns; } } } /* Wrap the help text. */ static char* wrap(const char* in, int offset, int size) { int pos = 0; int i, j; int l = strlen(in); int alloced = l + 1; char* out = (char*)malloc(alloced); int indent = 0; if (!out) { ERROR_NO("malloc"); } for (i = j = 0; i < l; i++) { out[j++] = in[i]; if (in[i] == '\n') { out = (char*)realloc(out, alloced += offset); if (!out) { ERROR_NO("realloc"); } pos = 0; for (int k = 0; k < offset; k++) { out[j++] = ' '; } if (indent) { indent = 0; } } if (in[i] == '-' && i > 0 && in[i - 1] == '\n') { indent = 1; } if (++pos > size) { int k; for (k = j - 1; k > 0 && !isspace(out[k]); k--); int useoffset = offset; if (indent) { useoffset += 2; } if (k == 0 || out[k] == '\n') { pos = 0; out[j++] = '\n'; out = (char*)realloc(out, alloced += useoffset); if (!out) { ERROR_NO("realloc"); } for (k = 0; k < useoffset; k++) { out[j++] = ' '; } } else { int m; int move_back = 0; out[k] = '\n'; pos = j - k; // move_back is used to step back in the in and out buffers when a // word is longer than useoffset. if (i > (k + useoffset)) { move_back = i - (k + useoffset); i -= move_back; } k++; out = (char*)realloc(out, alloced += useoffset); if (!out) { ERROR_NO("realloc"); } for (m = 0; m < useoffset; m++) { if (k + useoffset + m < alloced) { out[k + useoffset + m] = out[k + m]; } out[k + m] = ' '; } j += useoffset - move_back; } } } out[j] = '\0'; return out; } /* If stdout is a TTY, wrap stdout in a call to PAGER (generally less(1)). * Returns a pid_t you'll have to pass to end_pager(). */ static pid_t begin_pager() { char pager[15] = "/usr/bin/pager"; char *argv[2] = {NULL, NULL}; int stdout_fd = fileno(stdout); int read_write[2]; pid_t ret; if (!isatty(stdout_fd)) { return 0; } /* Get pager first, so we can bail if it's not there */ argv[0] = getenv("PAGER"); if (!argv[0]) { argv[0] = pager; /* missing PAGER */ } else if (!*argv[0]) { return 0; /* blank PAGER */ } /* Should use euidaccess(3), but requires _GNU_SOURCE */ if (access(argv[0], X_OK) < 0) { perror(argv[0]); return 0; } /* Set up pipes and fork */ if (pipe(&read_write[0]) < 0) { perror("pipe"); return 0; } if ((ret = fork()) < 0) { perror("fork"); return 0; } /* Switch stdout FD in parent */ if (ret != 0) { fflush(stdout); close(stdout_fd); close(read_write[0]); if (dup2(read_write[1], stdout_fd) < 0) { perror("dup2"); } else { close(read_write[1]); } return ret; } /* Switch stdin FD and start pager in child */ if (setenv("LESS", "FRX", 1) < 0) { perror("setenv"); } close(STDIN_FILENO); close(read_write[1]); if (dup2(read_write[0], STDIN_FILENO) < 0) { perror("dup2"); } else { close(read_write[0]); } execve(argv[0], argv, environ); /* This was not supposed to happen. Missing binary? */ perror("execve"); return 0; } /* Make sure we flush and close, or the child won't get all the data (and know * when we're done). Wait for the child to exit first. */ void end_pager(pid_t pager) { fflush(stdout); fclose(stdout); while (pager != 0) { int wstatus; if (waitpid(pager, &wstatus, 0) == pager) { pager = 0; } } } /* Help screen */ static void help() { int i, max; pid_t pager = begin_pager(); printf ("\n" "Usage:\n" "\n" " sipp remote_host[:remote_port] [options]\n" "\n" "Example:\n" "\n" " Run SIPp with embedded server (uas) scenario:\n" " ./sipp -sn uas\n" " On the same host, run SIPp with embedded client (uac) scenario:\n" " ./sipp -sn uac 127.0.0.1\n" "\n" " Available options:\n" "\n"); /* We automatically generate the help messages based on the options array. * This should hopefully encourage people to write help text when they * introduce a new option and keep the code a bit cleaner. */ max = sizeof(options_table) / sizeof(options_table[0]); for (i = 0; i < max; i++) { char *formatted; if (!options_table[i].help) { continue; } formatted = wrap(options_table[i].help, 22, 77); if (options_table[i].type == SIPP_HELP_TEXT_HEADER) { printf("\n*** %s\n\n", formatted); } else { printf(" -%-16s: %s\n", options_table[i].option, formatted); } free(formatted); } printf ( "\n\nSignal handling:\n" "\n" " SIPp can be controlled using POSIX signals. The following signals\n" " are handled:\n" " USR1: Similar to pressing the 'q' key. It triggers a soft exit\n" " of SIPp. No more new calls are placed and all ongoing calls\n" " are finished before SIPp exits.\n" " Example: kill -SIGUSR1 732\n" " USR2: Triggers a dump of all statistics screens in\n" " __screens.log file. Especially useful \n" " in background mode to know what the current status is.\n" " Example: kill -SIGUSR2 732\n" "\n" "Exit codes:\n" "\n" " Upon exit (on fatal error or when the number of asked calls (-m\n" " option) is reached, SIPp exits with one of the following exit\n" " code:\n" " 0: All calls were successful\n" " 1: At least one call failed\n" " 97: Exit on internal command. Calls may have been processed\n" " 99: Normal exit without calls processed\n" " -1: Fatal error\n" " -2: Fatal error binding a socket\n"); end_pager(pager); } static void help_stats() { printf( "\n" " The -trace_stat option dumps all statistics in the\n" " file. The dump starts with one header\n" " line with all counters. All following lines are 'snapshots' of \n" " statistics counter given the statistics report frequency\n" " (-fd option). This file can be easily imported in any\n" " spreadsheet application, like Excel.\n" "\n" " In counter names, (P) means 'Periodic' - since last\n" " statistic row and (C) means 'Cumulative' - since SIPp was\n" " started.\n" "\n" " Available statistics are:\n" "\n" " - StartTime: \n" " Date and time when the test has started.\n" "\n" " - LastResetTime:\n" " Date and time when periodic counters were last reset.\n" "\n" " - CurrentTime:\n" " Date and time of the statistic row.\n" "\n" " - ElapsedTime:\n" " Elapsed time.\n" "\n" " - CallRate:\n" " Call rate (calls per seconds).\n" "\n" " - IncomingCall:\n" " Number of incoming calls.\n" "\n" " - OutgoingCall:\n" " Number of outgoing calls.\n" "\n" " - TotalCallCreated:\n" " Number of calls created.\n" "\n" " - CurrentCall:\n" " Number of calls currently ongoing.\n" "\n" " - SuccessfulCall:\n" " Number of successful calls.\n" "\n" " - FailedCall:\n" " Number of failed calls (all reasons).\n" "\n" " - FailedCannotSendMessage:\n" " Number of failed calls because SIPp cannot send the\n" " message (transport issue).\n" "\n" " - FailedMaxUDPRetrans:\n" " Number of failed calls because the maximum number of\n" " UDP retransmission attempts has been reached.\n" "\n" " - FailedUnexpectedMessage:\n" " Number of failed calls because the SIP message received\n" " is not expected in the scenario.\n" "\n" " - FailedCallRejected:\n" " Number of failed calls because of SIPp internal error.\n" " (a scenario sync command is not recognized, a scenario\n" " action failed or a scenario variable assignment failed).\n" "\n" " - FailedCmdNotSent:\n" " Number of failed calls because of inter-SIPp\n" " communication error (a scenario sync command failed to\n" " be sent).\n" "\n" " - FailedRegexpDoesntMatch:\n" " Number of failed calls because of regexp that doesn't\n" " match (there might be several regexp that don't match\n" " during the call but the counter is increased only by\n" " one).\n" "\n" " - FailedRegexpShouldntMatch:\n" " Number of failed calls because of regexp that shouldn't\n" " match but does (there might be several regexp that shouldn't match\n" " during the call but the counter is increased only by\n" " one).\n" "\n" " - FailedRegexpHdrNotFound:\n" " Number of failed calls because of regexp with 'hdr'\n" " option but no matching header found.\n" "\n" " - OutOfCallMsgs:\n" " Number of SIP messages received that cannot be associated\n" " to an existing call.\n" "\n" " - AutoAnswered:\n" " Number of unexpected specific messages received for new Call-ID.\n" " The message has been automatically answered by a 200 OK\n" " Currently, implemented for 'NOTIFY', 'INFO' and 'PING' messages.\n" "\n"); } /************* exit handler *****************/ static void print_last_stats() { interrupt = 1; if (sp) { sp->print_closing_stats(); } if (main_scenario) { stattask::report(); } } static void stop_all_traces() { message_lfi.fptr = NULL; log_lfi.fptr = NULL; dumpInRtt = 0; dumpInFile = 0; } static void freeInFiles() { for (file_map::iterator file_it = inFiles.begin(); file_it != inFiles.end(); file_it++) { delete file_it->second; } } static void freeUserVarMap() { for (int_vt_map::iterator vt_it = userVarMap.begin(); vt_it != userVarMap.end(); vt_it++) { vt_it->second->putTable(); userVarMap[vt_it->first] = NULL; } } static void manage_oversized_file(int signum) { FILE *f; char L_file_name[MAX_PATH]; struct timeval currentTime; static int managing = 0; // we can receive this signal more than once if (managing) { return; } managing = 1; snprintf(L_file_name, MAX_PATH, "%s_%ld_traces_oversized.log", scenario_file, (long) getpid()); f = fopen(L_file_name, "w"); if (!f) { ERROR_NO("Unable to open oversized log file"); } GET_TIME(¤tTime); fprintf(f, "-------------------------------------------- %s\n" "Max file size reached - no more logs\n", CStat::formatTime(¤tTime)); fflush(f); stop_all_traces(); print_all_responses = 0; error_lfi.fptr = NULL; } static void releaseGlobalAllocations() { delete main_scenario; delete ooc_scenario; delete aa_scenario; free_default_messages(); freeInFiles(); freeUserVarMap(); delete userVariables; delete globalVariables; } void sipp_exit(int rc) { unsigned long counter_value_failed = 0; unsigned long counter_value_success = 0; /* Some signals may be delivered twice during exit() execution, and we must prevent all this from beeing done twice */ { static int already_exited = 0; if (already_exited) { return; } already_exited = 1; } screen_exit(); print_last_stats(); print_errors(); /* Close open files. */ struct logfile_info** logfile_ptr; struct logfile_info* logfiles[] = { &screen_lfi, &calldebug_lfi, &message_lfi, &shortmessage_lfi, &log_lfi, &error_lfi, NULL}; for (logfile_ptr = logfiles; *logfile_ptr; ++logfile_ptr) { if ((*logfile_ptr)->fptr) { fclose((*logfile_ptr)->fptr); (*logfile_ptr)->fptr = NULL; } } // Get failed calls counter value before releasing objects if (display_scenario) { counter_value_failed = display_scenario->stats->GetStat(CStat::CPT_C_FailedCall); counter_value_success = display_scenario->stats->GetStat(CStat::CPT_C_SuccessfulCall); } else { rc = EXIT_TEST_FAILED; } releaseGlobalAllocations(); if (rc != EXIT_TEST_RES_UNKNOWN) { // Exit is not a normal exit. Just use the passed exit code. exit(rc); } else { // Normal exit: we need to determine if the calls were all // successful or not. In order to compute the return code, get // the counter of failed calls. If there is 0 failed calls, // then everything is OK! if (counter_value_failed == 0) { if (timeout_exit && counter_value_success < 1) { exit(EXIT_TEST_RES_INTERNAL); } else { exit(EXIT_TEST_OK); } } else { exit(EXIT_TEST_FAILED); } } } static void sipp_sighandler(int signum) { sipp_exit(EXIT_TEST_RES_UNKNOWN); } static void sighandle_set() { struct sigaction action_quit = {}; struct sigaction action_file_size_exceeded = {}; action_quit.sa_handler = sipp_sighandler; action_file_size_exceeded.sa_handler = manage_oversized_file; sigaction(SIGTERM, &action_quit, NULL); sigaction(SIGINT, &action_quit, NULL); sigaction(SIGXFSZ, &action_file_size_exceeded, NULL); // avoid core dump if the max file size is exceeded } static void set_scenario(const char* name) { free(scenario_file); free(scenario_path); const char* sep = strrchr(name, '/'); if (sep) { ++sep; // include slash scenario_path = strndup(name, sep - name); } else { scenario_path = strdup(""); sep = name; } const char* ext = strrchr(sep, '.'); if (ext && strcmp(ext, ".xml") == 0) { scenario_file = strndup(sep, ext - sep); } else { scenario_file = strdup(sep); } } /** * Create and bind media_socket_audio, media_socket_video for RTP and * RCTP on try_port and try_port+2. * * Sets: media_socket_audio and media_socket_audio. */ static int bind_rtp_sockets(struct sockaddr_storage* media_sa, int try_port, int last_attempt) { /* Create RTP sockets for audio and video. */ if ((media_socket_audio = socket(media_sa->ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { ERROR_NO("Unable to create the audio RTP socket"); } if (media_sa->ss_family == AF_INET) { (_RCAST(struct sockaddr_in*, media_sa))->sin_port = htons(try_port); } else { (_RCAST(struct sockaddr_in6*, media_sa))->sin6_port = htons(try_port); } if (::bind(media_socket_audio, (sockaddr*)media_sa, socklen_from_addr(media_sa)) != 0) { if (last_attempt) { ERROR_NO("Unable to bind audio RTP socket (IP=%s, port=%d)", media_ip, try_port); } ::close(media_socket_audio); media_socket_audio = -1; return -1; } /* Create and bind the second/video socket to try_port+2 */ /* (+1 is reserved for RTCP) */ if ((media_socket_video = socket(media_sa->ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { ERROR_NO("Unable to create the video RTP socket"); } if (media_sa->ss_family == AF_INET) { (_RCAST(struct sockaddr_in*, media_sa))->sin_port = htons(try_port + 2); } else { (_RCAST(struct sockaddr_in6*, media_sa))->sin6_port = htons(try_port + 2); } if (::bind(media_socket_video, (sockaddr*)media_sa, socklen_from_addr(media_sa)) != 0) { if (last_attempt) { ERROR_NO("Unable to bind video RTP socket (IP=%s, port=%d)", media_ip, try_port + 2); } ::close(media_socket_audio); ::close(media_socket_video); media_socket_audio = media_socket_video = -1; return -1; } return 0; } /** * Set a bunch of globals and bind audio and video rtp sockets. * * Sets: media_ip, media_port, media_ip_is_ipv6, media_socket_audio, * media_socket_video. */ static void setup_media_sockets() { struct addrinfo hints = {0,}; struct addrinfo* local_addr; struct sockaddr_storage media_sockaddr = {0,}; hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; /* use local_ip_is_ipv6 as hint? */ /* Defaults for media sockets */ if (media_ip[0] == '\0') { strcpy(media_ip, local_ip); } // assert that an IPv6 'media_ip' is not surrounded by brackets? /* Resolving local IP */ if (getaddrinfo(media_ip, NULL, &hints, &local_addr) != 0) { ERROR("Unknown RTP address '%s'.\n" "Use 'sipp -h' for details", media_ip); } memcpy(&media_sockaddr, local_addr->ai_addr, socklen_from_addr(_RCAST(struct sockaddr_storage*, local_addr->ai_addr))); freeaddrinfo(local_addr); media_ip_is_ipv6 = (media_sockaddr.ss_family == AF_INET6); int try_counter; int max_tries = user_media_port ? 1 : 100; media_port = user_media_port ? user_media_port : DEFAULT_MEDIA_PORT; media_socket_audio = media_socket_video = -1; for (try_counter = 1; try_counter <= max_tries; try_counter++) { int last_attempt = (try_counter == max_tries); if (bind_rtp_sockets(&media_sockaddr, media_port, last_attempt) == 0) { break; } // Old RFC 3551 says: // > RTP data SHOULD be carried on an even UDP port number and // > the corresponding RTCP packets SHOULD be carried on the // > next higher (odd) port number. // So, try only even numbers. media_port += 2; } } /* Main */ int main(int argc, char *argv[]) { int argi = 0; pthread_t pthread2_id = 0, pthread3_id = 0; unsigned int generic_count = 0; bool slave_masterSet = false; generic[0] = NULL; /* At least one argument is needed */ if (argc < 2) { help(); exit(EXIT_OTHER); } { /* Ignore the SIGPIPE signal */ struct sigaction action_pipe; memset(&action_pipe, 0, sizeof(action_pipe)); action_pipe.sa_handler=SIG_IGN; sigaction(SIGPIPE, &action_pipe, NULL); /* The Window Size change Signal is also useless, and causes failures. */ #ifdef SIGWINCH sigaction(SIGWINCH, &action_pipe, NULL); #endif /* sig usr1 management */ struct sigaction action_usr1; memset(&action_usr1, 0, sizeof(action_usr1)); action_usr1.sa_handler = sipp_sigusr1; sigaction(SIGUSR1, &action_usr1, NULL); /* sig usr2 management */ struct sigaction action_usr2; memset(&action_usr2, 0, sizeof(action_usr2)); action_usr2.sa_handler = sipp_sigusr2; sigaction(SIGUSR2, &action_usr2, NULL); } pid = getpid(); memset(local_ip, 0, sizeof(local_ip)); #ifdef USE_SCTP memset(multihome_ip, 0, sizeof(multihome_ip)); #endif memset(media_ip, 0, sizeof(media_ip)); memset(control_ip, 0, sizeof(control_ip)); /* Initialize our global variable structure. */ globalVariables = new AllocVariableTable(NULL); userVariables = new AllocVariableTable(globalVariables); /* Command line parsing */ #define REQUIRE_ARG() if ((++argi) >= argc) { \ ERROR("Missing argument for param '%s'.\nUse 'sipp -h' for details", argv[argi - 1]); } #define CHECK_PASS() if (option->pass != pass) { break; } for (int pass = 0; pass <= 3; pass++) { for(argi = 1; argi < argc; argi++) { struct sipp_option *option = find_option(argv[argi]); if (!option) { if (argv[argi][0] != '-') { strncpy(remote_host, argv[argi], sizeof(remote_host) - 1); continue; } help(); ERROR("Invalid argument: '%s'.\n" "Use 'sipp -h' for details", argv[argi]); } switch(option->type) { case SIPP_OPTION_HELP: if (argi + 1 < argc && !strcmp(argv[argi + 1], "stat")) { help_stats(); } else { help(); } exit(EXIT_OTHER); case SIPP_OPTION_VERSION: printf("\n %s.\n\n", /* SIPp v1.2.3-TLS-PCAP */ "SIPp " SIPP_VERSION #ifdef USE_TLS "-TLS" #endif #ifdef USE_SCTP "-SCTP" #endif #ifdef PCAPPLAY "-PCAP" #endif #ifdef RTP_STREAM "-RTPSTREAM" #endif ); printf (" This program is free software; you can redistribute it and/or\n" " modify it under the terms of the GNU General Public License as\n" " published by the Free Software Foundation; either version 2 of\n" " the License, or (at your option) any later version.\n" "\n" " This program is distributed in the hope that it will be useful,\n" " but WITHOUT ANY WARRANTY; without even the implied warranty of\n" " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" " GNU General Public License for more details.\n" "\n" " You should have received a copy of the GNU General Public\n" " License along with this program; if not, write to the\n" " Free Software Foundation, Inc.,\n" " 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" "\n" " Author: see source files.\n\n"); exit(EXIT_OTHER); case SIPP_OPTION_INT: REQUIRE_ARG(); CHECK_PASS(); *((int*)option->data) = get_long(argv[argi], argv[argi-1]); break; case SIPP_OPTION_LONG: REQUIRE_ARG(); CHECK_PASS(); *((long*)option->data) = get_long(argv[argi], argv[argi-1]); break; case SIPP_OPTION_LONG_LONG: REQUIRE_ARG(); CHECK_PASS(); *((unsigned long long*)option->data) = get_long_long(argv[argi], argv[argi-1]); break; case SIPP_OPTION_TIME_SEC: REQUIRE_ARG(); CHECK_PASS(); *((long*)option->data) = get_time(argv[argi], argv[argi-1], 1000); break; case SIPP_OPTION_TIME_MS: REQUIRE_ARG(); CHECK_PASS(); *((int*)option->data) = get_time(argv[argi], argv[argi-1], 1); break; case SIPP_OPTION_TIME_MS_LONG: REQUIRE_ARG(); CHECK_PASS(); *((long*)option->data) = get_time(argv[argi], argv[argi-1], 1); break; case SIPP_OPTION_BOOL: REQUIRE_ARG(); CHECK_PASS(); *((bool*)option->data) = get_bool(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_FLOAT: REQUIRE_ARG(); CHECK_PASS(); *((double*)option->data) = get_double(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_STRING: REQUIRE_ARG(); CHECK_PASS(); *((char**)option->data) = argv[argi]; break; case SIPP_OPTION_ARGI: REQUIRE_ARG(); CHECK_PASS(); *((int*)option->data) = argi; break; case SIPP_OPTION_INPUT_FILE: { REQUIRE_ARG(); CHECK_PASS(); FileContents *data = new FileContents(argv[argi]); char *name = argv[argi]; if (strrchr(name, '/')) { name = strrchr(name, '/') + 1; } else if (strrchr(name, '\\')) { name = strrchr(name, '\\') + 1; } assert(name); inFiles[name] = data; /* By default, the first file is used for IP address input. */ if (!ip_file) { ip_file = name; } if (!default_file) { default_file = name; } } break; case SIPP_OPTION_INDEX_FILE: REQUIRE_ARG(); REQUIRE_ARG(); CHECK_PASS(); { char *fileName = argv[argi - 1]; char *endptr; int field; if (inFiles.find(fileName) == inFiles.end()) { ERROR("Could not find file for -infindex: %s", argv[argi - 1]); } field = strtoul(argv[argi], &endptr, 0); if (*endptr) { ERROR("Invalid field specification for -infindex: %s", argv[argi]); } inFiles[fileName]->index(field); } break; case SIPP_OPTION_SETFLAG: CHECK_PASS(); *((bool*)option->data) = true; break; case SIPP_OPTION_UNSETFLAG: CHECK_PASS(); *((bool*)option->data) = false; break; case SIPP_OPTION_TRANSPORT: REQUIRE_ARG(); CHECK_PASS(); if (strlen(argv[argi]) != 2) { ERROR("Invalid argument for -t param : '%s'.\n" "Use 'sipp -h' for details", argv[argi]); } switch(argv[argi][0]) { case 'u': transport = T_UDP; break; case 't': transport = T_TCP; break; case 's': #ifdef USE_SCTP transport = T_SCTP; #else ERROR("To use SCTP transport you must compile SIPp with lksctp"); #endif break; case 'l': #ifdef USE_TLS transport = T_TLS; if (TLS_init() != 1) { printf("OpenSSL Initialization problem\n"); exit(-1); } #else ERROR("To use a TLS transport you must compile SIPp with OpenSSL"); #endif break; case 'c': if (strlen(comp_error)) { ERROR("No " COMP_PLUGGIN " plugin available: %s", comp_error); } transport = T_UDP; compression = 1; } switch(argv[argi][1]) { case '1': multisocket = 0; peripsocket = 0; break; case 'n': multisocket = 1; peripsocket = 0; break; case 'i': multisocket = 1; peripsocket = 1; socket_close = false; break; } if (peripsocket && transport != T_UDP) { ERROR("You can only use a perip socket with UDP!"); } break; case SIPP_OPTION_NEED_SCTP: CHECK_PASS(); ERROR("SCTP support is required for the %s option.", argv[argi]); break; case SIPP_OPTION_NEED_SSL: CHECK_PASS(); ERROR("OpenSSL is required for the %s option.", argv[argi]); break; case SIPP_OPTION_MAX_SOCKET: REQUIRE_ARG(); CHECK_PASS(); max_multi_socket = get_long(argv[argi], argv[argi - 1]); maxSocketPresent = true; break; case SIPP_OPTION_CSEQ: REQUIRE_ARG(); CHECK_PASS(); base_cseq = get_long(argv[argi], argv[argi - 1]); base_cseq--; break; case SIPP_OPTION_IP: { int dummy_port; char* ptr = (char*)option->data; REQUIRE_ARG(); CHECK_PASS(); strcpy(ptr, argv[argi]); get_host_and_port(ptr, ptr, &dummy_port); } break; case SIPP_OPTION_LIMIT: REQUIRE_ARG(); CHECK_PASS(); if (users >= 0) { ERROR("Can not set open call limit (-l) when -users is specified."); } open_calls_allowed = get_long(argv[argi], argv[argi - 1]); open_calls_user_setting = 1; break; case SIPP_OPTION_USERS: REQUIRE_ARG(); CHECK_PASS(); users = open_calls_allowed = get_long(argv[argi], argv[argi - 1]); open_calls_user_setting = 1; break; case SIPP_OPTION_KEY: REQUIRE_ARG(); REQUIRE_ARG(); CHECK_PASS(); if (generic_count + 1 >= sizeof(generic)/sizeof(generic[0])) { ERROR("Too many generic parameters %d",generic_count + 1); } generic[generic_count++] = &argv[argi - 1]; generic[generic_count] = NULL; break; case SIPP_OPTION_VAR: REQUIRE_ARG(); REQUIRE_ARG(); CHECK_PASS(); { int varId = globalVariables->find(argv[argi - 1], false); if (varId == -1) { globalVariables->dump(); ERROR("Can not set the global variable %s, because it does not exist.", argv[argi - 1]); } globalVariables->getVar(varId)->setString(strdup(argv[argi])); } break; case SIPP_OPTION_3PCC: if (slave_masterSet) { ERROR("-3PCC option is not compatible with -master and -slave options"); } if (extendedTwinSippMode) { ERROR("-3pcc and -slave_cfg options are not compatible"); } REQUIRE_ARG(); CHECK_PASS(); twinSippMode = true; strcpy(twinSippHost, argv[argi]); get_host_and_port(twinSippHost, twinSippHost, &twinSippPort); break; case SIPP_OPTION_SCENARIO: REQUIRE_ARG(); CHECK_PASS(); if (main_scenario) { ERROR("Internal error, main_scenario already set"); } else if (!strcmp(argv[argi - 1], "-sf")) { set_scenario(argv[argi]); if (useLogf == 1) { rotate_logfile(); } main_scenario = new scenario(argv[argi], 0); main_scenario->stats->setFileName(scenario_file, ".csv"); } else if (!strcmp(argv[argi - 1], "-sn")) { int i = find_scenario(argv[argi]); set_scenario(argv[argi]); main_scenario = new scenario(0, i); main_scenario->stats->setFileName(scenario_file, ".csv"); } else if (!strcmp(argv[argi - 1], "-sd")) { int i = find_scenario(argv[argi]); fprintf(stdout, "%s", default_scenario[i]); exit(EXIT_OTHER); } else { ERROR("Internal error, I don't recognize %s as a scenario option", argv[argi] - 1); } break; case SIPP_OPTION_OOC_SCENARIO: REQUIRE_ARG(); CHECK_PASS(); if (!strcmp(argv[argi - 1], "-oocsf")) { ooc_scenario = new scenario(argv[argi], 0); } else if (!strcmp(argv[argi - 1], "-oocsn")) { int i = find_scenario(argv[argi]); ooc_scenario = new scenario(0, i); } else { ERROR("Internal error, I don't recognize %s as a scenario option", argv[argi] - 1); } break; case SIPP_OPTION_SLAVE_CFG: REQUIRE_ARG(); CHECK_PASS(); if (twinSippMode) { ERROR("-slave_cfg and -3pcc options are not compatible"); } extendedTwinSippMode = true; slave_cfg_file = new char [strlen(argv[argi]) + 1]; sprintf(slave_cfg_file,"%s", argv[argi]); parse_slave_cfg(); break; case SIPP_OPTION_3PCC_EXTENDED: REQUIRE_ARG(); CHECK_PASS(); if (slave_masterSet) { ERROR("-slave and -master options are not compatible"); } if (twinSippMode) { ERROR("-master and -slave options are not compatible with -3PCC option"); } *((char**)option->data) = argv[argi]; slave_masterSet = true; break; case SIPP_OPTION_RSA: { REQUIRE_ARG(); CHECK_PASS(); char *remote_s_address; int remote_s_p = DEFAULT_PORT; int temp_remote_s_p; temp_remote_s_p = 0; remote_s_address = argv[argi]; get_host_and_port(remote_s_address, remote_s_address, &temp_remote_s_p); if (temp_remote_s_p != 0) { remote_s_p = temp_remote_s_p; } printf("Resolving remote sending address %s...\n", remote_s_address); /* FIXME: add DNS SRV support using liburli? */ if (gai_getsockaddr(&remote_sending_sockaddr, remote_s_address, remote_s_p, AI_PASSIVE, AF_UNSPEC) != 0) { ERROR("Unknown remote host '%s'.\n" "Use 'sipp -h' for details", remote_s_address); } use_remote_sending_addr = 1; break; } case SIPP_OPTION_RTCHECK: REQUIRE_ARG(); CHECK_PASS(); if (!strcmp(argv[argi], "full")) { *((int*)option->data) = RTCHECK_FULL; } else if (!strcmp(argv[argi], "loose")) { *((int*)option->data) = RTCHECK_LOOSE; } else { ERROR("Unknown retransmission detection method: %s", argv[argi]); } break; case SIPP_OPTION_TDMMAP: { REQUIRE_ARG(); CHECK_PASS(); int i1, i2, i3, i4, i5, i6, i7; if (sscanf(argv[argi], "{%d-%d}{%d}{%d-%d}{%d-%d}", &i1, &i2, &i3, &i4, &i5, &i6, &i7) == 7) { use_tdmmap = true; tdm_map_a = i2 - i1; tdm_map_x = i1; tdm_map_h = i3; tdm_map_b = i5 - i4; tdm_map_y = i4; tdm_map_c = i7 - i6; tdm_map_z = i6; } else { ERROR("Parameter -tdmmap must be of form {%%d-%%d}{%%d}{%%d-%%d}{%%d-%%d}"); } break; } case SIPP_OPTION_DEFAULTS: { unsigned long *ptr = (unsigned long*)option->data; char *token; REQUIRE_ARG(); CHECK_PASS(); *ptr = 0; token = argv[argi]; while ((token = strtok(token, ","))) { if (!strcmp(token, "none")) { *ptr = 0; } else { unsigned long mask = 0; int mode = 1; char *p = token; if (token[0] == '+') { mode = 1; p++; } else if (token[0] == '-') { mode = -1; p++; } if (!strcmp(p, "all")) { mask = DEFAULT_BEHAVIOR_ALL; } else if (!strcmp(p, "bye")) { mask = DEFAULT_BEHAVIOR_BYE; } else if (!strcmp(p, "abortunexp")) { mask = DEFAULT_BEHAVIOR_ABORTUNEXP; } else if (!strcmp(p, "pingreply")) { mask = DEFAULT_BEHAVIOR_PINGREPLY; } else { ERROR("Unknown default behavior: '%s'", token); } switch(mode) { case 0: *ptr = mask; break; case 1: *ptr |= mask; break; case -1: *ptr &= ~mask; break; default: assert(0); } } token = NULL; } break; } case SIPP_OPTION_LFNAME: REQUIRE_ARG(); CHECK_PASS(); ((struct logfile_info*)option->data)->fixedname = true; strcpy(((struct logfile_info*)option->data)->file_name, argv[argi]); break; case SIPP_OPTION_LFOVERWRITE: REQUIRE_ARG(); CHECK_PASS(); ((struct logfile_info*)option->data)->fixedname = true; ((struct logfile_info*)option->data)->overwrite = get_bool(argv[argi], argv[argi - 1]); break; case SIPP_OPTION_PLUGIN: { int ret; REQUIRE_ARG(); CHECK_PASS(); void* handle = dlopen(argv[argi], RTLD_NOW); if (!handle) { ERROR("Could not open plugin %s: %s", argv[argi], dlerror()); } int (*init)(); void* funcptr = dlsym(handle, "init"); /* http://stackoverflow.com/questions/1096341/function-pointers-casting-in-c */ *reinterpret_cast(&init) = funcptr; // yuck const char* error; if ((error = dlerror())) { ERROR("Could not locate init function in %s: %s", argv[argi], error); } ret = init(); if (ret != 0) { ERROR("Plugin %s initialization failed.", argv[argi]); } } break; default: ERROR("Internal error: I don't recognize the option type for %s", argv[argi]); } } } /* Load compression plugin if needed/available. */ if (compression) { comp_load(); } if ((extendedTwinSippMode && !slave_masterSet) || (!extendedTwinSippMode && slave_masterSet)) { ERROR("-slave_cfg option must be used with -slave or -master option"); } if (peripsocket) { if (!ip_file) { ERROR("You must use the -inf option when using -t ui.\n" "Use 'sipp -h' for details"); } } if (ringbuffer_size && max_log_size) { ERROR("Ring Buffer options and maximum log size are mutually exclusive."); } if (global_lost) { lose_packets = 1; } /* If no scenario was selected, choose the uac one */ if (scenario_file == NULL) { assert(main_scenario == NULL); int i = find_scenario("uac"); set_scenario("uac"); main_scenario = new scenario(0, i); main_scenario->stats->setFileName(scenario_file, ".csv"); } #ifdef USE_TLS if ((transport == T_TLS) && (TLS_init_context() != TLS_INIT_NORMAL)) { ERROR("FI_init_ssl_context() failed"); } #endif if (useMessagef == 1) { rotate_messagef(); } if (useShortMessagef == 1) { rotate_shortmessagef(); } if (useCallDebugf) { rotate_calldebugf(); } if (useScreenf == 1) { rotate_screenf(); } // TODO: finish the -trace_timeout option implementation /* if (useTimeoutf == 1) { char L_file_name [MAX_PATH]; sprintf(L_file_name, "%s_%d_timeout.log", scenario_file, getpid()); timeoutf = fopen(L_file_name, "w"); if (!timeoutf) { ERROR("Unable to create '%s'", L_file_name); } } */ if (useCountf == 1) { char L_file_name [MAX_PATH]; sprintf(L_file_name, "%s_%ld_counts.csv", scenario_file, (long) getpid()); countf = fopen(L_file_name, "w"); if (!countf) { ERROR("Unable to create '%s'", L_file_name); } print_count_file(countf, 1); } if (useErrorCodesf == 1) { char L_file_name [MAX_PATH]; sprintf(L_file_name, "%s_%ld_error_codes.csv", scenario_file, (long) getpid()); codesf = fopen(L_file_name, "w"); if (!codesf) { ERROR("Unable to create '%s'", L_file_name); } } if (dumpInRtt == 1) { main_scenario->stats->initRtt(scenario_file, ".csv", report_freq_dumpRtt); } if (rate_increase_freq == 0) { rate_increase_freq = report_freq_dumpLog; } // Check the soft limit on the number of open files, // error out if this does not allow us to open the // required number of signalling channels, and warn // if this may not allow enough media channels. if (!skip_rlimit) { struct rlimit rlimit; unsigned max_sockets_needed = multisocket ? max_multi_socket : 1; if (getrlimit (RLIMIT_NOFILE, &rlimit) < 0) { ERROR_NO("getrlimit error"); } if (max_sockets_needed > rlimit.rlim_cur) { ERROR("Maximum number of open sockets (%d) should be less than the maximum number " "of open files (%lu). Tune this with the `ulimit` command or the -max_socket " "option", max_sockets_needed, (unsigned long)rlimit.rlim_cur); } if ((open_calls_allowed + max_sockets_needed) > rlimit.rlim_cur) { WARNING("Maximum number of open sockets (%d) plus number of open calls (%d) " "should be less than the maximum number of open files (%lu) to " "allow for media support. Tune this with the `ulimit` command, " "the -l option or the -max_socket option", max_sockets_needed, open_calls_allowed, (unsigned long)rlimit.rlim_cur); } } /* if (!ooc_scenario) { ooc_scenario = new scenario(0, find_scenario("ooc_default")); ooc_scenario->stats->setFileName((char*)"ooc_default", (char*)".csv"); } */ display_scenario = main_scenario; aa_scenario = new scenario(0, find_scenario("ooc_dummy")); aa_scenario->stats->setFileName("ooc_dummy", ".csv"); init_default_messages(); for (int i = 1; i <= users; i++) { freeUsers.push_back(i); userVarMap[i] = new VariableTable(userVariables); } if (argiFileName) { main_scenario->stats->setFileName(argv[argiFileName]); } // setup option form cmd line call::maxDynamicId = maxDynamicId; call::startDynamicId = startDynamicId; call::dynamicId = startDynamicId; call::stepDynamicId = stepDynamicId; /* Now Initialize the scenarios. */ main_scenario->runInit(); if (ooc_scenario) { ooc_scenario->runInit(); } /* In which mode the tool is launched ? */ main_scenario->computeSippMode(); if (ooc_scenario && sendMode == MODE_SERVER) { ERROR("SIPp cannot use out-of-call scenarios when running in server mode"); } sp = new ScreenPrinter(); if (!sp->M_headless) { screen_init(); } sighandle_set(); /* checking if we need to launch the tool in background mode */ if (backgroundMode == true) { pid_t l_pid; switch (l_pid = fork()) { case -1: // error when forking ! ERROR_NO("Forking error"); exit(EXIT_FATAL_ERROR); case 0: // child process - poursuing the execution // close all of our file descriptors { int nullfd = open("/dev/null", O_RDWR); dup2(nullfd, fileno(stdin)); dup2(nullfd, fileno(stdout)); dup2(nullfd, fileno(stderr)); close(nullfd); } break; default: // parent process - killing the parent - the child get the parent pid printf("Background mode - PID=[%ld]\n", (long) l_pid); exit(EXIT_OTHER); } } sipp_usleep(sleeptime * 1000); /* Create the statistics reporting task. */ stattask::initialize(); /* Create the screen update task. */ screentask::initialize(); /* Create the rate increase task. */ ratetask::initialize(); /* Create a watchdog task. */ if (watchdog_interval) { new watchdog(watchdog_interval, watchdog_reset, watchdog_major_threshold, watchdog_major_maxtriggers, watchdog_minor_threshold, watchdog_minor_maxtriggers); } /* Setting the rate and its dependant params (open_calls_allowed) */ /* If we are a client, then create the task to open new calls. */ if (creationMode == MODE_CLIENT) { CallGenerationTask::initialize(); CallGenerationTask::set_rate(rate); } #ifdef HAVE_EPOLL epollevents = (struct epoll_event*)malloc(sizeof(struct epoll_event) * max_recv_loops); epollfd = epoll_create(SIPP_MAXFDS); if (epollfd == -1) { ERROR_NO("Failed to open epoll FD"); } #endif open_connections(); /* Always create and Bind RTP socket */ /* to avoid ICMP errors from us. */ setup_media_sockets(); /* Creating the remote control socket thread. */ setup_ctrl_socket(); if (!nostdin) { setup_stdin_socket(); } if (rtp_echo_enabled && media_socket_audio > 0) { if (pthread_create(&pthread2_id, NULL, (void *(*)(void *))rtp_echo_thread, &media_socket_audio) == -1) { ERROR_NO("Unable to create RTP echo thread"); } } /* Creating second RTP echo thread for video. */ if (rtp_echo_enabled && media_socket_video > 0) { if (pthread_create(&pthread3_id, NULL, (void *(*)(void *)) rtp_echo_thread, &media_socket_video) == -1) { ERROR_NO("Unable to create video RTP echo thread"); } } traffic_thread(); /* Cancel and join other threads. */ if (pthread2_id) { pthread_cancel(pthread2_id); } if (pthread3_id) { pthread_cancel(pthread3_id); } if (pthread2_id) { pthread_join(pthread2_id, NULL); } if (pthread3_id) { pthread_join(pthread3_id, NULL); } #ifdef HAVE_EPOLL close(epollfd); free(epollevents); #endif free(scenario_file); free(scenario_path); sipp_exit(EXIT_TEST_RES_UNKNOWN); } sipp-3.6.1/src/xp_parser.c0000664000175000017500000003655313730472040015026 0ustar walterwalter/* * 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 * * Copyright (C) 2003 - The Authors * * Author : Richard GAYRAUD - 04 Nov 2003 * From Hewlett Packard Company. */ /* * Mini xml parser: * * WARNING 1: Only supports printable * ASCII characters in xml files. '\0' * is not a valid character. Returned string are * NULL-terminated. * * WARNING 2: Does not supports multithreading. Works * with static buffer, no memory allocation. */ /******************* Include files *********************/ #include #include #include #include "xp_parser.h" #define strstartswith(haystack, needle) \ (!strncmp(haystack, needle, sizeof(needle) - 1)) /************* Constants and Global variables ***********/ #define XP_MAX_NAME_LEN 256 #define XP_MAX_FILE_LEN 65536 #define XP_MAX_STACK_LEN 256 static char xp_file[XP_MAX_FILE_LEN + 1]; static char *xp_position[XP_MAX_STACK_LEN]; static int xp_stack = 0; static int xp_stack_invalid = 0; static char xp_history[XP_MAX_FILE_LEN + 1]; static char *xp_history_pos; #define xp_history_reset() do { \ xp_history[0] = xp_history[1] = '\0'; \ xp_history_pos = &xp_history[0]; \ } while(0) #define xp_history_push(n) do { \ strcpy(xp_history_pos + 1, n); \ xp_history_pos += strlen(n) + 1; \ /*xp_history_debug();*/ \ } while(0) #define xp_history_pop() do { \ while (xp_history_pos > xp_history && *--xp_history_pos != '\0'); \ /*xp_history_debug();*/ \ } while(0) /****************** Internal routines ********************/ static const char *find_first_of(const char *ptr, const char *needles, const char *end) { while (ptr < end) { const char *q; for (q = needles; *q; ++q) { if (*ptr == *q) { return ptr; } } ++ptr; } return NULL; } static void extract_name(char *name, const char *ptr, const char **end) { const char *p; name[0] = '\0'; if (!*end || *end < ptr) { return; } p = find_first_of(ptr, " \t\r\n/>", *end); if (p) { *end = p; } memcpy(name, ptr, *end - ptr); name[*end - ptr] = '\0'; } static const char *xp_find_escape(const char *escape, size_t len) { static struct escape { const char *name; const char *value; } html_escapes[] = { { "amp", "&" }, { "gt", ">" }, { "lt", "<" }, { "quot", "\"" }, { NULL, NULL } }; struct escape *n; for (n = html_escapes; n->name; ++n) { if (strncmp(escape, n->name, len) == 0) return n->value; } return NULL; } #if 0 static void xp_history_debug() { char *p = &xp_history[0]; fprintf(stderr, "DBG:"); for (;;) { if (p >= xp_history_pos) break; fprintf(stderr, " %s", p + 1); p += strlen(p + 1) + 1; } fprintf(stderr, "\n"); } #endif /* This finds the end of something like , and does not recurse * into other elements. */ static char *xp_find_start_tag_end(char *ptr) { while (*ptr) { if (*ptr == '<') { if (strstartswith(ptr, ""); if (!comment_end) return NULL; ptr = comment_end + 3; } else { return NULL; } } else if ((*ptr == '/') && (*(ptr+1) == '>')) { return ptr; } else if (*ptr == '"') { ptr++; while (*ptr) { if (*ptr == '\\') { ptr += 2; } else if (*ptr == '"') { ptr++; break; } else { ptr++; } } } else if (*ptr == '>') { return ptr; } else { ptr++; } } return ptr; } static char *xp_find_local_end() { char *ptr = xp_position[xp_stack]; int level = 0; while (*ptr) { if (*ptr == '<') { if (strstartswith(ptr, ""); if (!cdata_end) return NULL; ptr = cdata_end + 3; } else if (strstartswith(ptr, ""); if (!comment_end) return NULL; ptr = comment_end + 3; } else if (*(ptr+1) == '/') { level--; if (level < 0) return ptr; } else { level++; } } else if ((*ptr == '/') && (*(ptr+1) == '>')) { level--; if (level < 0) return ptr; } else if (*ptr == '"') { ptr++; while (*ptr) { if (*ptr == '\\') { ptr++; /* Skip the slash. */ } else if (*ptr == '"') { break; } ptr++; } } ptr++; } return ptr; } /********************* Interface routines ********************/ int xp_unescape(const char *source, char *dest) { const char *from; char *to; size_t pos; if (!source || !dest) { return -1; } from = source; to = dest; pos = strcspn(from, "&"); for (; from[pos] != '\0'; pos = strcspn(from, "&")) { size_t term; size_t escape_len; const char *escape; const char c = from[pos]; memcpy(to, from, pos); to += pos; from += pos + 1; if (c != '&') continue; term = strcspn(from, ";"); if (from[term] == '\0') { *to++ = '&'; pos = term; break; } escape = xp_find_escape(from, term); if (!escape) { *to++ = '&'; continue; } escape_len = strlen(escape); memcpy(to, escape, escape_len); to += escape_len; from += term + 1; } if (pos) { memcpy(to, from, pos); to += pos; } to[0] = '\0'; return to - dest; } int xp_set_xml_buffer_from_string(const char *str) { size_t len = strlen(str); if (len > XP_MAX_FILE_LEN) { return 0; } strcpy(xp_file, str); xp_stack = xp_stack_invalid = 0; xp_history_reset(); xp_position[xp_stack] = xp_file; if (!strstartswith(xp_position[xp_stack], "")) return 0; xp_position[xp_stack] = xp_position[xp_stack] + 2; return 1; } int xp_set_xml_buffer_from_file(const char *filename) { FILE *f = fopen(filename, "rb"); char *pos; int index = 0; int c; if (!f) { return 0; } while ((c = fgetc(f)) != EOF) { if (c == '\r') continue; xp_file[index++] = c; if (index >= XP_MAX_FILE_LEN) { xp_file[index++] = 0; xp_stack = xp_stack_invalid = 0; xp_history_reset(); xp_position[xp_stack] = xp_file; fclose(f); return 0; } } xp_file[index++] = 0; fclose(f); xp_stack = xp_stack_invalid = 0; xp_history_reset(); xp_position[xp_stack] = xp_file; if (!strstartswith(xp_position[xp_stack], ""))) return 0; xp_position[xp_stack] = pos + 2; return 1; } char *xp_open_element(int index) { char *ptr = xp_position[xp_stack]; int level = 0; int index_left = index; static char name[XP_MAX_NAME_LEN]; if (index > 0) { xp_history_pop(); } while (*ptr) { if (*ptr == '<') { if ((*(ptr+1) == '!') && (*(ptr+2) == '[') && (strstr(ptr, ""); if (!cdata_end) return NULL; ptr = cdata_end + 2; } else if ((*(ptr+1) == '!') && (*(ptr+2) == '-') && (strstr(ptr, ""); if (!comment_end) return NULL; ptr = comment_end + 2; } else if (strstartswith(ptr, ""); if (!doctype_end) return NULL; ptr = doctype_end; } else if (strstartswith(ptr, ""); if (!xmlmodel_end) return NULL; ptr = xmlmodel_end; } else if (*(ptr+1) == '/') { char *end = xp_find_start_tag_end(ptr + 2); if (!end) { return NULL; } extract_name(name, ptr + 2, (const char**)&end); level--; if (level < 0) return NULL; xp_history_pop(); if (strcmp(xp_history_pos + 1, name) && !xp_stack_invalid) { xp_stack_invalid = 1; fprintf(stderr, "Unexpected (expected )\n", name, xp_history_pos + 1); } } else { char *end = xp_find_start_tag_end(ptr + 1); if (!end) { return NULL; } extract_name(name, ptr + 1, (const char**)&end); xp_history_push(name); if (level == 0) { if (index_left) { index_left--; } else { xp_position[++xp_stack] = end; return name; } } /* We want to skip over this particular element .*/ ptr = end - 1; level++; } } else if ((*ptr == '/') && (*(ptr+1) == '>')) { level--; if (level < 0) return NULL; xp_history_pop(); } ptr++; } return NULL; } void xp_close_element() { if (!xp_stack) { xp_stack_invalid = 1; return; } xp_stack--; } int xp_is_invalid(void) { const char *elem; if (xp_stack_invalid) { return 1; } if (xp_stack) { return 1; } if ((elem = xp_open_element(1))) { /* anything after ? */ xp_close_element(); return 1; } return 0; } const char *xp_get_value(const char *name) { int index = 0; static char buffer[XP_MAX_FILE_LEN + 1]; char *ptr, *end, *check; end = xp_find_start_tag_end(xp_position[xp_stack] + 1); if (!end) return NULL; ptr = xp_position[xp_stack]; while (*ptr) { ptr = strstr(ptr, name); if (!ptr) return NULL; if (ptr > end) return NULL; /* FIXME: potential BUG in parser: we must retrieve full word, * so the use of strstr as it is is not enough. * we should check that the retrieved word is not a piece of * another one. */ check = ptr - 1; if (check >= xp_position[xp_stack]) { if ((*check != '\r') && (*check != '\n') && (*check != '\t') && (*check != ' ' )) { ptr += strlen(name); continue; } } else return(NULL); ptr += strlen(name); while ((*ptr == '\r') || (*ptr == '\n') || (*ptr == '\t') || (*ptr == ' ' ) ) { ptr++; } if (*ptr != '=') continue; ptr++; while ((*ptr == '\r') || (*ptr == '\n') || (*ptr == '\t') || (*ptr == ' ') ) { ptr++; } ptr++; if (*ptr) { while (*ptr) { if (*ptr == '\\') { ptr++; switch(*ptr) { case '\\': buffer[index++] = '\\'; break; case '"': buffer[index++] = '"'; break; case 'n': buffer[index++] = '\n'; break; case 't': buffer[index++] = '\t'; break; case 'r': buffer[index++] = '\r'; break; default: buffer[index++] = '\\'; buffer[index++] = *ptr; break; } ptr++; } else if (*ptr == '"') { break; } else { buffer[index++] = *ptr++; } if (index > XP_MAX_FILE_LEN) return NULL; } buffer[index] = 0; return buffer; } } return NULL; } char* xp_get_cdata(void) { static char buffer[XP_MAX_FILE_LEN + 1]; const char *end = xp_find_local_end(); const char *ptr; ptr = strstr(xp_position[xp_stack], " end) return NULL; end = strstr(ptr, "]]>"); if (!end) { return NULL; } if ((end - ptr) > XP_MAX_FILE_LEN) return NULL; memcpy(buffer, ptr, (end - ptr)); buffer[end-ptr] = 0; return buffer; } int xp_get_content_length(const char *P_buffer) { const char *L_ctl_hdr; int L_content_length = -1; unsigned char short_form; short_form = 0; L_ctl_hdr = strstr(P_buffer, "\nContent-Length:"); if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\nContent-length:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\ncontent-Length:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\ncontent-length:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\nCONTENT-LENGTH:"); } if (!L_ctl_hdr) { L_ctl_hdr = strstr(P_buffer, "\nl:"); short_form = 1; } if (L_ctl_hdr) { if (short_form) { L_ctl_hdr += 3; } else { L_ctl_hdr += 16; } while (isspace(*L_ctl_hdr)) L_ctl_hdr++; sscanf(L_ctl_hdr, "%d", &L_content_length); } /* L_content_length = -1 the message does not contain content-length */ return (L_content_length); } sipp-3.6.1/src/actions.cpp0000664000175000017500000005635313730472040015023 0ustar walterwalter/* * 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 * * Authors : Benjamin GAUTHIER - 24 Mar 2004 * Joseph BANINO * Olivier JACQUES * Richard GAYRAUD * From Hewlett Packard Company. * Guillaume Teissier from FTR&D */ #include "sipp.hpp" #include #ifdef PCAPPLAY #include "prepare_pcap.h" #endif static const char* strIntCmd(CAction::T_IntCmdType type) { switch (type) { case CAction::E_INTCMD_STOPCALL: return "stop_call"; case CAction::E_INTCMD_STOP_ALL: return "stop_gracefully"; case CAction::E_INTCMD_STOP_NOW: return "stop_now"; default: case CAction::E_INTCMD_INVALID: return "invalid"; } return "invalid"; } const char * CAction::comparatorToString(T_Comparator comp) { switch(comp) { case E_C_EQ: return "=="; case E_C_NE: return "!="; case E_C_GT: return ">"; case E_C_LT: return "<"; case E_C_GEQ: return ">="; case E_C_LEQ: return "<="; default: return "invalid"; } } bool CAction::compare(VariableTable *variableTable) { double lhs = variableTable->getVar(M_varInId)->getDouble(); double rhs = M_varIn2Id ? variableTable->getVar(M_varIn2Id)->getDouble() : M_doubleValue; switch(M_comp) { case E_C_EQ: return lhs == rhs; case E_C_NE: return lhs != rhs; case E_C_GT: return lhs > rhs; case E_C_LT: return lhs < rhs; case E_C_GEQ: return lhs >= rhs; case E_C_LEQ: return lhs <= rhs; default: ERROR("Internal error: Invalid comparison type %d", M_comp); return false; /* Shut up warning. */ } } void CAction::printInfo(char* buf, int len) { if (M_action == E_AT_ASSIGN_FROM_REGEXP) { if(M_lookingPlace == E_LP_MSG) { snprintf(buf, len, "Type[%d] - regexp[%s] where[%s] - checkIt[%d] - checkItInverse[%d] - $%s", M_action, M_regularExpression, "Full Msg", M_checkIt, M_checkItInverse, display_scenario->allocVars->getName(M_varId)); } else { snprintf(buf, len, "Type[%d] - regexp[%s] where[%s-%s] - checkIt[%d] - checkItInverse[%d] - $%s", M_action, M_regularExpression, "Header", M_lookingChar, M_checkIt, M_checkItInverse, display_scenario->allocVars->getName(M_varId)); } } else if (M_action == E_AT_EXECUTE_CMD) { snprintf(buf, len, "Type[%d] - command[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_EXEC_INTCMD) { snprintf(buf, len, "Type[%d] - intcmd[%-32.32s]", M_action, strIntCmd(M_IntCmd)); } else if (M_action == E_AT_LOG_TO_FILE) { snprintf(buf, len, "Type[%d] - message[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_LOG_WARNING) { snprintf(buf, len, "Type[%d] - warning[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_LOG_ERROR) { snprintf(buf, len, "Type[%d] - error[%-32.32s]", M_action, M_message_str[0]); } else if (M_action == E_AT_ASSIGN_FROM_SAMPLE) { char tmp[40]; M_distribution->textDescr(tmp, sizeof(tmp)); snprintf(buf, len, "Type[%d] - sample varId[%s] %s", M_action, display_scenario->allocVars->getName(M_varId), tmp); } else if (M_action == E_AT_ASSIGN_FROM_VALUE) { snprintf(buf, len, "Type[%d] - assign varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_ASSIGN_FROM_INDEX) { snprintf(buf, len, "Type[%d] - assign index[%s]", M_action, display_scenario->allocVars->getName(M_varId)); } else if (M_action == E_AT_ASSIGN_FROM_GETTIMEOFDAY) { snprintf(buf, len, "Type[%d] - assign gettimeofday[%s, %s]", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_subVarId[0])); } else if (M_action == E_AT_ASSIGN_FROM_STRING) { snprintf(buf, len, "Type[%d] - string assign varId[%s] [%-32.32s]", M_action, display_scenario->allocVars->getName(M_varId), M_message_str[0]); } else if (M_action == E_AT_JUMP) { snprintf(buf, len, "Type[%d] - jump varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); } else if (M_action == E_AT_PAUSE_RESTORE) { snprintf(buf, len, "Type[%d] - restore pause varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue); } else if (M_action == E_AT_VAR_ADD) { snprintf(buf, len, "Type[%d] - add varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_VAR_MULTIPLY) { snprintf(buf, len, "Type[%d] - multiply varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_VAR_DIVIDE) { snprintf(buf, len, "Type[%d] - divide varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue); } else if (M_action == E_AT_VAR_TRIM) { snprintf(buf, len, "Type[%d] - trim varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); } else if (M_action == E_AT_VAR_TEST) { snprintf(buf, len, "Type[%d] - divide varId[%s] varInId[%s] %s %lf", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_varInId), comparatorToString(M_comp), M_doubleValue); } else if (M_action == E_AT_VAR_TO_DOUBLE) { snprintf(buf, len, "Type[%d] - toDouble varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); #ifdef PCAPPLAY } else if ((M_action == E_AT_PLAY_PCAP_AUDIO) || (M_action == E_AT_PLAY_PCAP_IMAGE) || (M_action == E_AT_PLAY_PCAP_VIDEO)) { snprintf(buf, len, "Type[%d] - file[%s]", M_action, M_pcapArgs->file); } else if (M_action == E_AT_PLAY_DTMF) { snprintf(buf, len, "Type[%d] - play DTMF digits [%s]", M_action, M_message_str[0]); #endif #ifdef RTP_STREAM } else if (M_action == E_AT_RTP_STREAM_PLAY) { snprintf(buf, len, "Type[%d] - rtp_stream playfile file %s loop=%d payload %d bytes per packet=%d ms per packet=%d ticks per packet=%d", M_action, M_rtpstream_actinfo.filename, M_rtpstream_actinfo.loop_count, M_rtpstream_actinfo.payload_type, M_rtpstream_actinfo.bytes_per_packet, M_rtpstream_actinfo.ms_per_packet, M_rtpstream_actinfo.ticks_per_packet); } else if (M_action == E_AT_RTP_STREAM_PAUSE) { snprintf(buf, len, "Type[%d] - rtp_stream pause", M_action); } else if (M_action == E_AT_RTP_STREAM_RESUME) { snprintf(buf, len, "Type[%d] - rtp_stream resume", M_action); #endif } else { snprintf(buf, len, "Type[%d] - unknown action type ... ", M_action); } } CAction::T_ActionType CAction::getActionType() { return(M_action); } CAction::T_LookingPlace CAction::getLookingPlace() { return(M_lookingPlace); } CAction::T_IntCmdType CAction::getIntCmd () { return(M_IntCmd); } CAction::T_Comparator CAction::getComparator () { return(M_comp); } bool CAction::getCheckIt() { return(M_checkIt); } bool CAction::getCheckItInverse() { return(M_checkItInverse); } bool CAction::getCaseIndep() { return(M_caseIndep); } bool CAction::getHeadersOnly() { return(M_headersOnly); } int CAction::getOccurrence() { return(M_occurrence); } int CAction::getVarId() { return(M_varId); } int CAction::getVarInId() { return(M_varInId); } int CAction::getVarIn2Id() { return(M_varIn2Id); } char* CAction::getLookingChar() { return(M_lookingChar); } SendingMessage *CAction::getMessage(int n) { return(M_message[n]); } CSample* CAction::getDistribution() { return(M_distribution); } double CAction::getDoubleValue() { return(M_doubleValue); } char* CAction::getStringValue() { return(M_stringValue); } #ifdef PCAPPLAY pcap_pkts * CAction::getPcapPkts() { return(M_pcapArgs); } #endif #ifdef RTP_STREAM rtpstream_actinfo_t *CAction::getRTPStreamActInfo() { return (&M_rtpstream_actinfo); } #endif void CAction::setActionType (CAction::T_ActionType P_value) { M_action = P_value; } void CAction::setLookingPlace (CAction::T_LookingPlace P_value) { M_lookingPlace = P_value; } void CAction::setCheckIt (bool P_value) { M_checkIt = P_value; } void CAction::setCheckItInverse (bool P_value) { M_checkItInverse = P_value; } void CAction::setVarId (int P_value) { M_varId = P_value; } void CAction::setVarInId (int P_value) { M_varInId = P_value; } void CAction::setVarIn2Id (int P_value) { M_varIn2Id = P_value; } void CAction::setCaseIndep (bool P_value) { M_caseIndep = P_value; } void CAction::setOccurrence (int P_value) { M_occurrence = P_value; } void CAction::setHeadersOnly (bool P_value) { M_headersOnly = P_value; } void CAction::setIntCmd (T_IntCmdType P_type) { M_IntCmd = P_type; } void CAction::setComparator (T_Comparator P_value) { M_comp = P_value; } /* sample specific function. */ void CAction::setDistribution (CSample *P_value) { M_distribution = P_value; } /* assign from value specific function. */ void CAction::setDoubleValue (double P_value) { M_doubleValue = P_value; } /* strcmp specific function. */ void CAction::setStringValue (char *P_value) { M_stringValue = P_value; } void CAction::setSubVarId (int P_value) { if ( M_nbSubVarId < M_maxNbSubVarId ) { M_subVarId[M_nbSubVarId] = P_value; M_nbSubVarId++; } } int CAction::getSubVarId(int P_index) { return(M_subVarId[P_index]); } int* CAction::getSubVarId() { return(M_subVarId); } void CAction::setNbSubVarId (int P_value) { M_maxNbSubVarId = P_value; if(M_subVarId != NULL) { delete [] M_subVarId; M_subVarId = NULL; } M_subVarId = new int[M_maxNbSubVarId] ; M_nbSubVarId = 0 ; } int CAction::getNbSubVarId () { return(M_nbSubVarId); } void CAction::setLookingChar(const char* P_value) { if(M_lookingChar != NULL) { delete [] M_lookingChar; M_lookingChar = NULL; } if(P_value != NULL) { M_lookingChar = new char[strlen(P_value)+1]; strcpy(M_lookingChar, P_value); } } void CAction::setMessage(const char* P_value, int n) { if(M_message[n] != NULL) { delete M_message[n]; M_message[n] = NULL; } free(M_message_str[n]); M_message_str[n] = NULL; if(P_value != NULL) { M_message_str[n] = strdup(P_value); M_message[n] = new SendingMessage(M_scenario, P_value, true /* skip sanity */); } } void CAction::setRegExp(const char *P_value) { int errorCode; free(M_regularExpression); M_regularExpression = strdup(P_value); M_regExpSet = true; errorCode = regcomp(&M_internalRegExp, P_value, REGCOMP_PARAMS); if(errorCode != 0) { char buffer[MAX_HEADER_LEN]; regerror(errorCode, &M_internalRegExp, buffer, sizeof(buffer)); ERROR("recomp error : regular expression '%s' - error '%s'", M_regularExpression, buffer); } } char *CAction::getRegularExpression() { if (!M_regExpSet) { ERROR("Trying to get a regular expression for an action that does not have one!"); } return M_regularExpression; } int CAction::executeRegExp(const char* P_string, VariableTable *P_callVarTable) { regmatch_t pmatch[10]; int error; int nbOfMatch = 0; char* result = NULL ; if (!M_regExpSet) { ERROR("Trying to perform regular expression match on action that does not have one!"); } if (getNbSubVarId() > 9) { ERROR("You can only have nine sub expressions!"); } memset((void*)pmatch, 0, sizeof(regmatch_t)*10); error = regexec(&M_internalRegExp, P_string, 10, pmatch, REGEXEC_PARAMS); if ( error == 0) { CCallVariable* L_callVar = P_callVarTable->getVar(getVarId()); for(int i = 0; i <= getNbSubVarId(); i++) { if(pmatch[i].rm_eo != -1) { setSubString(&result, P_string, pmatch[i].rm_so, pmatch[i].rm_eo); L_callVar->setMatchingValue(result); nbOfMatch++; } if (i == getNbSubVarId()) break ; L_callVar = P_callVarTable->getVar(getSubVarId(i)); } } return(nbOfMatch); } void CAction::setSubString(char** P_target, const char* P_source, int P_start, int P_stop) { int sizeOf; if(P_source != NULL) { sizeOf = P_stop - P_start; (*P_target) = new char[sizeOf + 1]; if (sizeOf > 0) { memcpy((*P_target), &(P_source[P_start]), sizeOf); } (*P_target)[sizeOf] = '\0'; } else { *P_target = NULL ; } } #ifdef PCAPPLAY void CAction::setPcapArgs (pcap_pkts * P_value) { if(M_pcapArgs != NULL) { free(M_pcapArgs); M_pcapArgs = NULL; } if(P_value != NULL) { M_pcapArgs = (pcap_pkts *)malloc(sizeof(*M_pcapArgs)); memcpy(M_pcapArgs, P_value, sizeof(*M_pcapArgs)); } } void CAction::setPcapArgs(const char* P_value) { if(M_pcapArgs != NULL) { free(M_pcapArgs); M_pcapArgs = NULL; } if(P_value != NULL) { M_pcapArgs = (pcap_pkts *) malloc(sizeof(*M_pcapArgs)); if (parse_play_args(P_value, M_pcapArgs) == -1) { ERROR("Play pcap error"); } if (access(M_pcapArgs->file, F_OK)) { ERROR("Cannot read file %s", M_pcapArgs->file); } } } #endif #ifdef RTP_STREAM void CAction::setRTPStreamActInfo(const char* P_value) { char* param_str; char* next_comma; if (strlen(P_value) >= sizeof(M_rtpstream_actinfo.filename)) { ERROR("Filename %s is too long, maximum supported length %zu", P_value, sizeof(M_rtpstream_actinfo.filename) - 1); } strcpy(M_rtpstream_actinfo.filename, P_value); param_str = strchr(M_rtpstream_actinfo.filename, ','); next_comma = NULL; M_rtpstream_actinfo.loop_count = 1; if (param_str) { /* we have a loop count parameter */ *(param_str++) = 0; next_comma= strchr(param_str, ','); if (next_comma) { *(next_comma++) = 0; } M_rtpstream_actinfo.loop_count = atoi(param_str); param_str = next_comma; } M_rtpstream_actinfo.payload_type= rtp_default_payload; if (param_str) { /* we have a payload type parameter */ next_comma= strchr (param_str,','); if (next_comma) { *(next_comma++)= 0; } M_rtpstream_actinfo.payload_type= atoi(param_str); } /* Setup based on what we know of payload types */ switch (M_rtpstream_actinfo.payload_type) { case 0: M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 160; M_rtpstream_actinfo.ticks_per_packet = 160; break; case 8: M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 160; M_rtpstream_actinfo.ticks_per_packet = 160; break; case 9: M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 160; M_rtpstream_actinfo.ticks_per_packet = 160; break; case 18: M_rtpstream_actinfo.ms_per_packet = 20; M_rtpstream_actinfo.bytes_per_packet = 20; M_rtpstream_actinfo.ticks_per_packet = 160; break; case 98: M_rtpstream_actinfo.ms_per_packet = 30; M_rtpstream_actinfo.bytes_per_packet = 50; M_rtpstream_actinfo.ticks_per_packet = 240; break; default: M_rtpstream_actinfo.ms_per_packet= -1; M_rtpstream_actinfo.bytes_per_packet= -1; M_rtpstream_actinfo.ticks_per_packet= -1; ERROR("Unknown rtp payload type %d - cannot set playback parameters", M_rtpstream_actinfo.payload_type); break; } if (rtpstream_cache_file(M_rtpstream_actinfo.filename) < 0) { ERROR("Cannot read/cache rtpstream file %s", M_rtpstream_actinfo.filename); } } void CAction::setRTPStreamActInfo(rtpstream_actinfo_t *P_value) { /* At this stage the entire rtpstream action info structure can simply be */ /* copied. No members need to be individually duplicated/processed. */ memcpy(&M_rtpstream_actinfo,P_value, sizeof(M_rtpstream_actinfo)); } #endif void CAction::setScenario(scenario * P_scenario) { M_scenario = P_scenario; } void CAction::setAction(CAction P_action) { if (P_action.getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) { assert(P_action.getDistribution() != NULL); } int L_i; setActionType ( P_action.getActionType() ); setLookingPlace ( P_action.getLookingPlace() ); setVarId ( P_action.getVarId() ); setVarInId ( P_action.getVarInId() ); setDoubleValue ( P_action.getDoubleValue() ); setDistribution ( P_action.getDistribution() ); setScenario ( P_action.M_scenario ); setNbSubVarId ( P_action.getNbSubVarId() ); for (L_i = 0; L_i < P_action.getNbSubVarId() ; L_i++ ) { setSubVarId (P_action.getSubVarId(L_i)); } setLookingChar ( P_action.getLookingChar() ); setCheckIt ( P_action.getCheckIt() ); setCheckItInverse ( P_action.getCheckItInverse() ); setCaseIndep ( P_action.getCaseIndep() ); setOccurrence ( P_action.getOccurrence() ); setHeadersOnly ( P_action.getHeadersOnly() ); for (L_i = 0; L_i < MAX_ACTION_MESSAGE; L_i++) { setMessage(P_action.M_message_str[L_i], L_i); } setRegExp ( P_action.M_regularExpression); setIntCmd ( P_action.M_IntCmd ); #ifdef PCAPPLAY setPcapArgs ( P_action.M_pcapArgs ); #endif #ifdef RTP_STREAM setRTPStreamActInfo(&(P_action.M_rtpstream_actinfo)); #endif } CAction::CAction(scenario *scenario) { M_action = E_AT_NO_ACTION; M_varId = 0; M_varInId = 0; M_varIn2Id = 0; M_nbSubVarId = 0; M_maxNbSubVarId = 0; M_subVarId = NULL; M_checkIt = false; M_checkItInverse = false; M_lookingPlace = E_LP_MSG; M_lookingChar = NULL; M_caseIndep = false; M_occurrence = 1; M_headersOnly = true; for (int i = 0; i < MAX_ACTION_MESSAGE; i++) { M_message[i] = NULL; M_message_str[i] = NULL; } M_IntCmd = E_INTCMD_INVALID; M_doubleValue = 0; M_stringValue = NULL; M_distribution = NULL; #ifdef PCAPPLAY M_pcapArgs = NULL; #endif #ifdef RTP_STREAM memset(&M_rtpstream_actinfo, 0, sizeof(M_rtpstream_actinfo)); #endif M_scenario = scenario; M_regExpSet = false; M_regularExpression = NULL; } CAction::~CAction() { if(M_lookingChar != NULL) { delete [] M_lookingChar; M_lookingChar = NULL; } for (int i = 0; i < MAX_ACTION_MESSAGE; i++) { if(M_message[i] != NULL) { delete M_message[i]; M_message[i] = NULL; } free(M_message_str[i]); M_message_str[i] = NULL; } if(M_subVarId != NULL) { delete [] M_subVarId; M_subVarId = NULL; } free(M_stringValue); #ifdef PCAPPLAY if (M_pcapArgs != NULL) { free_pcaps(M_pcapArgs); M_pcapArgs = NULL; } #endif if (M_regExpSet) { regfree(&M_internalRegExp); free(M_regularExpression); } if (M_distribution) { delete M_distribution; } } /****************************** CActions class ************************/ void CActions::printInfo() { printf("Action Size = [%d]\n", M_nbAction); for(int i=0; iprintInfo(buf, 80); printf("%s\n", buf); } } void CActions::reset() { for (int i = 0; i < M_nbAction; i++) { delete M_actionList[i]; M_actionList[i] = NULL; } M_nbAction = 0; } int CActions::getActionSize() { return(M_nbAction); } void CActions::setAction(CAction *P_action) { CAction **newActions = new CAction*[M_nbAction + 1]; if (!newActions) { ERROR("Could not allocate new action list."); } for (int i = 0; i < M_nbAction; i++) { newActions[i] = M_actionList[i]; } if (M_actionList) { delete [] M_actionList; } M_actionList = newActions; M_actionList[M_nbAction] = P_action; M_nbAction++; } CAction* CActions::getAction(int i) { if(i < M_nbAction) { return(M_actionList[i]); } else return(NULL); } CActions::CActions() { M_nbAction = 0; M_actionList = NULL; } CActions::~CActions() { for (int i = 0; i < M_nbAction; i++) { delete M_actionList[i]; } delete [] M_actionList; M_actionList = NULL; } #ifdef GTEST #include "gtest/gtest.h" TEST(actions, MatchingRegexp) { AllocVariableTable vt(NULL); int id = vt.find("1", true); int sub1_id = vt.find("2", true); int sub2_id = vt.find("3", true); int sub3_id = vt.find("4", true); int sub4_id = vt.find("5", true); CAction re(NULL); re.setVarId(id); re.setNbSubVarId(4); re.setSubVarId(sub1_id); re.setSubVarId(sub2_id); re.setSubVarId(sub3_id); re.setSubVarId(sub4_id); re.setRegExp("(.+)(o) (.+)(d)"); int results = re.executeRegExp("hello world", &vt); ASSERT_EQ(5, results); ASSERT_STREQ("hello world", vt.getVar(id)->getString()); ASSERT_STREQ("hell", vt.getVar(sub1_id)->getString()); ASSERT_STREQ("o", vt.getVar(sub2_id)->getString()); ASSERT_STREQ("worl", vt.getVar(sub3_id)->getString()); ASSERT_STREQ("d", vt.getVar(sub4_id)->getString()); } TEST(actions, NonMatchingRegexp) { AllocVariableTable vt(NULL); int id = vt.find("1", true); int sub1_id = vt.find("2", true); int sub2_id = vt.find("3", true); int sub3_id = vt.find("4", true); int sub4_id = vt.find("5", true); CAction re(NULL); re.setVarId(id); re.setNbSubVarId(4); re.setSubVarId(sub1_id); re.setSubVarId(sub2_id); re.setSubVarId(sub3_id); re.setSubVarId(sub4_id); re.setRegExp("(.+)(o) (.+)(d)"); int results = re.executeRegExp("", &vt); ASSERT_EQ(0, results); ASSERT_STREQ("", vt.getVar(id)->getString()); ASSERT_STREQ("", vt.getVar(sub1_id)->getString()); } #endif sipp-3.6.1/src/send_packets.c0000664000175000017500000002664413730472040015466 0ustar walterwalter/* * send_packets.c: from tcpreplay tools by Aaron Turner * http://tcpreplay.sourceforge.net/ * send_packets.c is under BSD license (see below) * SIPp is under GPL license * * * Copyright (c) 2001-2004 Aaron Turner. * 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. * 3. Neither the names of the copyright owners nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "defines.h" #include "send_packets.h" #include "prepare_pcap.h" #include "config.h" #ifndef HAVE_UDP_UH_PREFIX #define uh_ulen len #define uh_sum check #define uh_sport source #define uh_dport dest #endif extern char* scenario_path; extern volatile unsigned long rtp_pckts_pcap; extern volatile unsigned long rtp_bytes_pcap; extern bool media_ip_is_ipv6; inline void timerdiv(struct timeval* tvp, float div) { double interval; if (div == 0 || div == 1) return; interval = ((double) tvp->tv_sec * 1000000 + tvp->tv_usec) / (double) div; tvp->tv_sec = interval / (int) 1000000; tvp->tv_usec = interval - (tvp->tv_sec * 1000000); } /* * converts a float to a timeval structure */ inline void float2timer(float time, struct timeval *tvp) { float n; n = time; tvp->tv_sec = n; n -= tvp->tv_sec; tvp->tv_usec = n * 100000; } static char* find_file(const char* filename) { char *fullpath; if (filename[0] == '/' || !*scenario_path) { return strdup(filename); } fullpath = malloc(MAX_PATH); snprintf(fullpath, MAX_PATH, "%s%s", scenario_path, filename); if (access(fullpath, R_OK) < 0) { free(fullpath); WARNING("SIPp now prefers looking for pcap files next to the scenario. " "%s couldn't be found next to the scenario, falling back to " "using the current working directory", filename); return strdup(filename); } return fullpath; } int parse_play_args(const char* filename, pcap_pkts* pkts) { pkts->file = find_file(filename); prepare_pkts(pkts->file, pkts); return 1; } void free_pcaps(pcap_pkts* pkts) { pcap_pkt *it; for (it = pkts->pkts; it != pkts->max; ++it) { free(it->data); } free(pkts->pkts); free(pkts->file); free(pkts); } int parse_dtmf_play_args(const char* buffer, pcap_pkts* pkts, uint16_t start_seq_no) { pkts->file = strdup(buffer); return prepare_dtmf(pkts->file, pkts, start_seq_no); } void hexdump(char *p, int s) { int i; for (i = 0; i < s; i++) { fprintf(stderr, "%02x ", *(char *)(p+i)); } fprintf(stderr, "\n"); } /* Safe threaded version */ void do_sleep (struct timeval *, struct timeval *, struct timeval *, struct timeval *); void send_packets_cleanup(void *arg) { int * sock = (int *) arg; /* Close send socket */ close(*sock); } void send_packets_pcap_cleanup(void* arg) { play_args_t* play_args = arg; if (play_args->free_pcap_when_done) { free(play_args->pcap); play_args->pcap = NULL; } } void send_packets(play_args_t* play_args) { pthread_cleanup_push(send_packets_pcap_cleanup, ((void*)play_args)); int ret = 0, sock, port_diff; pcap_pkt *pkt_index, *pkt_max; uint16_t *from_port, *to_port; struct timeval didsleep = { 0, 0 }; struct timeval start = { 0, 0 }; struct timeval last = { 0, 0 }; pcap_pkts *pkts = play_args->pcap; /* to and from are pointers in case play_args (call sticky) gets modified! */ struct sockaddr_storage *to = &(play_args->to); struct sockaddr_storage *from = &(play_args->from); struct sockaddr_storage bind_addr = {0}; struct udphdr *udp; struct sockaddr_in6 to6, from6; char buffer[PCAP_MAXPACKET]; int temp_sum; socklen_t len; #ifndef MSG_DONTWAIT int fd_flags; #endif if (media_ip_is_ipv6) { sock = socket(PF_INET6, SOCK_RAW, IPPROTO_UDP); if (sock < 0) { ERROR("Can't create raw IPv6 socket (need to run as root?): %s", strerror(errno)); goto pop2; } from_port = &(((struct sockaddr_in6 *)from)->sin6_port); len = sizeof(struct sockaddr_in6); to_port = &(((struct sockaddr_in6 *)to)->sin6_port); } else { sock = socket(PF_INET, SOCK_RAW, IPPROTO_UDP); from_port = &(((struct sockaddr_in *)from)->sin_port); len = sizeof(struct sockaddr_in); to_port = &(((struct sockaddr_in *)to)->sin_port); if (sock < 0) { ERROR("Can't create raw IPv4 socket (need to run as root?): %s", strerror(errno)); goto pop2; } } // When binding a raw socket, it doesn't make sense to bind to a particular // port, as that's a UDP/TCP concept but the point of a raw socket is that // we're writing the headers ourselves. Some systems (like FreeBSD) are // strict about this and return EADDRNOTAVAIL if we specify a port, so bind // to a sockaddr structure copied from our sending address but with the // port set to 0. if (media_ip_is_ipv6) { memcpy(&bind_addr, from, sizeof(struct sockaddr_in6)); ((struct sockaddr_in6 *)&bind_addr)->sin6_port = 0; } else { memcpy(&bind_addr, from, sizeof(struct sockaddr_in)); ((struct sockaddr_in *)&bind_addr)->sin_port = 0; } if ((ret = bind(sock, (struct sockaddr *)&bind_addr, len))) { ERROR("Can't bind media raw socket: %s", strerror(errno)); goto pop2; } #ifndef MSG_DONTWAIT fd_flags = fcntl(sock, F_GETFL , NULL); fd_flags |= O_NONBLOCK; fcntl(sock, F_SETFL , fd_flags); #endif udp = (struct udphdr *)buffer; pkt_index = pkts->pkts; pkt_max = pkts->max; if (media_ip_is_ipv6) { memset(&to6, 0, sizeof(to6)); memset(&from6, 0, sizeof(from6)); to6.sin6_family = AF_INET6; from6.sin6_family = AF_INET6; memcpy(&(to6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) to)->sin6_addr.s6_addr), sizeof(to6.sin6_addr.s6_addr)); memcpy(&(from6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) from)->sin6_addr.s6_addr), sizeof(from6.sin6_addr.s6_addr)); } /* Ensure the sender socket is closed when the thread exits - this * allows the thread to be cancelled cleanly. */ pthread_cleanup_push(send_packets_cleanup, ((void *) &sock)); while (pkt_index < pkt_max) { memcpy(udp, pkt_index->data, pkt_index->pktlen); port_diff = ntohs(udp->uh_dport) - pkts->base; /* modify UDP ports */ udp->uh_sport = htons(port_diff + ntohs(*from_port)); udp->uh_dport = htons(port_diff + ntohs(*to_port)); if (!media_ip_is_ipv6) { temp_sum = checksum_carry( pkt_index->partial_check + check((uint16_t *) &(((struct sockaddr_in *)(void *) from)->sin_addr.s_addr), 4) + check((uint16_t *) &(((struct sockaddr_in *)(void *) to)->sin_addr.s_addr), 4) + check((uint16_t *) &udp->uh_sport, 4)); } else { temp_sum = checksum_carry( pkt_index->partial_check + check((uint16_t *) &(from6.sin6_addr.s6_addr), 16) + check((uint16_t *) &(to6.sin6_addr.s6_addr), 16) + check((uint16_t *) &udp->uh_sport, 4)); } #if !defined(_HPUX_LI) && defined(__HPUX) udp->uh_sum = (temp_sum>>16)+((temp_sum & 0xffff)<<16); #else udp->uh_sum = temp_sum; #endif do_sleep ((struct timeval *) &pkt_index->ts, &last, &didsleep, &start); #ifdef MSG_DONTWAIT if (!media_ip_is_ipv6) { ret = sendto(sock, buffer, pkt_index->pktlen, MSG_DONTWAIT, (struct sockaddr *)to, sizeof(struct sockaddr_in)); } else { ret = sendto(sock, buffer, pkt_index->pktlen, MSG_DONTWAIT, (struct sockaddr *)&to6, sizeof(struct sockaddr_in6)); } #else if (!media_ip_is_ipv6) { ret = sendto(sock, buffer, pkt_index->pktlen, 0, (struct sockaddr *)to, sizeof(struct sockaddr_in)); } else { ret = sendto(sock, buffer, pkt_index->pktlen, 0, (struct sockaddr *)&to6, sizeof(struct sockaddr_in6)); } #endif if (ret < 0) { WARNING("send_packets.c: sendto failed with error: %s", strerror(errno)); goto pop1; } rtp_pckts_pcap++; rtp_bytes_pcap += pkt_index->pktlen - sizeof(*udp); memcpy (&last, &(pkt_index->ts), sizeof(struct timeval)); pkt_index++; } /* Closing the socket is handled by pthread_cleanup_push()/pthread_cleanup_pop() */ pop1: pthread_cleanup_pop(1); pop2: pthread_cleanup_pop(1); } /* * Given the timestamp on the current packet and the last packet sent, * calculate the appropriate amount of time to sleep and do so. */ void do_sleep(struct timeval* time, struct timeval* last, struct timeval* didsleep, struct timeval* start) { struct timeval nap, now, delta; struct timespec sleep; if (gettimeofday (&now, NULL) < 0) { fprintf (stderr, "Error gettimeofday: %s\n", strerror (errno)); } /* First time through for this file */ if (!timerisset (last)) { *start = now; timerclear (&delta); timerclear (didsleep); } else { timersub (&now, start, &delta); } if (timerisset (last) && timercmp (time, last, >)) { timersub (time, last, &nap); } else { /* * Don't sleep if this is our first packet, or if the * this packet appears to have been sent before the * last packet. */ timerclear (&nap); } timeradd (didsleep, &nap, didsleep); if (timercmp (didsleep, &delta, >)) { timersub (didsleep, &delta, &nap); sleep.tv_sec = nap.tv_sec; sleep.tv_nsec = nap.tv_usec * 1000; /* convert ms to ns */ while ((nanosleep (&sleep, &sleep) == -1) && (errno == -EINTR)); } } sipp-3.6.1/dtd_check.sh0000755000175000017500000000047413730472040014326 0ustar walterwalter#!/bin/sh failures=0 for file in $(find . -name '*.xml'); do if ! xmllint --path . --dtdvalid ./sipp.dtd $file >/dev/null; then echo "ERROR: $file failed validation" failures=$((failures+1)) fi done if test $failures -ne 0; then echo "Not OK" >&2 exit 1 fi echo "All files OK" >&2 sipp-3.6.1/.gitmodules0000664000175000017500000000211613730472040014231 0ustar walterwalter[submodule "gtest"] # https://code.google.com/p/googletest/wiki/FAQ # > In the early days, we said that you could install compiled # > Google Test libraries on *nix systems using make install. # > Then every user of your machine can write tests without # > recompiling Google Test. # > # > This seemed like a good idea, but it has a got-cha: every # > user needs to compile his tests using the same compiler # > flags used to compile the installed Google Test libraries; # > otherwise he may run into undefined behaviors (i.e. the # > tests can behave strangely and may even crash for no obvious # > reasons). # ... # > Therefore, for your sanity, we recommend to avoid installing # > pre-compiled Google Test libraries. Instead, each project # > should compile Google Test itself such that it can be sure # > that the same flags are used for both Google Test and the # > tests. # path = gtest url = https://chromium.googlesource.com/external/googletest ignore = dirty [submodule "gmock"] path = gmock url = https://chromium.googlesource.com/external/googlemock ignore = dirty sipp-3.6.1/README.md0000664000175000017500000000655413730472040013345 0ustar walterwalter Travis Build Status Coverity Scan Build Status SIPp - a SIP protocol test tool Copyright (C) 2003-2019 - The Authors 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 3 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, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). # Documentation See the `docs/` directory. Hopefully it is also available in html format at: https://sipp.readthedocs.io/en/latest/ # Building This is the SIPp package. Please refer to the [webpage](http://sipp.sourceforge.net/) for details and documentation. Normally, you should be able to build SIPp by using CMake: ``` cmake . make ``` There are several optional flags to enable features (SIP-over-TLS, SIP-over-SCTP, media playback from PCAP files and the GNU Statistical libbraries for random distributions): ``` cmake . -DUSE_SSL=1 -DUSE_SCTP=1 -DUSE_PCAP=1 -DUSE_GSL=1 ``` ## Static builds SIPp can be built into a single static binary, removing the need for libraries to exist on the target system and maximising portability. This is a [fairly complicated process](https://medium.com/@neunhoef/static-binaries-for-a-c-application-f7c76f8041cf), and for now, it only works on Alpine Linux. To build a static binary, pass `-DBUILD_STATIC=1` to cmake. # Support I try and be responsive to issues raised on Github, and there's [a reasonably active mailing list](https://lists.sourceforge.net/lists/listinfo/sipp-users). # Making a release * Update CHANGES.md. Tag release. * Make `sipp.1` by calling `help2man --output=sipp.1 -v -v --no-info --name='SIP testing tool and traffic generator' ./sipp` * Copy `sipp.1`, copy `version.h`. * Create sipp-VERSION.tar.gz with subdirectory sipp-VERSION. Upload to github as "binary". * Run `sudo docker build -t sipp-build docker && sudo docker run -it -v $PWD:/src sipp-build` to create a static binary. Upload this to Github as well. # Contributing SIPp is free software, under the terms of the GPL licence (see the LICENCE.txt file for details). You can contribute to the development of SIPp and use the standard Github fork/pull request method to integrate your changes integrate your changes. If you make changes in SIPp, *PLEASE* follow a few coding rules: - Please stay conformant with the current indentation style (4 spaces indent, standard Emacs-like indentation). Examples: ``` if (condition) { /* "{" even if only one instruction */ f(); /* 4 space indents */ } else { char* p = ptr; /* C++-style pointer declaration placement */ g(p); } ``` - If possible, check your changes can be compiled on: - Linux, - Cygwin, - Mac OS X, - FreeBSD. Thanks, Rob Day sipp-3.6.1/.gitignore0000664000175000017500000000106613730472040014047 0ustar walterwalter*.o *~ .*.sw* .autoclean .deps/ .version Makefile Makefile.in Makefile.am aclocal.m4 autom4te.cache/ configure config.guess config.h config.log config.status config.sub compile depcomp .dirstamp install-sh missing sipp sipp.1 sipp-*.tar.gz sipp_unittest stamp-h1 test-driver *.log *.csv vim_ctags compile_commands.json .*.sw? stamp-h.in *.orig *.rej *.patch compile_commands.json CMakeCache.txt CMakeFiles/ cmake_install.cmake # Ignore *.xml in root dir, but not in regression tests dir. /*.xml # Ignore autogenerated version.h. /include/version.cmake /version.h sipp-3.6.1/sipp.dtd0000664000175000017500000000742713730472040013536 0ustar walterwalter sipp-3.6.1/pcap/0000775000175000017500000000000013730472040012777 5ustar walterwaltersipp-3.6.1/pcap/dtmf_2833_5.pcap0000664000175000017500000000137413730472040015506 0ustar walterwalterÔò¡ÿÿ¤ñC“x::P¿™6 ‡¬$E,÷è@„À¨À¨À'b€åÛ¨À8N ¤ñC|Æ::P¿™6 ‡¬$E,÷é@ƒÀ¨À¨À'¡€eܨÀ8N @¤ñC›::P¿™6 ‡¬$E,÷ê@‚À¨À¨À'`€eݨÀ8N €¤ñCÒb::P¿™6 ‡¬$E,÷ë@À¨À¨À'ÿ€eÞ¨À8N À¤ñC÷°::P¿™6 ‡¬$E,÷ì@€À¨À¨À'ýÝ€eߨÀ8N ¤ñC$ÿ::P¿™6 ‡¬$E,÷í@À¨À¨À'üœ€eà¨À8N @¤ñC$M::P¿™6 ‡¬$E,÷î@~À¨À¨À'û[€eá¨À8N €¤ñC]›::P¿™6 ‡¬$E,÷ï@}À¨À¨À'ùš€eâ¨À8NŠÀ¤ñCÍ›::P¿™6 ‡¬$E,÷ð@|À¨À¨À'ùš€eâ¨À8NŠÀ¤ñCÙ›::P¿™6 ‡¬$E,÷ñ@{À¨À¨À'ùš€eâ¨À8NŠÀsipp-3.6.1/pcap/dtmf_2833_4.pcap0000664000175000017500000000137413730472040015505 0ustar walterwalterÔò¡ÿÿ£ñC "::P¿™6 ‡¬$E,÷¢@ÊÀ¨À¨À'D€å¹‘8N £ñC‡p::P¿™6 ‡¬$E,÷£@ÉÀ¨À¨À'ƒ€eº‘8N @£ñCo¾::P¿™6 ‡¬$E,÷¤@ÈÀ¨À¨À'B€e»‘8N €£ñC ::P¿™6 ‡¬$E,÷¥@ÇÀ¨À¨À'€e¼‘8N À£ñClZ ::P¿™6 ‡¬$E,÷¦@ÆÀ¨À¨À'À€e½‘8N £ñCš¨ ::P¿™6 ‡¬$E,÷§@ÅÀ¨À¨À'€e¾‘8N @£ñC[ö ::P¿™6 ‡¬$E,÷¨@ÄÀ¨À¨À'>€e¿‘8N €£ñCšD ::P¿™6 ‡¬$E,÷©@ÃÀ¨À¨À'}€eÀ‘8NŠÀ£ñCïD ::P¿™6 ‡¬$E,÷ª@ÂÀ¨À¨À'}€eÀ‘8NŠÀ£ñCúD ::P¿™6 ‡¬$E,÷«@ÁÀ¨À¨À'}€eÀ‘8NŠÀsipp-3.6.1/pcap/dtmf_2833_0.pcap0000664000175000017500000000137413730472040015501 0ustar walterwalterÔò¡ÿÿ ñC–s::P¿™6 ‡¬$E,ö™@ÓÀ¨À¨À'[í€å/0Dà8N  ñC®Á::P¿™6 ‡¬$E,öš@ÒÀ¨À¨À'[,€e/1Dà8N @ ñC_ ::P¿™6 ‡¬$E,ö›@ÑÀ¨À¨À'Yë€e/2Dà8N € ñC] ::P¿™6 ‡¬$E,öœ@ÐÀ¨À¨À'Xª€e/3Dà8N À ñC¬ ::P¿™6 ‡¬$E,ö@ÏÀ¨À¨À'Wi€e/4Dà8N  ñCëù ::P¿™6 ‡¬$E,öž@ÎÀ¨À¨À'V(€e/5Dà8N @ ñCÏG ::P¿™6 ‡¬$E,öŸ@ÍÀ¨À¨À'Tç€e/6Dà8N € ñCÜ• ::P¿™6 ‡¬$E,ö @ÌÀ¨À¨À'S&€e/7Dà8NŠÀ ñC– ::P¿™6 ‡¬$E,ö¡@ËÀ¨À¨À'S&€e/7Dà8NŠÀ ñC/– ::P¿™6 ‡¬$E,ö¢@ÊÀ¨À¨À'S&€e/7Dà8NŠÀsipp-3.6.1/pcap/dtmf_2833_8.pcap0000664000175000017500000000137413730472040015511 0ustar walterwalterÔò¡ÿÿ¦ñCR…::P¿™6 ‡¬$E,ø¢@ÊÀ¨À¨À'º?€å =í€8N ¦ñCbÓ::P¿™6 ‡¬$E,ø£@ÉÀ¨À¨À'¹~€e >í€8N @¦ñCƒ!::P¿™6 ‡¬$E,ø¤@ÈÀ¨À¨À'¸=€e ?í€8N €¦ñC“o::P¿™6 ‡¬$E,ø¥@ÇÀ¨À¨À'¶ü€e @í€8N À¦ñC¾::P¿™6 ‡¬$E,ø¦@ÆÀ¨À¨À'µ»€e Aí€8N ¦ñCð ::P¿™6 ‡¬$E,ø§@ÅÀ¨À¨À'´z€e Bí€8N @¦ñCèY ::P¿™6 ‡¬$E,ø¨@ÄÀ¨À¨À'³9€e Cí€8N €¦ñCê§ ::P¿™6 ‡¬$E,ø©@ÃÀ¨À¨À'±x€e Dí€8NŠÀ¦ñCZ¨ ::P¿™6 ‡¬$E,øª@ÂÀ¨À¨À'±x€e Dí€8NŠÀ¦ñCf¨ ::P¿™6 ‡¬$E,ø«@ÁÀ¨À¨À'±x€e Dí€8NŠÀsipp-3.6.1/pcap/g711a.pcap0000664000175000017500000021674013730472040014476 0ustar walterwalterÔò¡ÿÿ×é@=V&&ÐPfv" E@@#  ˆÖR€ˆæýðÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=fŒ&&ÐPfv" E@@#  ˆÖRQ€æþàÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=&&ÐPfv" E@@#  ˆÖQ`€æÿÐÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=»w&&ÐPfv" E@@#  ˆÖPo€çÀÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=[í&&ÐPfv" E@@#  ˆÖO~€ç°ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=Bc&&ÐPfv" E@@#  ˆÖN€ç ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=|Ó&&ÐPfv" E@@#  ˆÖMœ€çÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=£H&&ÐPfv" E@@#  ˆÖL«€ç€ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=ɽ&&ÐPfv" E@@#  ˆÖKº€çpÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@= 3&&ÐPfv" E@@#  ˆÖJÉ€ç `ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=1¨&&ÐPfv" E@@#  ˆÖIØ€ç PÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=Ú &&ÐPfv" E@@#  ˆÖHç€ç @ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=Ä’ &&ÐPfv" E@@#  ˆÖGö€ç 0ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=% &&ÐPfv" E@@#  ˆÖG€ç ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=é| &&ÐPfv" E@@#  ˆÖF€ç ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=.ò &&ÐPfv" E@@#  ˆÖE#€ç ÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=ñk &&ÐPfv" E@@#  ˆÖD2€ç ðÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=†Ü &&ÐPfv" E@@#  ˆÖCA€çàÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=WR &&ÐPfv" E@@#  ˆÖBP€çÐÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=èÆ &&ÐPfv" E@@#  ˆÖA_€çÀÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕ×é@=†< &&ÐPfv" E@@#  ˆÖªù€ç°ÞàîJJZZZZZZZZZZZZZZZZZZZZÕZZÕZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕZÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕZZZZZÕZZZÕÕÕZÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕZZÕZZÕÕÕÕÕZZZÕÕÕZZZZZZZZÕÕÕÕÕZZ×é@=>± &&ÐPfv" E@@#  ˆÖD€ç ÞàîZZZZZZZÕZZZÕÕZZZZZZZZZZZZZZZZZÕZÕÕZÕÕZZÕÕZÕZZZÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕZÕZÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZÕÕÕÕÕÕÕZZÕÕÕZZZÕÕZÕZZZZZÕÕÕZZZÕÕÕZZZZZZZZÕZZZZZÕZZZZÕZZÕZÕÕÕÕÕZZZÕÕÕÕZÕÕÕÕÕZZÕÕÕÕ×é@=v&&&ÐPfv" E@@#  ˆÖ.f€çÞàîÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕZZÕZZZÕÕZZZÕZZZZZÕÕZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZÕZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ×é@=Ý›&&ÐPfv" E@@#  ˆÖó€ç€ÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZÕZZZÕÕZZÕÕÕZZZZÕÕÕZÕÕÕÕÕZÕÕÕÕÕÕZZZZÕÕÕÕÕÕÕÕÕÕZZÕZZZZÕÕZZZÕÕÕZZÕZZZZZZZÕÕZZÕZZZZÕZÕZZZZZZÕZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZÕZZZZZZZZZÕÕZZZZÕZZZZZZZÕZZZZZZZ×é@=Ï&&ÐPfv" E@@#  ˆÖÊÓ€çpÞàîZZZZZÕZÕÕÕÕÕÕZÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕZÕZZZZZZÕZZZZZÕZZÕÕÕZZÕÕZZÕÕÕZZZZÕZZÕZZÕÕZZZÕÕÕZZZZZZÕZZZÕZZZZZÕÕÕZZZÕZZZZÕÕÕÕZZZZZZZZZZÕÕZZZZZÕZZZÕZZZZZZZÕZZÕZÕÕZÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZÕÕÕZÕÕÕÕÕÕÕZZÕZZÕÕÕÕÕÕÕÕÕZZÕÕÕØé@=D&&ÐPfv" E@@#  ˆÖ¼€ç`ÞàîÕÕZZZZZZZZZÕZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZÕÕÕZZÕÕÕZÕZZZZZZZZZZÕZZÕÕZZZZZZZZÕÕZZÕÕÕZÕÕÕÕÕÕZZZZZZÕÕZZÕZÕÕZÕÕZÕÕZZÕZZZZÕÕZZÕÕZZZÕÕZZZZZZZÕÕZZZZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕZZZZZZZZZZZZZZZZZZZZÕZZZZÕÕÕÕÕZZZZZZÕÕÕÕÕÕÕÕÕÕZØé@=ø¸&&ÐPfv" E@@#  ˆÖ¾Ñ€çPÞàîÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕZÕÕÕÕÕÕÕZÕÕÕÕZZZZZZZZZÕZÕÕÕZZZZZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZÕZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZÕÕZZZZZZZZZZZØé@=M5&&ÐPfv" E@@#  ˆÖ1Ä€ç@ÞàîZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZÕZZZZZZÕZZZZZZÕÕÕZÕÕÕÕÕZÕÕÕÕÕÕÕÕÕZÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕZÕÕÕZZÕZZÕZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZØé@=L£&&ÐPfv" E@@#  ˆÖM€ç0ÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZØé@=1&&ÐPfv" E@@#  ˆÖqÊ€ç ÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕZZÕÕÕÕÕÕZZZZZZÕÕÕÕZZÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕZÕZÕÕÕÕÕZZÕÕZZÕÕÕÕÕZZZZÕZZZZZÕZZZZZZZZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZØé@=5&&ÐPfv" E@@#  ˆÖÙ¦€çÞàîZZÕÕÕÕZZZZZZZZZZÕÕZÕÕÕÕZZZZÕÕZZZÕÕZZZZZÕZÕÕZÕÕÕZZZZZZÕÕÕZÕÕZZZZÕÕZZZZZZZZZZZZZZZZZZÕÕÕZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZÕÕÕÕÕÕÕÕZZZÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕØé@=&&ÐPfv" E@@#  ˆÖ˜•€çÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕZZÕZZZÕÕZÕÕZZÕÕÕÕÕÕÕZZZÕZÕZZÕÕÕÕÕÕÕÕÕZZZÕÕZÕÕÕÕZZÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZZZZZZZZZZZZZZZZZZZZZÕZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJZZZJJJJJJJJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZZJJJJJJJJJØé@= |&&ÐPfv" E@@#  ˆÖ]J€çðÞàîJJJJJJJJJJJJJJJJJJZZZZZZZJJJJJJJJZJZZZZZZZZZZZZJJZJJJJJZZZJJZZZZJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJZZZJZZZJJJJJJJJJJJJJJJJJJZJZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJØé@==ò&&ÐPfv" E@@#  ˆÖÌ€çàÞàîJZJJJJJJJJJJJrJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJrJJrrrrrrrrrrrrrrrrrrrrrrrrrJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZZZZZZZZZZZZZZZZZZZZZZZZZZJJJZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕZZZZÕÕZZZZZZZZÕÕZÕÕÕÕØé@=‚b&&ÐPfv" E@@#  ˆÖ´Á€ç ÐÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZÕÕÕÕÕZÕÕZZÕÕZZÕÕZZÕÕÕÕÕÕÕÕZÕÕZZZZZZZZZZZZZZZZZZZZZÕÕÕZZÕZZZZZÕÕZZZZZZZZZØé@=¥×&&ÐPfv" E@@#  ˆÖÐ €ç !ÀÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZÕÕZZÕZZÕZZÕZÕÕÕÕZÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÅÕÅÅÕÅÕõÅZõÅJõýzåÕÅÕÅÅÕõZåZÅýÕõõÕÅõÕÅõÅÅÕõÅÅÅÅÅõÅÅåÅZýåõÕÅýõÅÅõõõÅõýÅÅýÅÅõÅÅõõÅÕÅýÅÅÅÅýõõõÅýýõõõýõõõõõõÅõõÅÅõõõÅÅõõõõÅÅõõõõÅÅõõõÅÅÅÅõõÅÅõõÅÅÅÅõõØé@=CM&&ÐPfv" E@@#  ˆÖž–€ç!"°ÞàîÅÅõõõõÅÅÅõÅÅõõÅõõÅÅõõõõõÅõõõõÅõõõõÅõõõõõõõõõõõõõõõõõÅÅÅÅÅÅÅÅÅÅÅõÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÕÕÕÕÕÕÕÕÕÕZZZZZZZZZJZZZZZJJZJJJJJJrrrrrrrrrrrrrrrzzzzzzzzzzzfffffffffffbbbbbbbbbbbbbbbbbbbbnnnbnbnnnnnnnnnjjnjjjjjjjjjjnjnnnjnjnnjjjjjnjjnjjjjjjjjjjjnjjjjnnnØé@=Â&&ÐPfv" E@@#  ˆÖíÇ€ç"# ÞàînnnnnnnnnbbbbbbbbbbbbbbbbbbfffffffzzzzzzzzzrzrJrrJrrrrJZrJZJJZZZZZZÕZZÕÕÕÕÕÕÅÅÅÅÅÅÅÅõõÕõõõõõýÅõýýýýýýýýååýåååýåååååáååááåááááááááááááííááíííííááíááíííííííííííííííííááíááííáííááááááááááááááááááááåááåååååååååååååååååýýåýååýýýýýýýýýýýýýýýýõõõõØé@=–7&&ÐPfv" E@@#  ˆÖô!€ç#$ÞàîõõõõõõõõõõõõÅÅõÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÅÅÅÅÕÕÕÕÕÕÕÕÕÕÕÕZZZZZZZZZZZZZZZZZZZZJJJJJJJJJJJJJJJJJJJJJrJrrrrrrrJrrrrrrrJJrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrJrrrJJJrJJJJõééýzbJÅýÕJrZÅÅÕZJJÕÕZJJJÕÕÕÕZÕÕÕÕZJZÕÕZZÕZZJZZJJZZJJJJZZZZØé@=‚¬&&ÐPfv" E@@#  ˆÖ¡<€ç$%€ÞàîZZZJJZZZÕÕZZJZZJJJZõáÅÅZZÅÕÕJZÅÕÕZÕÕÕZZÕÕÕÕÅÅÕÅÕÕÕZÅÅÅÅÅõÅÅÅõÅÅõÅÕÕZÕÕÕÅÅõÅÕÕÅÅÅÕZÕÅÕÕÅÅÅÅÕÕÅõÅÕõýåýÅÅýõõõõõõõõõõõÅÅÅõõÅÅõõÅõõõýåõõõõÅÅõõõýõõõÕÕZZÕÅÅÕõýÅÕÕZÕÅÅÕÕÕÕZÕÕZÕÕÕÕÕZZZJZZÕZZJZÕZJZÕZZJJJZJJJZZJJrrJJJZZZZZJJrJJJZZJrJJJJJrrrrrrrJJJJrrJØé@=Þ!&&ÐPfv" E@@#  ˆÖÞ‡€ç%&pÞàîrJJZZrrrrrrJZJrrrrzzzrZZZZJJrzzrrJZrrrrrJJzrJJJJrrrJrrrrrrrrJrJJrzrrrrrzzJrrJZJJJrrrJJJJrrJJJJrrrrJJrJJJZJrrJJJJrJJZJJJJJJJJrJJJJJJJJZZZJJZZZZJJJJZÕÕZZJJÕÕÕZZZZJJÕZZZJZÕÅJJZZZÕZZÕZZJZZZZZÕZÕÕÕÕÕÕZÕÕZZÕZÕÕÕÅÕÕZZÕÕÕZZÕÅÕZZZÕÅÅÕÕÕÕÕZZÕÕÕÕÕÕZZÕØé@=¹–&&ÐPfv" E@@#  ˆÖèÑ€ç&'`ÞàîÕÕÕÕÅÅÕÅÕÕÕÕJZZÕÅÕÕZÕÕÕJZÅÅÅÕZZÕÅÅÕÅÕÕZZJZÕZÕÕZZÕZZZÕÕZZZÕÕÕZZÅÕZZJÕÕZJJZZÕZJJZJrJJZZZZJJZÕZZZrrZZJZÕÕÕZZJZZJJJZJZÕÕZZJrJZZJZZJJJrZZZZJJZZJZJJZZrrJJZZJJJZJJJJJJrrZZJZJJJJJrrJJJrrJJZJzrZZJJJJZZJrrrJZZJrrrJZJZZJJJrrJJJZJrJZZJZrJZrrrzZÕJJZJJZJØé@=o &&ÐPfv" E@@#  ˆÖQ€ç'(PÞàîZZJZZZÕZJZÕZZJJÕÅÕJJrJÕÕZZZZZZÕÅÕÕÕZZZÅÕÕÕZÕÕÕZÅÕÅÕJZÅÕÕÕJZÕÕZÕÕÕZZÅÕZZÕZZZÕZÕÕÅÅZZÅÕZZJÕÅÕÕÕZÕÅJZÕÕÕÕÕJÕZÕZJÕÕÕÕÕÅZZZÕZÕÕÕÕõÅÕÕZÕÕZÕÕZÕÕÕÕÕÕZÕÅÅÕZÅÕÕZZÕõÅÕÕÕÅÅZÕÕZÕÕZZÕÅÅZÕÅÅÅÕZÕÅÅÕZZÕÕÕÕÅÅÅÕÅÅÅÅÕZÕÕÕZÕÅÅÅÅÕÕÕÅZJZÕÕÅÅZÕÕZÅÅÕÅÕJÕÕZZZZÅÕJÕÕÕØé@=)&&ÐPfv" E@@#  ˆÖ¿8€ç()@ÞàîÅÕZZÕÕZZZZÕÅÅZÕÅÕÅÕJÕÕZZÕÕÕZÕÕZZZÕÕZJZZÕÅÕZÕÅZZÕÕJZJJÕZJZÕÕÕZZÕZJZJÕÕZÅÕÕÕZZÕÅJZÅZZÅZZZZZZÕZZZÕÕJZZZÕZJÕÕÕÅÕÕÅÕZJJÕÕZZJZÅZZZÕÕÕZJZJZZJÕÕZJZZZZÕÕZZZÕZZJrZZÕJrZÕÅÕZJZÅÕJJJJZJJZÕÅÕZJZZZJZJrZÕJJZJZÕJZÕZÕZZZZZZJJZZZZZZZJJZZZÕZZZZZZZZZZZÕÕJZZZÕZJØé@=iö&&ÐPfv" E@@#  ˆÖÂ0€ç)*0ÞàîZÕÕÕZJZZZÅÕZZZÕÅÕÕZZÕÕZJÕÕÅõÕZÅÕÕZrJÕZZJZÅõÕZZÅÅZZZÕÕÕZÕõZÕÅZÕÕJÕÕZZJZÕÕÕÕZZÕÕZZZÕZZÕZÕÅZÕÕÕÕÕZZZZÕÕZZZZZÕZÕÕZÕZJÕÕZÕZZÕÕÕZÕÕÕÕZJZÕZJZZÕÅZJZZÕÕZZÕÕÕÕZÕÕÕÕÕÕZJJÕÕÕÕZÕÕZÕÕJÕÕZZZZÕÕZÕÕZZÕÕZZZZÕÕZZZZÕÕZZZJZZZZÕÕÕÕZÕÕJJJJZZZJZZZZJZÕJJZZZJZÕÕZJJZØé@=§l &&ÐPfv" E@@#  ˆÖö€ç*+ ÞàîZJJJJZJZZZÕÕJJZZZZrJZZÕZJJJZJrJÕZÕZJZJJZZJZrJÕZJrJZÕZrJJZZJJÕZÕÕZZZJJZJrJZÕÕJZJZÕZJZZZÕZJZÕZZJJJZÕZJJJZZJrZZZÕZJZJrZZZZZJZÕZZJJJJZZZZZZZZZÕZJZJJJJÕÕZJZZÕZZZZZZZJJZJZZZZJJJrJZZZZZZJrrJJZZJJJJJZJJJZZZJJJZZJJrJJJJJZZZJJZZZJJJZJJJJZJJZZZZJJZZJJØé@=Èâ &&ÐPfv" E@@#  ˆÖvЀç+,ÞàîJJÕZZZZZZZZZZJZZZZZZJJZZZZJZÕZZZZZZZZZZJZZZZZZZZZZJJZZZZZJZZZZZZJZZZJZZZZZZJJJJJJJZZJJZZZZJJJZZJJJJZJrJJJJJJJZJZJJJJZZJJJZZZZJJZZJJJJJZZZJJJJJJJJJJJJJZJJJJZJJZZJJJZZZJJZZJJJZZZJZZZJJJZJJJZZZZJZZZZZJZZZZZJJZZZJJJJJZZJZZZZJJZZZJJZZZZZZZZJJJJZØé@=½X &&ÐPfv" E@@#  ˆÖL˜€ç,-ÞàîJJJJZZZZZJJJZJJJJZZZZJZZZJJJZZZZZJJJZZJJJJJZZJZZZZZZJJZZJZZZZJJJJZZZZZZZZZZZZZZJZZZZZZZZZZJJZZZZZZZZZZZZZJZZZZJJZJZZZZZZZJJJZZZZZZZZZZZZZZZJJJJJZZZZJJJJJJJZZZJJZZJZZZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJZJZZZZJJJZZZZZZZZJZZZZJØé@=ïÎ &&ÐPfv" E@@#  ˆÖŒx€ç--ðÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZØé@=ÞD &&ÐPfv" E@@#  ˆÖ¡Ã€ç..àÞàîZZZZZZZZZZZZZZZZZZZZZZZZJJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZÕZÕZZZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZJJZJJJJJJJJJJJØé@=ص &&ÐPfv" E@@#  ˆÖ‡¯€ç//ÐÞàîJJJJJJJJJJJJJJJJJJJJJJJJJJJZZJZZZZZZZZZZZZÕÕÕÕÕÕÕÕÕÕÅÅÅÅÅÅÅÅõõõõõõõõõõõýýõõýýýýýýýýýååååååýåååååááááåáááááááááááááááííááííííííííííííííííííííííííííííííááááááááááááááááááååáååááááááááááááááááááåååååáååååååååýåýýýýýýýýýýõýõõõõõõõõÅÅÅÅÅÅÅÅÅÕÕÕÕØé@=+ &&ÐPfv" E@@#  ˆÖÔŠ€ç00ÀÞàîÕÕÕÕÕÕÕÕÕÕZZÕZZZZZZZZZZZZZZJJZZZZJZZZJJJJJJJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZZJJZZJZZZJZZJJZZJJJZZZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJZJJJJJJJJJJJJJJJJØé@=  &&ÐPfv" E@@#  ˆÖÖ€ç11°ÞàîrrJJJJrJJJJJJJJJJJZZJZZZZZZZZZZZZZZZZZZZZZZZJZZZJJZJJJJJJJJrrrrrzzzzzzzffffffffffffzzffzzzzzzzrrrrJJJJJJJJJJZZZZZZZJZZZZZÕÕÕÕÕÕÕÕZZJrrzffbnjjjnnbfzzrJZÕÕÅõýåááíéé•———‘‘‘—éýõõÕrrzbnbnbbnnbbbzzffzzrrrrJZJZZJrrrzzfbbbnnjjjjjnnbbfØé@=‰ &&ÐPfv" E@@#  ˆÖ/B€ç22 ÞàîffzrJZÕÅõýáíé—‘“Ÿ™™™™“éííýÕÕJzffbbfnnbbbffbfffzrrrJJZÕZJJJJJJzfffbbbnnnjnnbbbbfzrrJZÅõýåáíé•—“Ÿ›…„™‘é•íõÅÕrffffbnjnbbffbnbffzzzrrJZZJJJrJrzffbbnnjjjjjjjnnbffzrJZÕõýåíéé—‘“™›™‘•éáýÅÕrfbbbbnjjnnbfnnnbbzzfzrrJZrzfzrzfffbnnnnjjjjjjnbfzrJZÅØé@=>Š &&ÐPfv" E@@#  ˆÖ€½€ç33Þàîõåáé—‘Ÿ›„…““•áåÅJrfbbbnnnjnbbnnbbfffzzzrJZÕÕZJrrJrfffbnjjjjjjnbfzrrZÕõýáé—“Ÿ›„††›“‘•áýÅJzfnnbnjjnnbbbnnbfzzzrrJZÕÅÅÕJJZJzzfbfnjjjjjnnbbzrJZÅýáé—“™„†€ƒ„Ÿ‘íåýZzzbbfbnbbnfzfbfbfzzrJJJÕõõýýÅÕZÕZrffffbnjjjjjjjnbbfzrJZÕýá•‘Ÿ…†ƒ‚†™Ÿ•áØé@=ìÿ &&ÐPfv" E@@#  ˆÖŠÅ€ç44€ÞàîáÅJrffzfbbfbfzzffffzzrJJJÕÅõýýýýõÕÕÕJzzfffbnnnnbnnbfzrJZÕõá•‘™„‚Œ››™‘•éõÕZJJÕrzrrrÕZrrrrJZJJZÕÅýýåáááíåõõõÕZZrrzffzzfzzzJJJZÕÅýá•“™„€ƒ„››—•áÅÕZZÅÅJrJJÕÅZJrzrZZJJrJÅýýýõõýýÅÅÕJJJJrrffzzrrrrJJZÕÅõå鑟…†€›———é•íõZJZýíýZrrJÅÅJfjbrZZZrrZÅýåõØé@=æu&&ÐPfv" E@@#  ˆÖ9˜€ç55pÞàîZJzzzrrzzrJJJzfzJZÕÕÕÕõåé“…€Œ‹íjfåýZrJí™›•ZjrÕZffÕí•áJbbrõÅrjJÅÅJbjnzzzzzrÅýåýÅÕÅᓎµ‹‘ZíåýÕᛑZJååZrJõíåJnrrfnbrZJffJÕÕÕÕÕÅÅÕJÕ턉·ˆå Å‘—áZÅí‘éz Õ•—ýrbfZJbÕé—õfnnjbZýááõZJõ“‚µµ r“Ÿ‘ÅrÕáéÕ bé—ÕØé@=Éé&&ÐPfv" E@@#  ˆÖîM€ç66`ÞàînÕééõbjfzrrJÅåáåý•„޵z 4 •††‘Zfrõõfõ“ŸéznfrZÅõÕzjnzZÕÕÕÅõå选ˆ›n 5 j—“ÅrrZZbõ‘“ýZÕZzfrZÕZzbbzÕýåáá—…´‰•54Z‡€•rjfJrjfé•rzÅÕrbfrZÕZzbfJÅáé‘™€ˆ‹‡z 4 •އånnzfZééÅjrZJrrJÕÕZrzrÕ呅е‚åÙé@=ë&&ÐPfv" E@@#  ˆÖÞû€ç77PÞàî Ňƒ“ZnnffjfõíårjrÕÅÕÕÕZZZZÅ჊·µ™4 b™Œ›õbjbfnjÅééÕjfJÕZJJrrrJÅ툷µ…j6 ‘Œ‰†ånnnfýíõfJÅÕJffzJý—„Œµ´å47r‡‰ŒŸJrõõrnJZZrzzJý“†Ž··Å66Z€µ‰™JjrõýZzJZZJZõ陀‹¶µ™515•޵‚‘fÙé@=ö‘&&ÐPfv" E@@#  ˆÖ–B€ç88@ÞàînJÕÕzfJZÕÅ啟‡Œ´´Õ44 bŸŒŒ‡•rnrÕÕrnbzZý—™‡ƒ‹ŸÕnõ—“—íýõÅÕrbbrJrzffzrrJJJÕÅýåáé•‘—•ééíééíáõZrzzzzzbnjjjjnnnnnbbffzrZÅýáí••—‘‘—••éíáýõÕrzfbbbnjjjnnbbffrrZÕõýåííéé•éíááåýõÅZrzfbbnnjjjjjnbffzrJZZÕÅõýýåýååÙé@=f&&ÐPfv" E@@#  ˆÖ`~€ç990ÞàîýýýýõÅÕZJrrzfbbbnnnnnnbbbfzzrrJZZZÕÕÅÅÅÅÅÅÅÅÅÅÕÕZZZJJrrrzzzzzzzzrrrrJJJZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZZZZZZZZJJJJZZZZZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZÕÕÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZJZZZZZZZJJZZJJJJJZZJJJJJJJJJJÙé@=Y|&&ÐPfv" E@@#  ˆÖ˜{€ç:: ÞàîJJZJJZZJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJJJJJJZZJJJZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZÕÕÕÕZZZÕÕÕÕÕÕÕÕÕÕZÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÅÕÕÕÕÕÅÅÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅõÅõõõõõõõõõõõõõõõõõõõõõõýõýýýýÙé@=Lò&&ÐPfv" E@@#  ˆÖ'e€ç;;ÞàîýýåýýõõÅZZÕÕÕÕZÕÅÕÅÅõõõõõõõõõýýýýåáííééééé••••••—————‘‘‘‘‘———•———‘—•ééééééíééééíííéíááííííáááááååååååýýýýýõõõõõõZrrZÕÕÕÅõýõÅÕõýáé••—‘“‘‘‘‘—éåÅZzbjjzJZÅá•…‚މ‘nfffÅõÅÅZzfnzzfzJJznjnnnjnbfJJZÕÕÕÕÕZZJrzfbÙé@=yh&&ÐPfv" E@@#  ˆÖ¹ù€ç<<ÞàîbnjjjjnnbffzrrJJZÕÅõåååáååýõÅÕZJrzzfbbnbbbnnnnnbbbfzzrrrJZZZÕÕÕÅÅÅÅõõýååíééé••éííáåýõÅÕZJrrrrrJrrrrrrrJJJZZÕÕZÕÅÅÅÅÅõõõõõõõõýåáíé••••éííáýõÅZJrzffffffffzfffffzzzzzrrrrrJJJJJJrrrrrJZÕÅýåååááåýõÅZJzfbnjjjjnÙé@=aÞ&&ÐPfv" E@@#  ˆÖØ^€ç=<ðÞàînnbbbnbfJÅ啟™‘—‘áJfnfzzrrzfnnfbnbbjnJý—™€‰…‡™ZnnzýáõýåÅrbjnbzJZJrzzbjjnzZý‘…€ˆµŒ‘znnbrZý•éåííõrzbjfrJZÅÕÕZJrzbnbbfrZõéŸ†ŠµŒ‘JfrnfzJÅåííáååýZzzbjnnfzrJÕÕÕÕÕZJJrzzzrJÕå韇‚Ùé@=kT&&ÐPfv" E@@#  ˆÖѦ€ç>=àÞà‚‡…“õrZJfnzZZÕõýýåýõÅÅZzfffbbbfzzrJZZZZZJrrzrrrJÕý鄃…›“áõýõÕJZZZÕÅÅZÕõÅZJJrfffbbffbfzrJJJJJrrzrrJJZõá‘…‚†›™“áõõõJrrrzJÕZrJZZrrzbbbbnnnnjnbfzzzzffbbfffzJõí“„€€„Ÿ‘ýZZZzbffbfJJzzrrzffnjjjnffbnnjjjnnbzÕá“…€†™“‘éÕzzznjbbbfJrfzrÙé@=œÊ&&ÐPfv" E@@#  ˆÖGS€ç?>ÐÞàîzfbnjnbbbnnjjjnnbrÅ퟇…‘•õzzznnnfJJzzJrfbnjjjnbnnbnnbbbfJõ陆‡‘—ázbrfjjnfzffrznnbjbbbnbbbbbfrÅá“„„‘‘ázbJfjjnrzbzfnnnjbbfzzffzzJÅí“……‘—åffJnbfJznfzbjjjjnfzzrJrJZÅý韄Ÿ——Ùé@=‡;&&ÐPfv" E@@#  ˆÖýD€ç@?ÀÞàîrjZJfbJrjbrnjjjnnbzrzJZJJÕÅýá—™›••‘ýnbZjrzZnjJfjjnbzzJZZÅõõõýáé—Ÿ›“é•áznrzrffZznffjjbzrrZÅõõáéé—Ÿ„„™Ÿ“árzrnnzbbJZrrJfnjjnnfJJZÅõýåáíé—“…‡•‘áfnJfbÕzbÕÅzbrfjjnbbzJZÕÅýááí•‘“Ÿ„†™‘“éJjzzjJJfJÕJÙé@=J±&&ÐPfv" E@@#  ˆÖ¤”€çA@°ÞàîbffjjjjnnfzrJÕÅõýíéé—™„…“éZbfbnrrJZÕrzfnjnnbzrJÕõýåíé•‘Ÿ›†Ÿ‘‘áfjbjnrJZZJrznjnbzrJZÅýåáé‘™‡™—éåbbrZJzffjjjnfrJZÅåáí—Ÿ…†‡“éåJfJÕJfbnjnfrJJÅåáé“›‡™éõÕzZZfjnfzrÕýá—Ÿ…†ýJzÙé@=Ä%&&ÐPfv" E@@#  ˆÖWœ€çBA ÞàîbrZfbfzJõé‘™†ƒ™õJrbÕýZnjjnrZÕõ•›‡‚Œ„áõõbnbfÅíýrzznjfffJõåå铟›„†›éååÕbnzzzrÅÅJzzffbnnnbbbbbfzzJZÕõýåáé•••éééíáåýõÅÕJrrrzfbbbbbbbfffzrJZÕõýåíé•—‘“““‘•éíáåýõõÅÕÕZZZZZZZÕÕÕÅÅõýåáé•—‘“Ÿ™™›››™ŸÙé@=òš&&ÐPfv" E@@#  ˆÖñ·€çCBÞà“—••éíááåýýýõõýõýýýååááíé•‘‘“Ÿ™™››………›››™“‘—••ééíááááååááííííéé•——‘“Ÿ™™™™™™ŸŸŸ“‘—•ééíííááåáåååáááííééé••——‘‘““““““‘——••éééíááááááááííííééé•••————‘‘‘‘‘‘‘‘‘‘————••ééíííáááááááááááíííííéééééé••••••••éééééééííííííááíááááááááíííííÙé@=U&&ÐPfv" E@@#  ˆÖ€çDC€Þàîíííííííéííííííííéééééééééééééééííáííáéíííííííííéíééííííííííéé•••••••••éééééééééííéíííííííííáááááááíííééééééé•ééééééééíííááááííááååýýýýýýýåååååááíááááíííííáåýýýýåååýýõÅÅÅÅÅÅÅÅÕÕÕÕÕÅõýýýõÅÅÅõåáåýõÅÅÕÕÅÅÅÅÅÕÕZZZZZZZJJJJJJZZÕÕÅÕZZZÕÅÅÅÕÕÕÕZZZZZÙé@=‰…&&ÐPfv" E@@#  ˆÖjû€çEDpÞàîZZJJJJrJrrrrrrrzrrrJJZZZZZZZÕÕÕZZZZZZJJJJrrrrrzzzzzzzzzzzzzrJJJrrrJJJZJJJJJJrrrrrrrrzjnffffzzzzzzzfzzrJJrJbjzrrJJZJJrrrzfffzffzzfffffzzzzzffzzzzzrrrzzrzzzzrzfrrfffbjnbfrrzbfzzbjjbfzrJZJJJzfffbbnnnjjjnjjjnnnnfbfzZýÅý•“‘•åÅrnÙé@=œû&&ÐPfv" E@@#  ˆÖçð€çFE`Þàîjbjjnjjrý•™‚ŽŒ‚‡‘õbnzÕýí•éáåÕnjfJÅõýí‘™†Œ€™ŸÕJJJõáåÅZrbjfrJÅõýé…‚€‘Ÿ™ZZrråáýÅZrbnzrZõõýé“›ƒŸ…åjjÕZJå•áýÕÅznzJZõåå푟‡Œ››„ézbnnÅÕÕí—íåõõrjÙé@=µo&&ÐPfv" E@@#  ˆÖO€çGFPÞàîfJZõåáí•…€ŽŒ„„„ézznzÅÕý•—íååõznjnzJÕõåáé—™‡ƒŽƒ…„™õbbrÕÕåéíýýýZnjnzrZõåáé…†ŒŒ‡™›éfjnZÕÅííýõýÕbbzJÅýåí‘›‡‚™Ÿ‘JJZÕýíýõõÕfjjfrZõåá‘›‡‚Žƒ™ŸÅJZJýíåõýÅznjfrZõåá—›„€Ùé@=ßä&&ÐPfv" E@@#  ˆÖîû€çHG@Þà“ŸánzÕJÕááõõõJbjjzrJÅåᕟ…‡‚Œ†—ZbÕZJåíýõåÅrfbjbzJÕõåí鑟›„€…‘‘•õzbnzZZZõåýõõÕÕÕJbnffbbzJÕÅõýáé•‘“Ÿ›……—•íÅJfbbbbnfrZZÕÅõõõÅÕZZrzfffzzrZÅõýåáé—“Ÿ™™“•íåÅJfbnnnbfrJÕÕÕÅÅÕZJrzfbnnbzrJZÅýåáí—“Ÿ™……Ÿ‘íåÕrbjjÙé@=Z&&ÐPfv" E@@#  ˆÖ g€çIH0ÞàîbfzJÅõõÅõÅÕZrfbnnnnnfrJÕÅõåáé—“™›‡„‘åõZbjfrJÅåáýÅÕZrbjnzJÕÅõõåé‘™…†‚•ZJzbJÕÅá••ýJzfjjbJÕÅÅÅõá“„€‚ˆ´Žéjj rá•핟ŸýnbfbbJÅÅZZÅᙃˆ·¶†n5 J—…™•‘›‘bfõÅfjzÕJffZ鈵·°µý 75Z“€„“““Z zýéÙé@=ÎÖ&&ÐPfv" E@@#  ˆÖQ«€çJI ÞàîõbbznnZ•ƒµ·³²Šr 65鎎†“‘én 5jrõé•Ånjnn՗ж²¼·õ 77•Š‹ƒŸ‘íb 4 bÕá——ýbjjÕ•‡ˆ·³¿³56õ‚µŠ…•r5 nJá——åzjjrá™´±¼¼‰  15›ˆ‹Œ†…™á jÅ•—áÕrfnnZí™´°¼³…51 f›‚€‡‡€„ýÕááÅÅÅÅrjjfZý‘€‹¶²·“Ùé@=¾D &&ÐPfv" E@@#  ˆÖƒ€çKJÞàîf 7 ní—íé™…áfbbfJõýÅZZÕÕZZå“„ƒ‰‰„ýÅáÕjfjrí‘íýõõÅZbbrZÕÅýýý啟„€Œƒ‘õýåJnnrååõÅÅõõZfnfzrÕõýåé“›†ƒ‚†‘ííåzrõÅÕÕõåýZznjjnbfJÅýåí‘™‡€‚…‘éíÅjjJÕZZÅýåõZfbbjnnfJõýᕟ„ƒ‚•éåzbÙé@=+» &&ÐPfv" E@@#  ˆÖi,€çLKÞàîJJrZõýõÕrfbnjnzZÅýí‘›‡€‚€†ŸííJzáíýýååýrnfrýíéé“‡ŽŠ´ŒízJf bjá„›íõááJnbbfZåýJfZå釷²µÅZJ 06fÕjf…µƒåJÅÕ5fýýZzZýZnÕå—…¶¼²„jJ3<4ÕJz“µµ™Zzz74býíõrfrzr៎¶¾¹b090ýíõ—ˆ¶‚õj 136 Ùé@=1 &&ÐPfv" E@@#  ˆÖMö€çMKðÞàîjrJjÕ“‡‚´½¼‚ 2:?nõÕÅŸ†õ 56334nnjÕ‘†Ž·½¿µf 5?<4Å•—‘„ƒ—n 5rZrrÕýõZzrõ—„ƒµ°¼¿´•jnbz‘„…™›†€„—Zzõ铟Ÿ™…„††‡ŒŒŒ†Ÿ‘‘‘———•éé•—•ééééííáåååýõõõõÅÅÅÅõõõýåååááíéíéé•——•••éííååýõÅÅÕJrzzfffffbbbbfzÙé@=ö¦ &&ÐPfv" E@@#  ˆÖe6€çNLàÞàîzzrJJJZZZÕÕÅÅÅÅÅÅÅõõõõÅÕÕÕÕZZJrrzzzzzzzzzzrrrrrrJJJZZZZZZÕÕÕÅÅÅÕÕÅÅÕZÕÕÕÕÕÕÕZZZZJZZZJJJJJJZZZÕÕZÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕZZZZJJZZJJrzrJJJrrrrrJrrJrrrrrJJJJJJrrrrrrrzzzzrzrzzzzzzzrJJJrrzzzzzzzfzzzzzfzzfzzrrrrzzzzzrJrrrrzrrJJZJJZZJJJJZZZZZZZZJJJJJJZZZÙé@=Ý &&ÐPfv" E@@#  ˆÖáZ€çOMÐÞàîZZZZZZÕÕZZZZZÕÅõõõõõÅÅÅÅÅõÅÅÅÅÕÕÕÅÅÅõõÅÕZZÕÕÕÕÕÕÕÕÅÕZZZZZÕÕÕÕÕZÕÅÅÕÕÅÅÅõõõõõõõõõõõõýýýýååáíáååáííáåååáíáíááííéííááååííííííááåíííéééíííééééééíááííííííáííííáááííííááááííííííåååýåååååýýýåååááåååááåýõÅÅÅÅÕÅÅÕÕÕZÕõýåáíáýõÅÕJJZÅýí•••íáááíé——•éíááÙé@=“ &&ÐPfv" E@@#  ˆÖ`¡€çPNÀÞàîííéééíááåýõõÅÅÅÅÕÕÕÕÕÅõýåååáááåååååååáååååýåáååýååýÅÅÅõýõõýýýýýýýýõÅÅõÅÕÕÅÅÅÕÕÕÕÅÅõÅÕJZZrzzzzzffzzzzzrrzrzffrzffzrzfffJÅÕrfjbJÅÕrbjnffzrJZrfnbzzzrJzfzznjnffnjnfzbjnnnfrzbbfzzbfzfbfrJrrzrrrfbzÕÕfnfZÕrfrJZZZZrrJZZZJJZZZJZÅÕZZrzzJÕÅÕJJJJÙé@=” &&ÐPfv" E@@#  ˆÖßX€çQO°ÞàîJJZÅýõÅZJÕõÅÕÅÅÕÕõýååýýýáíííééíáåá•——éåýáíáåååýõÅõõõÅÅÕÅÅZJÕÅÅZrrJZZZZJrrZÅýÅÕZZÕÅÅýýÅZÕõááýÅZZÕÕÅõÕzzJÕõÅJzfrJZrzzzzJÕJffrZÕÕJrJÅõÕZZÕÅÕÅõýõÅÅÅÅÅÅÕÕZJJÕÕÕJJJZZZZZZZÕZÕÕõÅÕÅÅÅõõÅÕõýåÅÕõýõZÕõååýååáé••ííí••áåõÅýíáõÅÕÕÕÕÕJzbfrrfnnbbbfbnjbfbnjjÙé@=y &&ÐPfv" E@@#  ˆÖf€çRP ÞàînjnnnjnbfzzzrJZÕÕJJJZZJJrzrJrffbbbbffnjnbffbfzZZzJJZJZÅÕÕÕÅáéáýåáåõåíåýýéíZrrrJffrznjbfbbÕZfnrrfjnJbbzbjfrnjnjffbZýfzÅáíéå“……™„Ž‚ŽŒˆ‰µµ···µŠ¶¶·‹‹·¶µ‹Š¶´ˆ‰‹·‰Ž‹Šƒ€€ƒ€‡Ùé@=õí &&ÐPfv" E@@#  ˆÖš€çSQÞà††ƒ€€ƒƒ†‚‚†€‚€ƒ€Œƒƒƒ‡‡›…€™†€›‡™„™‘…Ÿ——““‘á—ááíéíéá—•ááí•ýááåááýýýýõõÕõõÕrÕÅZJZJÅrZfÅfJrrJJZzZrzzfrzzfbbnbnjnjnjjjjjjnjjnjjnbnnjbjjjjjÙé@=$c &&ÐPfv" E@@#  ˆÖâ_€çTR€ÞàîjjjnbfbbbbzrJZJrrrJZZZZÅõÅÅÅõõÅÕZÕÅõõÅÅõõÅõýõÅÕÅõÅÅÅÕrrJÕÕÕZJZJrrJZJJJrzzrJZZZJrzJZÕÕZZJJZÕÕZZZZZZÕÅÕÕÕÕÕÕÕÕÕÅÕÕõýõõõõõýýåååýåýýÙé@=ZØ &&ÐPfv" E@@#  ˆÖ€çUSpÞàîýýýåýýýåýõõõõõÅÅõýýõÅÅÅÅÅÅõõõõõÅÅõõýõõõýõõõõÅÅõõýýõõõõÅÅõõÅÕÕÕÕÕÕÕZZZZZZJJrrrrzzrrrrJZJZÕÕÕÅÅõõååáíé•———‘“““““—•ííåýÅÅÕJzfnjbzZýí‘™†‚‰´¶°²²Š„„•znnZýýÕõíJ jzÅ呄޶½¹º°ƒŽ‘brÅáá›Znõj  frjJfnÙé@=‡M&&ÐPfv" E@@#  ˆÖÞ€çVT`ÞàîfbÕ“™‡‹·°³‚…n fnzýéírÅÕnjbrfnnnnzõí—„Žµ±±Œ€„b jjrõí•ýÅýfjzfbznnzbbzJÅá‘„‚ˆ¶¶ƒ‡†rjbjfÅííÅÕõbnfbjfzbfrzzZÅõí„‚ˆ·‹……“nfjzýáÅJÅZjnbbfrzzJJrZõå•™†Œµ·›„ýbbnÕååÕÕõfÙé@=µÂ&&ÐPfv" E@@#  ˆÖ±l€çWUPÞàîjbffzJrzZZJÕõýí…ƒ‰´µ‡›rbfnfÅáýÕõõbjbzzrJJZZZÕÅåé‘…‚‰´µƒ†…ÕnnjbZåýÅõýrjjbzrrJZÕÕÕõõå•„‚‹·µ‚ƒ„ÕbfjbJýýÕõáZnjnrJrZÅÕÕõýý啟‡‚ˆ´ˆƒƒ›ÅzzjjfÕÕJÕåÕfbjnzrJZõýõõýõýá•“™‡‚ƒ‡…›•õÕZzbbbÙé@=O8&&ÐPfv" E@@#  ˆÖ¶î€çXV@ÞàîbnnfzfzrzbnnnbfzJZÕÅÅõýå鑟›…„›Ÿ‘—éåÅZJfnnbfrJÕÅõõåáíé—‘“Ÿ™Ÿ“‘—íåõÕJzbjjjjjnbfzJZÕÅõõýååááííééééíáåõÅZJrzfbjbjjnjjnnjjjnnnbbbfrJJJZÕÅÅõõýýýåååýýåýõõõÅÅÕÕZJrrrzffbbbfbbbffffzzzrJJJZÕÕÅÅÅÅÅÅÅÅÅÚé@=\k&&ÐPfv" E@@#  ˆÖòá€çYW0ÞàîÕÕÅÕZznrnjnjnbfzJJJJZZÕÅÅõõõJZÅJõ•éJbÅÕbfÕýÕzrfjfjbzZÕZrZZJÕÕzzZÕZJJZÅÅÅÕZÕõõZJJZZZJrJJZÕrJÕÅÕZJÕÅÕJÕõZZõõZZõÕJrJJJJrZÅõZJZÕrzZÕzzÅõZJZýåõåíõå—áÅå•áýåí•íýåíýÕõááýýíåÅÅáíýõåéíåáéíáíáá—‘éáééáééíåáéíåáåáííáááééííééíííááíéíåíéíááííááááíáÚé@=¡à&&ÐPfv" E@@#  ˆÖ^Z€çZX ÞàîåýýåååýýýýýýõõååååáíáýýåýýååååýõõÅÅÅÕÕÕÕÕZZÕÕZZZZZZZÕÅÅÅõýýååáááåýýýýýõõõõÅÕÕÕÕZZZZJrfffffffrJJJJJZZJZZZJrfnnnnjnbzzzzzzrJZÅÕÅýýýååáííááííååýÅÕÅÅÕÕÕZÕÕÕZZJrrJJJrrrJJZZZÕÅõõõýõõõõõõõõÅÅÅÅõõõõõõýõõõýõõõõýýõõõÅÅõõõõýõõõõõõýååýõõýõÅõýýýõýõÅÅÕZZÚé@=PU&&ÐPfv" E@@#  ˆÖn³€ç[YÞàîZÕÅýýýõÕÕÅÕÕÕÅÕZÕÅÅÕÕZJZZÕÅÅZZÕÕZÕÅýõõõÕÕZJrrrJJJJJrrrrrzrZZJJJJrJJJZZÕÕZZZZJJZZZZZZZJrrJZZZZZJrrJZZZZZZJJJJJZÕZJJJJrrzrrrJJrzýŸõjé•fZáõbjJõZnbZÕrnnfJZzjnzJrbnbrJrzffrJrzzrrrzzrrrrJrzzrrrrJJJnbååzjÅáÕnÅýJjbÕåõfzZJrzJÅýZnzÚé@=3Ë&&ÐPfv" E@@#  ˆÖîã€ç\ZÞàîbzÅõJbrÕÕrzbfrrzzJrrrrjJõrnfÕýZbnfJZrbzJÕÕJrJÕZJrzrJJrrrrJJJJJJJZJrJJZZrrJZÕÕZZÕZZJJÕõÅZZÕý•“Ÿ—áõÅÅýååáõZfjbbbjjnbzrJJrffzrÕõý푟Ÿ„€ƒƒ‚‚ÅjnJÕÕf bzZå—Ÿ…†µ±²¼¶Õ5 1= 훑™J ÕíåZfjjfÚé@=WA&&ÐPfv" E@@#  ˆÖ‘P€ç]ZðÞàîffzZå•“™†µ·±·• 6 r‡‘…›õJí•õnjbjzfnbJõáé‘›ƒˆµ¶°ŠZ 7õ†‘•…€›ÕråíýzjnjbfbnfJÅýá—›Œˆ´¶·™  醙헇€JJõýZffrrnjnnzzzrÅýåá•…ƒŒ‰µ·Žábjjé•å‘„‡årnfzjfZJbnrÅÕrbjnbnnfÚé@=B·&&ÐPfv" E@@#  ˆÖäz€ç^[àÞàîJÕÕõá—“™›‡ƒ‚ŒˆŽ™ÅrJzrååýá—““—áÅÅÅrbnjjbbnnfzzrrrrzbbfzffzJÅýáí•“Ÿ›…„†€›íýýÕzbjzZZÕõåáííáååýÅZJzffnjjnbzzrJJJJJrrrJJÕÅýåííí—Ÿ…„…‡†Ÿ•áýÅJfnnfzzrZÕÅõõýýýõÕZrzffnjnfrJJrrzffzzrJJZÅÕõýåáé•‘‘‘ŸŸ•åýõZzfbbffbfrJJZZZZZZJJznjnjÚé@=;-&&ÐPfv" E@@#  ˆÖn<€ç_\ÐÞàîjnbnfzzfzzzzzzzJrZZÕÕÅõýýýáááííé••——íåýõÕJzfffffbzrrrJJJJJrrzfbbnjjjnnbbfzzfrzzzrzJJrJÕZÕõõõýåýååáíé•••íåýõÅZrzzzffffzrrrrJJJrrrzfffbnnnnnnbffzrrrrrrrJJJJÕÕÕÅÅõõõýýááíéíé——•íáåýÅÕJrrrzzzrJJZZZZZZZJrrrrzfffbbffzrzrJJJZrJZZZZZÕÅÅÅÅýýýååÚé@=ã&&ÐPfv" E@@#  ˆÖuB€ç`]ÀÞàîáííé••‘—•ííåýÅÕJJJJrJJJZZÕÕÕÕÕÕZZZZJrzzzzffzrJJrJZZZZZÕÕZÕÅÅÅõÅõýýýáíííé••—‘“—éíáåõÕZJZJJJJZÕÕZZÕÕÕÕÕZZZZJJJrrzzzzrrrrrJJJZZZZZZÕÕÅÕÕÅõõýåááíéé•‘—éíáåýÅÕJJJrzrrJZZJZZZZZJJJJrrrrzzzzzzzrrzzrrrrrrrJJJZZZÕÕÅõýýåáíé••éíáåýÅÕJrzzfffzzrJJJJJJJJrrÚé@=í&&ÐPfv" E@@#  ˆÖ=€ça^°ÞàîrrrrzzzffzzrrrrrrrzrJJrJJJZZZÕÕõõååáé•——éííáýÅZJrzzfbfzrJJJZZZJJJJJJrrrrzzzzzrrrrJJJJJJJJJJZZZZÕÕÅýýåáé•—•éáááýÅZrzzzffzrrJJJZÕZJJJJJJrrrrrrrrrJJJZZZZZZZZZZZZÕÕÕÅõõýåáíé••éíååýõÕJrrrrzzrJZZZZZÕZZJJrrrzfzzzrrrrJJJJJJZZJZZZÕZZÕÅõõõåáé••éáýÅÅÕÚé@=‰&&ÐPfv" E@@#  ˆÖKâ€çb_ ÞàîJzbbfzzfzrZÕZJJZZZrzbbbbbbbffzrrrrrJJJJrrrrrrZÕÅõåé—“Ÿ™íÅZJzjnfzrJÅåáåÅZZJrbjfJJJrJJJrzzzzrZÕõí‘™‡ƒŽ†ázbnrõýåé…áJfnjfzzZÅõÅJfbbbjjbrÕõá—™†‚ŽŠŠ†J ퟟ“™„›érzýåõZÕÅZfnbbzÕåé—‘“›†‚ŒŸnr“…™‘——éÚé@=Ãþ&&ÐPfv" E@@#  ˆÖ ¿€çc`ÞàîÕbÕååÕzfzfjfrJZõá•‘Ÿ›Ž‡Jb—††íááÅnnfZýáõrbbbbnjjfZõýåí‘›„†‚õf釀›éýõÕbfrJZõåõJbnnbfffrÕåé•‘›†ŽŽ‡ZJ‘ƒ›éõÅJjJÕZZõýÅJbjjbzrrZýé‘™…Œˆ“õ›‚ƒýZJffÅÕJZõõÕzjjfrrrÕá—™›‡ƒ‘Úé@=ñs&&ÐPfv" E@@#  ˆÖ¿Õ€çda€ÞàîÅŸ€‘ÅrzbfÕZrJÕÅZfnzJZZõí“™„†ƒ€éjõ†„—ÅJrbfZJrZÅÅZznnfJÕÕõá—™‡€ŒŒ‡ýjÅ“›ŸéõÅÕzbzfzZõýÅZrrZÕÅÅõå•“Ÿ…‡„“ÅbbbnfÅåýÅÕõõÅrnnnjjbzJJZZÕÅýýååí•‘››“åJrJrbjbJÅÕZZÕÅÅZzffffbnjnfzrJÕÅýáíé•‘™…„ŸáZJZJznjÚé@=Qé&&ÐPfv" E@@#  ˆÖ·Æ€çebpÞàîbrÕÕZZÕõõÕJzzzzfbnnfzrJZÅýáí•‘™›„‡™éÅrJJznjnzZZZÕÕÅýýÅZrrrzfbnbfzrJZÅýáí•‘Ÿ›……Ÿ•ýÕJzfbjjbJZJJZÅõõÕJrrzzfnnbfzzrJÕõåáí•—“Ÿ››•ýÅZrbnjjnfrrrrZÅÅZJrrzfbnnnbffzrZÅõýáíé—‘Ÿ“éýÕZrbjjjnfzzzrZZJzzzzfbnjnnnbfzrZÅõýåí•—‘“‘íõZJzbjjbfbbfrrzffffbÚé@= _&&ÐPfv" E@@#  ˆÖ\)€çfc`ÞàînjjjjjnbfzJÕÅõýáé•—‘—íõZJrbnnnbnbfrrzffzzfbnnnnnbfzrZÕÅýáé•‘““—áÅõõzbjnbrZÅÅJrrzbjjjnbzJZÅýåí—“™…†€†“õfnjzýáååí•éõfjfZÕZZÅýýõÅÅýí•‘™„€€“ZnnÕé•íé—‘íJfÕõõÕÅýýÅÕÕÅåí—…Œ‡áfnjzá—•é‘•ÕjbZõõÅÕõõÅZJÕõåÚé@=xÓ&&ÐPfv" E@@#  ˆÖÄ*€çgdPÞàîé‘™‡ƒŒ‚ŸÅnjÕé••—“‘åzzÕõÅÕÅõÅZJJÕõåí‘›†‚ŒƒÕnÅé••‘‘ýfzÕõÅÕÅõÕJrrZÕõá—™†ƒŒ›õnJí••‘“árfZÅÅÅÅõÕJzrJZÕýí“…€‚ƒ“JjÅé•—Ÿ‘ýzzZÅõÅÅõÕrzzrJÕõí“…‚Œ™ÅnZé•—“™árfJÕÅÅÅÅÕrffzzrZý•Ÿ„‚Úé@=ªH&&ÐPfv" E@@#  ˆÖ^ý€çhe@Þàî‚“rÅ••—™‘õbfZÕÅÅÅÅZzbffzrZýé…†‚ŒŸZÕ•——›åffZÕÅÅÅÕJfbbbffrÅá‘™‡ƒŒŽ‘få‘‘‘Ÿ›“ÅjzÕÅÅÅÕZznjnbbzJõí“…†ƒŽŒ“n埓™“õzÕÅÅÕÕJznjnbzÕå•„ƒ‚•៟““Ÿ‘õfÕÅÅZJrznnfzJõíÚé@=˽&&ÐPfv" E@@#  ˆÖÍÑ€çif0Þàî“›‡ƒ„Zf陟“‘‘•ÅjnJÅõÕJrrzbjjnfrJÕå—Ÿ…†€‚ƒJjÕé——éééáõrjzZõýõÅÅÅÅÕZJZÕõýåáééé••••éíýÅÕÕÕZZJJJJZZJJrrrrrrrrrzrrzzrrrrrrrrJJJZZÕÕÕÕÅÅÅÅÅÅÅõõõõõõõõõÅÅÕÕÕZZJJJrrrrzrzzrrrrrrrJJJZZZZÕÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÕÕÕZZZZZÚé@=3&&ÐPfv" E@@#  ˆÖ¹.€çjg ÞàîJJJJJJJJJJJJZZZZZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÅÅÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZZZZZZZZZZZZZZZZZZZZJJJJJZZJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJrJJJrrJrrJrrrJJrJrrJrrrrrrJrrrrrrrrrrrrrrrJrrrrrrrrrrrrrrrJrrrrJJÚé@=/¨&&ÐPfv" E@@#  ˆÖY!€çkhÞàîJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJZJZZJJJJZJJZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÅÅÅÅÅÅÅÕÅÕÕÕÕÅÅÅÅÅÕÕÕÅÕÕÕÕÕÚé@=ß &&ÐPfv" E@@#  ˆÖìU€çliÞàîÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZZZÕÕÕZZÕZZZZZZZZZÕÕZZZZZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZJJJJZZZZZJJJZZJJZZZZZZZZZJZZZZJJJJZZJrzfzzzzzrrrrrrrJJJJJJJJJZZZZÕÕÕÅÅõõõõýýýýýýýååååååááááááåååååÚé@=…“ &&ÐPfv" E@@#  ˆÖ‚€çmiðÞàîåååýýýýýýõõýõõõõõõõõõõõõõõõýýýýýýýåååååáåååååýýõÅÕÕZZZJrrzzfbbbbbbbbbfffzrJÕÅýáé•—“™…‡†„í rÕõå—ŸŸérnzÅáéíÅnbJZJznjjnnbfrZõýí•‘™…‡„í jnrýŸ†…ízrí—éýJfnfZÕZZZJrfnbfzrJZÕõýåååáé•“™›ŸÅbbbzõ‘…›•ÅZrfjZýýÕÅýÚé@=‰ &&ÐPfv" E@@#  ˆÖ^}€çnjàÞàîýÕzjjfrrzJÕÅÅÕZZÅõÅÅõå•‘™‡ƒˆ‚åý„‚†ŸízbzZᛟéÅZrnfJÕÅýååõZJzzzzrJÕÅõýýåååýýááííé—‘“‘åzfzbjbJõáýýåýZzbnbfrJrzffbnjjnbbfrZÕÅýí‘™„ƒƒ™ÕbÕá“››—áõffõåíé•áÅzbrZÕZZJrfnnjjjbfzJÕÅýåýåååáíéé••ÕzZznjnzJÕÚé@=l &&ÐPfv" E@@#  ˆÖÚÖ€çokÐÞàîåýÕõõrbfnjjzrfzJJzzzzfbbfzfrZÕÅåé—…†€ƒ…íåÕrýí•™‘éíõznzJÕåáåååÅZzbnjnnbrZÕÅõõõÕÕÕZJZÕÅõá—›„†‡‘••ÕjnnrZõé—••‘éýÅÕznjjnjfÕÕÕõõÕÕJJrffzffrZÕÅåí•“™„†€ƒ›—‘ánjjzZå—•—Ÿ“íååJbnjnbbJÅÅÅýýÅÕÕJzbbbnnfzzJÕÅõýåáí•—“ŸÚé@=Kõ &&ÐPfv" E@@#  ˆÖÐ&€çplÀÞà›•‘•ZbzjjnfÕýýå••áååÅrzbjbbfZJZÕÕZZrzfbbbbfzrJÅõåé‘›„†‘‘“ZjfjJýýá‘‘ííéýJrfjfzfJõZJÅZfzzbjnnbbzJZÕåíé“™…†ƒ„•Ÿ•fjffÅýõé•í•íÕJrbnzzzÕÅJÕÅJfzbjjbnbfrJÕõåá•“Ÿ…†ƒ™•™ájnfjrýýå‘éí•áJrzjjfJzJõÅJÅÅzfzbjÚé@=÷k &&ÐPfv" E@@#  ˆÖÉþ€çqm°ÞàînbbfrZÕõá•—…‡€‚Ÿ—›ýnnfÕáíéŸéí•õffbbJrJõõZÕÅrbfnjnnzJÕõåé—›„‚™íýjbÕáé鑟éõåÕjfZJZõõZJJfjjnbzZÅõá•‘Ÿ…†ƒ‡í—•jjnÅí—•——ÅÕÕjjjzÕÅÕÅõÕrzfjjbzJÕõåí•“™›†ƒ™å•ýnbJå—“—•—árffnzrJÅõÕJZJbÚé@=Ü &&ÐPfv" E@@#  ˆÖÍÊ€çrn ÞàîjnjnnbrÕÕÅåí•‘Ÿ…†ƒŸýíõzrÕí‘—ííýbjfZÕZÅõÕrrrbjnjnfrrZõýåí—™…‚ŸõáÅZÅá—Ÿ‘åýÅjnbbZÅõõÅÕZrbfbjnffzrÕÅõåí•“™„áÅåjJýé‘™ŸíZZfnrJÕõõõÕJrrnjbbnfrrZÕÅõýåí—™‡ƒƒ—Õõrbå•““•JbbjbJÅõõÕÕÕznfbjnfzzJZÕÅÚé@=jR &&ÐPfv" E@@#  ˆÖÚ€çsoÞàîÅõåáí—™…†‚†ýrÕjÅ陓••õjnzJÕõýÅrrznnbbfzJZZZÅõÅÅåí•…†‚„ÅfJnõ•Ÿ™‘ååZfJZÕÅÅJbjnjnbfzJZZZZÕÕÕý푟„€€éfbnbÕí“›ŸíÅÕjbZõýõÕZzjjjjnzJJZZÅÅZÕÅõõå—Ÿ„‚„õbbbÕé‘™›ýZfbJõååÅZJfjnbbzJZÕÕÕÕÅÕÅõýá—™‡€‚„ýbbÚé@=ñÆ &&ÐPfv" E@@#  ˆÖþˆ€çtp€ÞàîbÅ鑟…åJbfZõååÅJrfjnbfJZÕÕÕÕÕÕÕÅõå韅†‚ƒ‘znjrí—“™…—ÅznJÅõåýÕJznjnnbzrZÕÕZÕÅÕÕõýá—Ÿ…€•rbnJá•‘™…“õrnzZÕõåõÕJzbbbnbzrrZÕÕÅõÅõýåí—Ÿ…†ƒ•Õrffõýå‘™“áõZrnbbbrÕÅÕÕÕÕZrzzrrzrJZÕÅÅõýååáé•‘“Ÿ—åõÅÕrfbzZÕZÕõýõÅÕÕÕÕZZÚé@=< &&ÐPfv" E@@#  ˆÖ ™€çuqpÞàîZÕZJJJZZJJZZZZZZÕÕÕÕÕZZZZZÕÕÕÕÕÅõõÅõõýýýýýõõõÅÅÕÕZZZZJJJJJJrrrrrrrrrrrrrJJJZZÕÕÕÅÅÅõýýååáííáåýõõÕZrrrrrrJZJJJJJJrrrJrrrrrrrrJJZZZZZZZZZÕÕÕZÕÕÕÕÕÅõõýåááíééáýýýÅZrrrrJrJÕÅÕÕÕÕÕZJrrzzzzzzrrrJZJZZZZJZZZZZÕÕÅÅýåáéé—“‘íýåõZfnnbbnnzÕÕZÕÅõÅZrrrfnjÚé@=?± &&ÐPfv" E@@#  ˆÖý8€çvr`ÞàînnbnbfrrzzrrrzzzzzfzzrrrZÕÅõýíé•‘Ÿ›áõýÕbjjjrÅÕZÅýýÅJrrrnjfzzzrZJzffbbnnbfzzJÕÅýåé—“Ÿ›‡™åÅýJjjfåéååé•íõJzrfjjnbrJrJÕZzffbnnjnfzrZÅõåé—‘Ÿ›‡€‚†éÅýÕjbjzí“éí—‘éýJzrfnbfrJZÅZZZJrzfbjjjjnbfzJZÕÅÅÅõåáé—“™„†€€•õåÕnfnJÚé@=L9&&ÐPfv" E@@#  ˆÖ‘T€çwsPÞàîí•í•—•íåZrrbjjnfzbrÕZJZrzrfjnjbnfrJZÕÅÅýááí—“Ÿ…‡‚„JféÅzrjJ•áÅíéõýõJrrnnznjffbzfbrznnfnjnnnfzzJZZÕõõåááé———“Ÿ™›‘íáåZrzbbzbbrJJÕÕÕÅõÕÕÕJrrznjjbbfzzzzzzrrrJZZZZÕÕÅõõýýýýååååååååååååýýýõÅÅÕÕÕZZZZJJJJJJJZJJrrrzzzzzrrrrJJJJJJJZZÚé@=°›&&ÐPfv" E@@#  ˆÖ]€çxt@ÞàîZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÅÕÅÕÅÕÕÅÕÕÕÕÕÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÕZÕÕÕZZÕÕÕÕZÕÕÕZÕÕÕÕZZÕÕÕÕÕÕZZÕÕZÕÕÕÕÕZÕÕZÕÕÕÕZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJJJJJZJJJJJJJJJJJJJJJJJJJJJJÚé@=Ø&&ÐPfv" E@@#  ˆÖ‘±€çyu0ÞàîJJJJJJJJJrrJJJJJJrJJJJrJJrJrrJJrrrrrrrJJrrrrrrrrrrrrrrrrrrrrrrrrrrrJJrrrrrrrrrrrrrJrrrrrrrrJJJJrrJJrJJJJJJJJJJJJJJJJJJJJJJJZZZZZZZZZZZZZÕZZZZZZZÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÅÅÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÅÅÅÕÕÅÅÅÛé@= D&&ÐPfv" E@@#  ˆÖ'€çzv ÞàîÅÅÅÅÅÅÅÅÕÅÅÅÅÅÅÕÕÅÅÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕZÕZZÕZZÕZÕZZZZZÕZZÕZZZÕZZZZZZZZZJZZZZZJZZZZZZZZZZJZZJZZJZJJZZJJZJJJJJJJJZJJJJJJJJJJJJJJJJJJJJJJJJJJZJJJJJJJZJJZJJJJJZJJZZJJJJJJZZJJJJJJJJZJZZZJJJJJZZJZJJJZJZJZZZJZJJJZZZJJZZZZJZZÛé@=þ¸&&ÐPfv" E@@#  ˆÖó€ç{wÞàîZJJZZZZZZZÕZJZZZZZÕZZZÕZZÕÕZÕÕZZÕÕÕZÕZÕÕÕÕÕÕÅÕÕÅZZÅÕÕÅÕZÕÕÕÕÕÕÅÕÕÅÕÕÕZÕÅÕÕÅÕÕÕZÕÅÕÕÅÕÕÅÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕZÅÕÕÕÕÕÕÕÕZÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÅÕÕÅÕÕÅÕÕõÕZÅÕÕÕÅÅÕÕÕÕÕÕÅÅÕÕÅÕÕÅÕÕÕÕZÕÕÅÕÕÕÅÕÕÕÕÅÅÕÕÅÕÅÅÕÕÅZÅÅZÅÅZÅÅÕÅÅZÕÅÕÕÕÕÅÅZÕÕÕZÕÕÕZÕZÕZÕÕÕZÕÅZZÛé@=B5&&ÐPfv" E@@#  ˆÖþ­€ç|xÞàîÕZZZZZÕZZÕZJÕÕZÕZZZZZÕZZÕZZÕZÕZZÕZZÕZZZZZZZZZZZZÕZZZZJÕZZZZZZJZJZZZZZZZZZZZZZZZZZZZZZZZZJJZZJZZZZZZZJZJJZZZZZZZJJZJJJJJZJJJrJJJZJJJJJJZJJJJJJZZJZZZZJJZJZZZZZJZZJJJrJJrrrrrrrrrrJrrrrJrJJJJJJZJZÕZZÕÕÅÅÅÅõÅõÅÅõÅÅÅÕÕZJJrrzffbbbnnnnnnbbbffzrrZÕÅÛé@=O£&&ÐPfv" E@@#  ˆÖó5€ç}xðÞàîýåáí•—‘‘““‘——éáýõÕJrzbnnjjjjnnnbffzzzzzrrzrrrrJJJZÕÅÅÅýåááíé—‘“‘‘—íáåõÕZJzbbnjjnnnbzzzzzzfzzfffbbbffzrJZÕÅõýáé—“Ÿ››››“‘éáýZJznjjnbbzrrrrJrrrzfbbbbbffzrJJZZÕÕÅÅÅÅõÅÅõõýåáíé•—••ááåÅÕZJzfffnnbbbbbnnjnjjjjjjnbbzrZÕõåé—›„‡‡„™“•áõZrbjÛé@=]&&ÐPfv" E@@#  ˆÖëE€ç~yàÞàîjjnbfzfzzffbnnjjjjnbfzrrrJJJJZZZZZZÕÕÅýåí•‘“‘‘•ááõÕZJzbnbbnbfbbffbnbnjjjjjjjjnffzrJZÕÕÅõåí—™„‡„„Ÿ‘—åõZJfnjjnjbfbfzrzzzzbnbjjjjjnfzzrZZZÕÕÕÕÕÕÕÅõåí•“Ÿ……›™‘ííýÕZJfjbbbbzzzzrJzzzfnnnjjnnnbfzrJJZZZÕÕÕÕÅÅýá•‘Ÿ…‡„›™‘éáýÅJrfnbfzfzrzzrJrzfbnnnnnnÛé@=h&&ÐPfv" E@@#  ˆÖù`€çzÐÞàîbffzJZZZÕÕÕÕÅÅÅõýáé—“™„‡„›Ÿ‘•åýõZrzzffrrzzrrrrrzffbnbbbffzzrrJJJZZZÕÕÕÅÅõåí•‘›…›Ÿ“—éíáõZJrzfzfbbffffzbbfbbbfbbffzzrJJZZZÕÅÅÕÅõåáé‘™„…™‘éíáõJJrzfzfbbffffzffffbffbbffzrrrrJZZÕÕÕÕÕÅõýí•‘›…›““—ííåÕrrzffzbnffbfzffffbbbbnbbffzzrrJJZZZZZZÕÅÛé@=]&&ÐPfv" E@@#  ˆÖÛ¸€ç€{ÀÞàîýáé‘™›™““‘•íáýZrrffffnnbbbfzfbbbnnbbnbbbfzzzrJJJZZZZZÕõýá•‘›…™“““•íáýZrrzfzfbbfbbzzfffbnbfbbbbfzzzrJJZZZZÕÕÕÅõåá•‘›„›—éíåÕrrzfzzbbfffzzfzzbbffbbfffzrrJJJZZZZÕÕÕÅõýáé—™„…Ÿ““•íáÅJzzzzzfbbbfzzzzfbbbbbffffzzzrJJZZZZZÕÕÕÅõåí•‘Ÿ›„›““—éíåÛé@=W|&&ÐPfv" E@@#  ˆÖ„™€ç|°ÞàîÕrrrzzrfbbfzzzrzffbbfffffzzrJrJZZÕÕZZÕÕÕõýáé—™„„Ÿ“—ééýJrrzzrrfbffzrrzzzfbfffffzzrrrJJZÕÕÕÕÕÕÕÅýåí—“Ÿ›„›““‘••áÕrrzzrrzfffbzrzzzzbbffbfzzrrrrJZZÕÕÕÕÕÕÕõýåé—“Ÿ›„Ÿ‘“‘••áZrzffrJzffbbzrzrrzffffffzzrrrrJJZÕÕÕÕÕZZÅõåí•‘Ÿ›…™‘‘“‘••áÕrzffrJzffbbfÛé@=ò&&ÐPfv" E@@#  ˆÖ)ý€ç‚} ÞàîzfzzffbbnnbfzzrzzzrJJZZZZJZÕÅõáé—“™›••—•ééåZfbnnfzfbbnjbbbfzfbbnjnnnfzzzzzzzrJJJJJJJZÕõáé—“Ÿ—í••ííáÅzbnjnffbbnjjnnbfbbbbnjjnnffzzzffzrrJJJrJJZÅýá•‘ŸŸ—íí•íííåJfnjjffbbbjjnnnbfbbbnjjnnbzzzzzzzrrJJJJJJJÕõåí—“Ÿ•íééíéíÅrfnjbzfffbnnbbbfffffbnnÛé@=Œb&&ÐPfv" E@@#  ˆÖÒ€çƒ~ÞàînbfrrrrrrrrJZÕZZZZÕõåí—“Ÿ™“éí•éíéáÕzfnnfzfzzbbffbfzffzfbbbbfzrrJrrrrJZZZÕÕÕÕÅýáé‘™•áéííéíõJfnjnfffzfnbbnbfffffbbnnbfzrrrzzrrrJZZZZÕÕõåí—“ŸŸ•ááíáí•áZzbjnffzzzbnbnnbfffznbzrZÕÕÕÕZZJZJZZÕÅåé“›‚‚„éZfjbffrJJõõõÅÕJfbjjbbzzzrJJJJrzzzÛé@=¨×&&ÐPfv" E@@#  ˆÖEȀ焀ÞàîzzrrJJZÅýá•“›†ƒŒ€“ÅbjbzZÅÅõýõÅÕJbnjbfzrzzrrrrrzzzfzrrrJÕÅýá•“›†ƒŒ›åfbrÅýýýåýÅÕJbnfrrrzzzrrrrzffzzzrJJÕÅõá•›†‚ŽŒ™õjzÕåáåýýÅZJfjnzrrzfbnbfzzrzzzffzzrrJZÅåé‘™†‚Ž…õJå—‘•åõÕrzbfJÕÕJfjjfzrJrzzzzzzrrrZÛé@=DM&&ÐPfv" E@@#  ˆÖ¦€ç…€pÞàîÕýí‘™‡ƒ‰á JퟓíõÕzbnrÅõÅJbjfJZZJrzzzzzzrrZÕýé‘™‡ƒŽ‰€á Z—›…ŸíÕJfnjjJõýÅrnnzZÕZJrzzzfzzzrJÕýí—›†‚Ž™r j훇…‘õrfnjjrýåõJbjzZÕÅZJrzzzzrrrZÕýí—›†‚Žˆ†õ á…†ŸárbjjjrýáåZbzZÅÅÕJzfzzzzzrJÕõá•‘Ÿ„€Ûé@=ûÁ&&ÐPfv" E@@#  ˆÖÁN€ç†`Þà• Õ™€›érnjfÅááÕbfZÅõÕJzffzffzzrZÅýí—Ÿ…ˆ‰›z n“€‚‡‘ÕnbÕáíõfnJÅõÕJzfffffffzJÕýá•‘™„€‹Œ— Å„‚…ífjrýéáZjzÕõõZrffffffbfzZÅýá•“™†‚ˆŠ‚å 5 퀃™ýbjjjÕí—íJjZõýõZzfffffffzJÕõåí鑟‡‚‰µŽ•Ûé@=ž7&&ÐPfv" E@@#  ˆÖ”¸€燂PÞàî5퀎…åfjnnnÅ•‘éZnÕýåõZzzzzzzffzJÕõýåá鑙дƒÅ jŒŽ€“Õnjnbjzá‘‘ýbzÅýõÕJzzzzfbbbzZÅõýåáé“…€Žµ‹™n 5 Z‡Œ…írjnnjÕ•“éÕjJýýõZrzfzzfbbfrZÅõýåá•„‚ˆµˆ“5õŽ›ízjjnjnõ—“éJnZýýÅZrzfzfbbbfrZÅõýýåé“…ƒ‰µˆÛé@=y¬&&ÐPfv" E@@#  ˆÖ×%€爃@Þàî5õŽ›ázjjnõ—“íJnZýåõZzffffbbbfrZÅõýõýí“…ƒ‰µ‹™ 5 Õ†ŽŒ…ízjjnÅ•“éZjZååõZzffffbbbfrZÅõõõýᕟ†ŒŠµƒõ “Ž“ÅnjjJí‘—ýnzõåýÕJzfbfbbbbzJÕÅÅÅÅýí‘…ƒŽµˆŸj 5Շޅízjjbõ—“éZjZýåõZrffffbnnfrZÕÅÅÅÅýé“„‚ˆµ‰“Ûé@=à!&&ÐPfv" E@@#  ˆÖÍK€牄0Þàî ýŽ›árjjnjzå‘íJbÕåáõZrzzzzbbbzJÕÅõõÅÅýé„‚‰µ‰ õ†‚›íJnnbjzå‘éJnÕååõÕJzzzffbbzJZÅõÅÅÅõá—™†Œ‹ŠÕ nŒ‡—ÅfnbbjÅ——õjrõåýÕJrzzfbbnfrZÕÅÅÅÕÅýé“…ƒŽµˆŸj õ†‚™íJbnbjrá“írbÕýýÅZJrzzfnnnfJÕÅÅÅÕÕõá—ŸÛé@=½–&&ÐPfv" E@@#  ˆÖ0µ€犅 ÞàµŒá •‚†‘ýzbbbÕ•Ÿ“åbzÅýõÕZJJrfbnjbrZÅÅÅÕÕÕõᕟ‡‚ˆµå ‘‚„—õznbbjÅ—Ÿ‘õnjrÕÅÕZZZZrzbjjbzZÅÅÅÕZZÅýí—Ÿ‡‚ˆŠ‡zJ…ŒƒáZfnbbzå“éJbJÕZZJJZJzbjjbrZÅÅÕZZZÅýáé“›ˆ†J fŸ‚€“åZfbbbzå‘“írjzZZZZZZrznjjfJÕÛé@=Z&&ÐPfv" E@@#  ˆÖ'W€狆ÞàîÕÕÕZÕÕõåé—Ÿ†Œ‹Œí适›éÅrfbfnõ—Ÿ‘õnjnbfzJÕõÅJbnrZÕÕZZZÕÅýåí—™Ší •€‚™áÕrfffbý‘Ÿ‘õjzrznjbJÅõÅzjfJÕZJJZÕÅõýåí‘›€‰µŒå j‘ƒ™áÕJrfff哟—ÅjjrZJbjjfÕõÅrjfJZZJrJZÅõõýá—Ÿ†µŠŸ ý‡Œ†•õJrzfnÕ•Ÿ“áffJÕrÛé@=A&&ÐPfv" E@@#  ˆÖÈ1€猇ÞàînjrÕÅZbjzZJrzzrZÅÅõýá—™µ‰‘ 凄éÅJrfbjÅ—‘åbfZÅZzjfZÕJfjjzJJrzzrZÅÅõõåé“„‚ˆµ€Z zŸ‚ƒ“åZrzfbzá““•ZnrÅÅÕzjfJZrfnbrJJrrrJZÅõõýåí‘›ƒ‰Š†r JŸ‚€—õJrfbbrá“‘éZjbJÅÅÕJbjjzrzfnnjbzJJrrrJZÕõýýýᕟ‡Œ‹— 퇂›íÛé@=yö&&ÐPfv" E@@#  ˆÖ ¡€ç‡ðÞàîÕrzffnnÅ•‘—åzjzÕÅÅZfnnzJJzbnjnzJZJrrJJÕõýýåᕟ‡‚ˆ‰Ÿj ý…‚„éÕrrffbJí—•árjfZÅÕJbnnfJJznjjbrJJrzrJZÅõýåᕟ‡ˆŽ ý›ƒ›íZrrffbrá••åJjfÕÅÕrbfJZrbjnzJJrzrJZÅõåáé‘™†ˆ— f—ƒ›ÅjbfrrbJí•íÅzjJýåÅJbbrJznjjnzrzfbfÛé@=¼k &&ÐPfv" E@@#  ˆÖäý€玈àÞàîzJZÕÅõýá•…ƒ‚†ár៙•ÅrzzfffnnJõõÅZznjfJÕÕJfnjbzJJzbjnfzrrrrJZÅýáé—™„†ƒ€Jfõ‘™‘ýJzffzzfnjfZÅÅÕrbjbrÕÕZfjjfrrznjbzzrrrrZÅýáé•‘™…‡‡‘Jnrå—‘áZfnbffzfnjfrJJrfnnfrrzbnnnbfffnjjbfzzzrJZÅýåáííé—‘™›™—ÅzzrZÅååõZrzffffffffzrrÛé@=ðà &&ÐPfv" E@@#  ˆÖN€ç‰ÐÞàîrzzffnjnbzzfbnnjjjnbfrJJZZZÅõýåíííååáé•—‘‘•áÅZÕÕÕÅõÅÕZJrzzzzzzzzzzzbnjjnbbbnjjjnbbbbffzzrrrJZÕÅõõõýýõÅýíéí•——••íåýõÕÕÅÅÅÅÕZJJrrrrrrJrzzbnjbbbbbnjjnbfzrrrrrJJJÕÅÅÅõýååááíé••—‘‘‘“‘—éíáááååýýýýýõÅÕZZÕÕÕÕÅÕZrzfrrJZZÕZZZZZÅõõõýåáááááí••—‘Ûé@=îU &&ÐPfv" E@@#  ˆÖ³i€çŠÀÞàî““ŸŸ™››……„„„‡„…›ŸŸŸ““‘‘‘‘——•ééééíåýýååååååååýååáííééíé•éé•‘‘‘‘““Ÿ™™™››…………›™™Ÿ“‘‘—••ééééííáåýõÕJZÕÕÕÕÕZZZJJZÕÅÅõõõýýõÅõåååáááííííéé••—•——••••éééíáååýõõõÅÅÅÅÕZJJrzfbffffbbbbnnbfzzzrrrJJrrJÕÕÕÕÅÅÅõõõýýåáåáááááááååýýõÅõõÅÕZJJZJrrrÛé@=Ë &&ÐPfv" E@@#  ˆÖ4²€ç‘‹°ÞàîzfnjjjjjjjjjnnnbfffffffrJJJZZZÕÕÕÅÅõýõÅõõõõõõõõÅÅÕÕÕZJJJrrzzzzffbjjjjjjjjnnbfffbfzrrrJJZZZZÕÅÅÅÅõýõõýõõõõõõõÅÕÕÕJJrzzzzzfbjjjnjjnbbffzzrrrJJZZÕÕÕÕÅÅÅÅÅÅÅõõõÅÕÕÕZJJJrrzzzfbnjjjjnnbffzzfzJJZÕÕÕÅÕÅÅõÛé@=U@ &&ÐPfv" E@@#  ˆÖüë€ç’Œ ÞàîõýååååååååååååååýýýõÅÅÕÕÕZJJrzzbnnjjjjnnbbbnbfzzzrrrJJJJZZÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕZÕZJJJrzzzfbnjjjjjnnbfffzzzrJJZZÕÕÕÕÅÅõõõýýõýýõýýýõýýýõõõõÅÕÕZZJzzzfbnjjnbfffffzrJJZZÕÕÕÅÅÅÅõõõýýýýõýýýýýýýååýýõõÅÅÕZZrzzfbnnnnnÛé@=ĵ &&ÐPfv" E@@#  ˆÖU€ç“ÞàîjnjjjjnnnnbbfzzzzzJJZZÕÅÅÅÅõõõýýýýåýýýýýýýýõýýõõõõÅÅÅÅÕJJrzzfbnnjjjjnnjnbbfffzrzrrJJZZÕÕÕÕÕÕÅõõõõõõõýýýýýõõõÅÅÅÕÕZZJrrrzffffffffffffzzzrJJJJJZÕÕÕÅÅÅÅÅõõõýýýýýýýýåýýåååýýåýýýõõýõõõÅÕÕZZJJrzzzfffbbbbbnnnnbbbbffzrrrJJZÕÕÕÅõõõýýåÛé@=(+ &&ÐPfv" E@@#  ˆÖîò€甎€ÞàîåáááááááááííááááááåáåååýýýõÅÅÅÕZZJJrzfffbbbbbnnnnnnbbbffzzzzrJJJZÕÕÕÅÅÅÅõõõýýýýýåååååååýýýõõõÅÅÅÅÕÕZZJJrzzzfffbnnnjjjjjjjjnnnnnbbfffzzzrrrJZZZZÕÕÕÕÅÅÅõõõõõýýýýýýååýåýýååýåýýýýýýõõõõõÅÅÅõÅÅÅÅÅÅÕÅÅÅõõõõõõýýõýýýýýýåååååååýýýýýýõõõÅÅÕÕÕÕZZZZJÛé@=  &&ÐPfv" E@@#  ˆÖÌ€ç•pÞàîZJJrJJJrJrrrrrrzrrzzzrrrrrrrrJJJJJZZZZZZZZZZZZZZZZZZJJJJJJJrrrrrrzzzzzzffzffzzzzzzzzffffffffbbbbbbbbbbbbfffzrrJJJJZZZZÕÕÕÅÅÅÅÅõõõõõÅÅÅÅõÅÅÅÕÅÅÕÕÕZZZZZZJJJJJJJJrJJJZZZZZZÕÕÕÕÅÅõõõõõýýõõõõýõõõõõýõõÅÅÅÅÅÅÕÕÕZZZZJJJJrrrrrrrrzzzffzfzffffffffzfffÛé@=‰ &&ÐPfv" E@@#  ˆÖç–`ÞàîzzzzzzzzzzzzzzzrrrrrrJrrrJJrJJJJJrrJJrrJJJrJJJrrrJrrrrrrrrrrrrrrrrrrrzrzzzzrrzrrrzzrrrrrJJrrJJJZZJZZÕZZÕÕÕÕÅÅÅõõÅõõýõõýýõýýýýýýýýýåýýýýýýýýýýýýýýõýýýõõýõõýýýýýýýõõõõýõõõõõõÅÅÅÅÅÅÅÅÅÕÕÕÕÕÕÕZZÅÅJJZÕÅZÕZZÕÕÕZZZÕÕÕÕÕZZÕÅÕÕÕÕÅÕÕZÕÕÕÅÕÕÕÕÕÕÕÕÕÕÅÅÛé@=üŠ &&ÐPfv" E@@#  ˆÖ<؀痑PÞàîÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅZÕÕÕÅÕÕZÕÕÕÕZZZÕÕÕÕÕZZÕÕZZZZÕÕZZZZZZZJZZZJJJJJJJJJJrrrrrrrrrrrzrrrrrrrJJrrJJJJJJZJJZZZZZÕÕÕÕÕÕÕÕÕÅÕÅÅÅÅÅÅÅÅÅÅõÅõÅõõÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÅÕÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅõõÅÅÛé@=î&&ÐPfv" E@@#  ˆÖ…€瘒@ÞàîÅõõõõõõõõõõõõõõõõõõõõõÅõõõÅÅõõõÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÕÅÅÅÅÅÕÅÅÅÅÅÕÅÅÅÕÅÅÅÕÕÅÅÕÕÅÅÅÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÅÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕZZÕZZZZZÕZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÛé@=Ýv&&ÐPfv" E@@#  ˆÖdš€癓0ÞàîZZZZZZZZZZZZZÕÕZÕÕZZZZZÕÕZÕÕZZÕZÕÕZZZZZZZÕZZZÕÕÕÕÕÕÕÕZZÕÕÕÕZZZÕÕZZZÕÕZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÛé@=Áì&&ÐPfv" E@@#  ˆÖ¡V€çš” ÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÜé@=x &&ÐPfv" E@@#  ˆÖ~N€盕ÞàîZZZZZZZZZZZZZZÕZZZZZZZZÕZZZZZZZZZZÕÕÕÕÕZZÕZZÕZZZZZÕÕÕZZZÕÕZZZÕZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZÕZZZZZZZZZZZZZÕÕZZZZZZZZZÕZZZZZZÕZZZZÕÕÕZZÕZÕÕÕZÕÕÕÕZZÕÕZZÕÕÕÕZZÕÕÕÕZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÜé@=‹–&&ÐPfv" E@@#  ˆÖ]2€眖ÞàîZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZÕZZÕZZZZZZZZZZÕÕZZÕÕZÕÕZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕÕZZZÕZÕÕÕÕÕZZZZZZÕZÕÕZÕZZÕÕZZZZZZZZZZÕZZZZZÕÕÕÕZZÕZZZZZÕZZZZZÕZZÕÕÕZZZZZZZZZÕÕÕÕÕÕZZÕZZÕÕÕÕÕÕZÕÕZZÕÕÕÕÕZZZZZZÜé@=‚&&ÐPfv" E@@#  ˆÖdÏ€ç–ðÞàîZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZZÕÕZÕÕÕÕZZZÕÕÕZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZÕÕÕÕÕÕÕZZZÕZZÕÕÕZÕÕÕÕZZZÕÕZZÕZZZZZÕÕÕZZZZZZZÕZZÕÕÕÕÕZZZÕZZZZZZZZZZZÕZZZZZÜé@=T|&&ÐPfv" E@@#  ˆÖF;€çž—àÞàîZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕZZZZZZZZÕZZZZZZZZZZZZZZZZÕÕÕÕZZZZZZZZZZÕZZÕÕZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕÕÕZZZZZZZÕÕZZZZZZZZZZÜé@=hñ&&ÐPfv" E@@#  ˆÖ‹›€矘ÐÞàîZZZZZZZÕÕÕÕZÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕZÕÕÕÕZÕÕÕÕZÕZÕÕÕZZÕZZÕZZÕÕZZZZÕÕÕÕÕZZZZZZZZZZZZZZZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZJZZZZZZZZZZZZZZZZZZJJZZZZZZZZÕZZZZÕÕZZZÕÕZZZÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕZZÕÕÕÕÕZZZZÕÕÕÕÕZZÕÕÕÕÕZZÕÕÕÕÕÕÕZZZÕÕZZZZZZZZÕZZÜé@=÷f&&ÐPfv" E@@#  ˆÖ.¨€ç ™ÀÞàîZZÕZÕÕÕÕÕÕZZÕZZZZZZZZZÕZZZZZZZZZZZZZZZZÕÕÕÕZZZÕÕZZÕÕZZZZZZZZZZZÕZZÕÕÕZZZZÕÕÕÕÕÕZÕÕÕÕÕÕÕZZZÕÕÕÕÕÕÕZÕÕÕZÕÕZZZZZZÕÕZZÕÕZZÕÕZZÕÕZZZZZÕÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZÕZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZZZÜé@=ÀÛ&&ÐPfv" E@@#  ˆÖø=€硚°ÞàîZZÕÕÕÕZZZZZZZÕÕÕZZÕÕÕÕÕÕÕÕZZZÕÕÕZZÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZZÕZÕZÕÕÕÕZÕÕÕÕÕÕÕZÕZZÕÕÕÕZZÕÕZZZZÕÕÕZZZZÕÕÕÕZZZÕÕÕZZZÕZZZZZÕÕZZZZZZZZZZÕÕZZÕÕÕÕÕZÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÕÕZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÜé@=QQ&&ÐPfv" E@@#  ˆÖZð€碛 ÞàîÕÕÕZZÕZZZZZÕZZZZÕÕÕÕÕÕÕZZZZÕÕZÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÅÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÕÕÕÕÅÕÕÕÕÕÅÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕZZZÕZZÕZZZÕÕZZÕZZZZZÕÕZZZZÕÕÕZÕÕÕÕÕÕZZZZZZÕZZZZÕZZZZÕÕÕÕZZZÕÕZZÕZZZZZÕÕÕZZZZZZÜé@=…É&&ÐPfv" E@@#  ˆÖj¿€磜ÞàîZZÕÕZÕÕÕÕZÕÕÕÕÕZZÕÕZÕZZÕÕZZZÕZÕZZZZZZZZÕZÕÕÕÕZZZZZÕÕZJZZZZJZÕÕÕZÕZZÕZZZZZZZZZZZÕZZÕJJZJJZZZZZZZZZJZZZÕZZJJZZJJJZJZJJZZZZZZJJJJZZZJZÕÕÕZJJZZZJJZZJJJJZZZZZZZZZÕJZZZZZZZZZZZZZÕÕÕJJZÕZJJÕÅÕZJJZÕZÕZZZÕÕJZZZZÕZJZZÕZÕZJZÕÕÕZZÕZÕZZZÕÕZZZÕÅÕZJÕÕZZZÕÜé@=s;&&ÐPfv" E@@#  ˆÖÚ€礀ÞàîÕÕZZÕÕÕZZZZÕÕZZÕÕÕÕÕÕÕÕÕZÕÕÕÕZZÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕZÕÕÕÕÕÕZÕÕÕÅÕÕÅÅÅZÕÕÅÕÕZZÅÅZZZÕÅÕZÕÅÅÕÕÕÕZÕÕÅÅÅÅÕÕÕZÕÅÕÕZÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅZZZÕÕÕZZZÕÕJZÕÅÕÕZÕÕÕÕJÕÕZZZÕZJÕÕÕZZZZZÕÕÕZÕÕZÕZJZÅÕJZZZÕJZJZÕZZÕZÕÕÕZZZZZZJJZÕZZÕJZZZJÕZZZJÕZZJJZZZJÕÕÕZJÕZrJÕÅJJZZÕZÜé@=²&&ÐPfv" E@@#  ˆÖ@þ€神pÞàîZÕÅÕZZZZZÕJZÅÕÕZJÕÕJZZÕÕZZrZÅÕZZÕÕÕÅZJÕÕÅÕZÅõÕZÕZÕZrZÕZZÅÅÕÕZÕÅÅZZÕÕÅÅÕZÅÕÕZÕÕZÅZZZÕÕJJZõÅÕZÕÅÕÕÕZÕZÅÅÕJZõÕJÅÅZÅÅZZÕZÕÅÕZZZÕÅÅÕÕÅÕZÕZZJZÅÕÕJZÕÅZÕÕÅÅÅJrÕÅõÕÕÅÅÕZZZÕÅÅÅÕÅÅÅÅÅZJÅÅZZZJÕÅÅÕÅýõZZÅýÅJÕõýÅZÕõõÅÅÕõÕÅÕÕõÅÕZÕÕõJrÕýýÅJZõåÅzJÅýÅJZÕÅõõÕrÜé@=(&&ÐPfv" E@@#  ˆÖS€禟`ÞàîÕÅýÅJJÕÅÅÕZÕÕÅÅÅZZÕÕÅZZÕÅÅÕZÕÕÅÅÕÅõÅZÕÅÅZÕÕZÕÕÕÅÅÕZÕÕõÕJÕÅÕÕJÅõÕÕÅÅÕÕÕÕÕÅÕZÕõõÕZÕõÅÕJÕÕÅÅJZÕZJÕZÅÕZÕÅÕJÕÅõZJJÕÅZJZõõýÕrJÅÕÕrzÅÅÕZrZÅõÕZJÕÕÕJJÕÕZZZrZÅÕZZÕJrZJZZrJJJJZJZÕÕZJrJZrrrJZrZJJÕZJJJZJrzJZJzzJZJrJÕÕJzzZZrzrJJJrZÕZZJrZZrrJZJrrrJJJrrrJrÜé@=ž&&ÐPfv" E@@#  ˆÖŒW€ç§ PÞàîrJJJZJJrJJJzrJzJrrJrrJJrrfrJZrrrJZZrfrÕJffrZJzzzJJrrzzrrzzzrrzzrrrrrfzrrrzrrzzrzzJzzzzzzfzzzfbfffrfbffzfzzrrzzrzfzzffffrzffffzzfzfzzbfffbzzzzzzzzzfffzfzzzfzfzzzfzrzffzfrzzzrzffzzzzzzzzzzzzrrzzrzrfzzzrzzzzzrrzrrJrzzzrrrzzrJrrzzrrrrrzzrrrrrrJÜé@=f&&ÐPfv" E@@#  ˆÖ*Ò€稡@ÞàîJrrrrJJrJrrrrrJrJJrrrJJJJJJJJJrrrJJJJrrJJJJJrrJJJJJJJJJZZÕJJJZZJJJJZZJJJJZZZZZJZZJZZZZZZZZZÕZZZZZZZJZZZZZZZZJZZZÕZZZZZZZZZZZZZZJZZZZZÕZZZJJJZJJZZZZJJZZZJJJJJJJJJZZZJJJJZJJZJJJJJJrJJJJJJJZZJJJZZZJJJJJJJJJJJJJJJJJJJJrJJJJJJJJJJJJJJJJJJJJJJJJJÜé@=Š…&&ÐPfv" E@@#  ˆÖL¼€ç©¢0ÞàîZZJJJJJJJJJJJJJZJJZZZZJZZZZZZZZZZZZZZZZZZÕZZZZZZZZZÕÕÕZZÕÕÕÕÕZZÕÕÕÕÕZÕÕÕÕZZÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÅÅÕÕÕÕÕÅÕÅÅÕÕÅÅÕÕÅÕÕÅÕÕÕÕÕÕÅÅÅÅÕÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÅÅÅÅÕÕÕÕÅÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÜé@= û&&ÐPfv" E@@#  ˆÖT©€窣 ÞàîÕÕÕÕÕZZÕÕZZZZZZZZZZZÕÕÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÅÅÅÅÅÅÅÅÅÅÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÅÕÅÅÕÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÕÕÅÅÅÕÕÅÅÅÅÕÅÕÕÕÅÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÜé@=´o&&ÐPfv" E@@#  ˆÖïH€竤ÞàîÕÕÅÕÕÅÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÅÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕZZZÕZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÕÕÕÕÕÕÕZZZZZZZZZZZZZZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕZbjÅáýÕrrÕÅÕZrZÕZZZJJZZZJZZJZZZZZZÕÕÕZÕÕÕrfrÅÅÅÕZÕÅZZZZZZrrZJrJfzrzzzfznnfZJÜé@=éä&&ÐPfv" E@@#  ˆÖȦ€笥ÞàîbZÅrrrfzrzzÕõÅõÕZÕbzffzzZÕõJZÕZfbjnzbrJZZZåJJÅzrÕÕrÕõJÕýZrÅÅZÕýÕÕýýZÕÕZÕÅZÕÕÅýõÅýõÅýÕÕÅýÅõýZÅÅÅÅÅÅÕÕõýõÅÅõõÕýõÅÕõýÕÕýýZÅåJJÅzrÕZJJZÕZÅÕrZÅÕrõÕzÕZJJZZJZÅÕÕÕZZÅÕZÕÕÕÕÕÅÅÕZZZÕÕÕÕÕÅÕZÅÕZÅõZÕýÕÕýõÕåýÕýýõýýõÅýõÅÅÅýÅÕáýÕåýÅõýõõýåÅõåýõáýõíåýáåýáåÜé@=„Z&&ÐPfv" E@@#  ˆÖÖ¢€ç­¥ðÞàîýýååõýýýýáýõrZáJJýZråýJõåÕÅýõõõýÅõýõõõõýõõýõýåýýýýýõýÅÅýõÅÅÅÅÅÕÕÅÕÕÕZZJZZrJZrrJZJJJrrJrJZJrZÕJÕõÅÅýåýåáááíééíééíííáýýÅZrzbjjnnnfffrJZÅõõåéé‘™™‡ƒ™—†—ÕÕZjzfnzZnjzbnffzZÕZÅýýõíé•‘Ÿ›„„雇ýõýÅfnjÜé@=oÖ&&ÐPfv" E@@#  ˆÖÜó€箦àÞàînrnnZJnzZbbbjbfzJZÕÅõåááí—‘“™‡†Ÿ“ƒåíéJfZjnnfbjfÕffZJbbrbjnjjnbffzrrZÕÅÅõåíé•“›†‚‰›—Œ›zfáZnjjõõJÅýåÅJfzbnbfzJJrJZZJÕýáá•›‡‚ˆµ‹†µ‘fZé rJéZÕá—éÅÕõZbrzzZZZZZZÕõýí—…†ˆ´´ƒ†‹†Üé@=«D &&ÐPfv" E@@#  ˆÖŒ¶€篧ÐÞàîõÕýznzåáåå—“åÕõÕjjnfrJZJZÕÕÕÅÅýá镟…‡‚ŽŠ´‰…éÅõJjzJÕýååéáÅÅZfnnzrJÕÕÅÅÅõõÅýááé—“Ÿ›‡ƒ„††“íáÅzbnzzJÕõõÅZÕZfnnnnfJÕÅõõÅõõÅõõõýááéé—“Ÿ™›‡‡›…™‘íáÅZrbjjnfbzrrJrzrznnjjjjjnbfzrJZÜé@=§¹ &&ÐPfv" E@@#  ˆÖ絀簨ÀÞàîÕÕÕÅÅÅõõõõýýýááíéé•——‘‘“‘—•éáýõÅZrzfbbnbbbfzzzzzzzfbbbbbbbbfzzrrJZZZÕÕÕÕÕÕÅÅÅÅÅÅõõõõõýýýýååååáåååååýõýõÅÅÕÕÕZZZJJrrrrzrzzzzzzzzzrrrrrrJJZZZZZZZZZÕÕÕÕZÕZÕÕÕÕZÕÕÕÕÕÕÕÕÕZÕZZZZZZZZZZZZZJJZJJZJJJJJJJJJZZJZJJZZJJZZZZZZZJZZZZZJZZZZZZZZZZZJJZZZÕZZÜé@=./ &&ÐPfv" E@@#  ˆÖê1€籩°ÞàîZZZJJZZZZZZZZZZZZÕZZZZÕÕZZZZZZZJZÕÕZZÕZZZZÕÕÕÕÕÕZÕÕÕÕÕÕÕÅÕÕÕZZÕJJZZZZZZJZZZZJJZJJZJJJZZZJJZZZZZZZZJZZZJZZZZJZJJÕZJÕZZZJZZZZZÕÕZÕÕZZZZZZJJJZJZZJZZJJJÕJrZZJJZZJÕZZÕÕZZZZÕÕZÕÕZZZZZZZJZJJZJrJrJZZJJJJJJrJZrJJrrrrJJrrJJJZrzJJJJJzzzfzJzrrzJJzzJrrzÜé@=ü£ &&ÐPfv" E@@#  ˆÖ>3€粪 ÞàîrzrzrJrrrJZZZzZÕJrÕZJJÕÅõJÕÅÅÕZÕZÅÕJÕÅJÅÕZZÅZÕÕZZÅÅÅÕÅÕÕÅÅÕÅJÕåõÕõÅÅõÅÅýõÅõåÅÅÅÕÅÅÕÕÅýõÕåýõõÅýýýýÅÅõåÅÕåýõýõåíõååýååýõååÕõýáýýÕ•áýõíýÕÅåÕáåÕýáýõÅááÅõáíõõåáõåÅýíýýýýåáýÕííõZåíåõÅý•ZJõáÅÕZõéýJåýÅåÅJ‘ázýáÕõõJéÅÕÅõÕýÕJZéZÕZrbfzÕZýåáõÅõýrJýÕJõJýÜé@=+ &&ÐPfv" E@@#  ˆÖg;€糫Þàîírý•õÕýýÕZZzzbbÕZnJÅÅÕZÅÅzfÅÕrrZJÕJJÅõõýýýõÅýýÅÅÅÅõÅÅÅõÅÕÅZÕõÅÕÅÕZZZZZZZZJJZÕZJZÅÅZZÅõõZÕõõÅÕõáááåíéííéé—“‘‘‘—éáõÕJznjbzJÕýí‘™‡€‰µµµµ‹— 1  •‹‡—áån74fý„›ån fbfrfjzrrrÕå—Ÿ›„‚‹µµµí1?J‰³ˆÕ 5Üé@=‡Ž &&ÐPfv" E@@#  ˆÖҀ紬€ÞàîZ†Œ™éíÅfáíééÕ fJÕznjbzZÅÅ჋·¶¶´ ::5í´±126J›†‹µ…b44rí‘…ƒ€ÕjZáíýZnbJÕZJJJrrÕå•™ƒ‰´·´ˆZ2%2—‹´Œ›…n523勵‘áJ 5õ‡—zzõýÕjnbbjbzzzzJÅá•™ƒˆ‰›?< õ´‚ýJÕn ZƒŒŸZnjÕ—™ŸåjbrZÕZÕZÜé@=K &&ÐPfv" E@@#  ˆÖKx€çµ­pÞàîfzõÅrbnbfzfzJõýýýÅÅýí•™‰µ´µ†16‘µ±Ž•ÅÅr펇ábnÕõÕÅá“„‡“ÕbbZá—•íééÅbퟗõzzzZÕZJÅáé•éáýýá•‡Š´´Š“1 n†·¶†õrrfjb“‚ƒÕbÅõÕÅå—›™åfjJá•íõõÅnJé•ÅbjbffbnfZýåýõÕJZõ逋Ћޑ04ŸŠµ…Jnfbý„…ézjrfbÕé—ZzZÕJrZÅz5záýfÜé@=;z &&ÐPfv" E@@#  ˆÖìv€ç¶®`ÞàîznZÕrfzzfJý—„‰ˆ‚é26‘‚ébzÕf ý™—Jnjý—•ÅnnfnfÕýzffjjnbfrrffzÕဉ‰Žƒí65“†™åÅáízjá•õzrÕJÕíáõÅõõZnzÅÅrjznbjnnnfrzffJÕÅៀ‰ˆŽ„Z5õ“•í铟årJrZá—ånnzrrÕé“•ÅfbbbjbJJbnbbjnbjjnffbbbzJJÕÜé@=ð &&ÐPfv" E@@#  ˆÖ;¦€ç·¯PÞàîå—Ÿ†‰ˆ‹‰›nnÅÅÅ•„†‘rjZé•ýZZÕrbÅåáåááýJbjjjjnrJJzbnjjnnbfzzzzzJÕý•™„ƒŽˆˆˆ†Õjnzí›™éõõÕbbffJõåõrfrrfbzÕÅÅÕÕJzfnjnnnbbbbbjjnnnbfzzzJZÕõå鑟…‡ƒ†“åõÅJzrÕõõÕÕÅýÅZrzzffbbbbbfzrJrrJJJrrrrzffffbbbbbbnnnbbbfffzzJZõá•“›„‚‚†Ÿ“•åõÅZrÜé@=Sf &&ÐPfv" E@@#  ˆÖA0€縰@ÞàîzzrJJrrrzzzfbnnjnfffzrrJJJZJrrJrzzfbbbbbbbbbffzrJÕõåé‘™„‚Œ‚…“íýýõJrJJJÕZJZJrrrfbbnnbffzrzrJZZZZZJJrrzzffffzzzzrrJÕõåí—…‡ƒŒ‡Ÿ“—áåýÕJJJZÕÕZJZJJJrfbbnbbffffzrrJJJrrrzzffbbbbfffzrZÅýí•“™‡€Œ†Ÿ‘•åõõZzfzzJZJrrrzrzbnnjjnnnnnbfzzzzfffbbnnnjnnbfÜé@=ûÛ &&ÐPfv" E@@#  ˆÖñû€ç¹±0ÞàîfrZõå•“™‡€ƒ…“—íÅÅÅrbfffzrzzzzfzfnjjjnjnnnbfzzffbbnnnjjjjnbfrZõá—…‚…“éåýÅZJzbffzrzJffzznnjnnnfffffbbnnjjnnnfrZõí‘™†‚†™õýÕbfzbbbZÕZÅõJJJrfnnjnnnbfffzrrzzfbbnnbbbfrZÅá—Ÿ„ƒ‚‡†íõõrbrfrbJÅÅÅåÅZJJzbjnjnjbfzzrJJrrrfzfffzrJZõá—™†Üé@=®`&&ÐPfv" E@@#  ˆÖ8Ê€纲 ÞàÅÕZnbbfzfÕýÅõýÕJrzfjjjjnzzrrJJJrJrzzzzrZÅýá—Ÿ‡‡„›ÅrJnjnbJrZåáõýõJzfbjnfzrJJJJZJJJrrJZÕýá•…Œ‚Ÿ…‘fnzjnzÕÅÅéíÅõÅrbjfrrJJJJrJJJJZZÅýá—Ÿ„€Œ€‘énbnrõýÅá•ÅrJfbbbJZrrJJrrJÕZÕýáí•…†ƒ†•éíbnnfJåíõõíÅbnbjjrrfzÜé@=ÆÂ&&ÐPfv" E@@#  ˆÖŽ!€绳ÞàîZrzzZZJÕýááé‘›†ƒ•åárbfzZá—áÕåýbbbbzZJzrJrzJÕÅõåé—‘“›‡€ƒõåÅjzJJõ•—ÅJÅJjzrfrJzfzzrJZõíé—Ÿ›‡ƒŒéÅÕfjZÕÅí—ÅJZbbrJrrJzffzrZÕõí•—™›†ƒƒÕZÕJýõý•“åzffjjzÕJffzfbbJÅõå•›„€‰†áJZnjÅåý陓ÅzfjjjnJÕrfffbnnrÕÕõéÜé@=g8&&ÐPfv" E@@#  ˆÖ"S€ç¼´Þà›™õrJbõýõí“éJnnjbJZfjnbnjfZõýá“›‡€Œ‰åzzjÕáåáõbjjjzZrbjnbjfÕÅõí“™…†‰ƒírzbZáåå‘ýbjjfZrnjnjfÕõý韇€€‹…Jbfåéýé™—JjnjrJzjjjrÕÕý韄ƒ‰Š‡Åffýéå陑ÕnjzJfjzÕÅå‘…€‚´µ“zbnz•Ýé@=kk&&ÐPfv" E@@#  ˆÖ¡¶€ç½´ðÞàîéå„ézjjrrnrZZý‘„ƒ´·†rbrné•ý•‡rnjzrjfrrÅ陇µ´ŠŸrnnríéá‘›ébjfbfrrõ‘…‡Šµ‹‡Jnffá—á陕fjbnbrrÅ•Ÿ…‡ƒ‰‹ˆŠ†zjrr•‘å៕njbfzrý‘Ÿ…‚Žˆ‹µZÕbzé“áõŸ“fnÝé@=©à&&ÐPfv" E@@#  ˆÖù7€ç¾µàÞàînzfõ—™†ˆ™Ž‚nõzjfzé…ýf—íjjbnrfZíé‘™…Ž‹µ€ÅzÅzÕ•‘åå‘éznfjjjfõí핟„‚޵·‹áZárá™åÅ›™ÅfbjfÅí•‘Ÿ†‚Œ‹·¶„JÅÅÕ—•ý•™åzrzjrÅå镟„†‚‹´µ™õåZnZááå‘“ýJÝé@=`U&&ÐPfv" E@@#  ˆÖø€ç¿¶ÐÞàîZrjjjjjZÅå•—Ÿ‡‚ˆµˆýõzbÕíéí“‘åZZfjjjnjjnJÅå•…†ƒŒˆŠ„ÅJJjZé“••íJzffbbjjnbfZõáé—™†€ˆŽÕZrÕííá—“åJrzjbnjnbnjjbrZõí—Ÿ…†‰‰™Õrfrá•í—ŸíZzfbfnzZfjjjzzfZÅÅýååáé•“™„†›ÕfJbbÕåõÝé@=\Ê&&ÐPfv" E@@#  ˆÖ(â€çÀ·ÀÞàîÅ•éJbznjbjjrrbnbnbnnzZÕõíé—‘“ŸízbfjjrõåõýáõfnnnzffJrfbbnbffJÅõåé•‘“‘‘“—õzzfnnbJýýõåáÅrfnjnrrrÕõÅÕZrrzfzJZÕõýýýõÅÕZrrrJJZZÕÕÕJJJzzzzzrrJZÕÅÅÅÅÅÅÅÅõõýýýååýýõÅÕJrrrJZÕÅÕJfnnrÕá—Ÿ™Ÿ•õZrfjnrÅýåí—•áÅZznnbzZõåååýýÅJrrrrJZÅýýýýýÝé@=@&&ÐPfv" E@@#  ˆÖQ“€çÁ¸°ÞàîÅZJrrrrJÕÅõõõõÅÕZJJrrJZÕÅõõõÅÅÕZJrJJZÕÕÅõõõõÅÕZZZZZZÕÅÅÅÅÅÕZJrrrrJZÕÅõõÅÅÅÕZZJJZZÕÅÅÅÅÕZJrzzzrJZÕÅõõõÅÕZJJrJJZÕÅÅÅÕZJrzffzrJÕõõõõÅÕZrrrrJZÕÅÕÕÕZrzffzzrZÅõõõõÅÕJrrrrJZZZÕÕZZJrrzzrJJJZÕÅÅÅÅÕZZJJZZÕÕZZÕÕZrzrrzzJZZZZÕÅÕÕZÕZJJZÕÅÅÕZÕÕJrzrJrzrZÕZÝé@=È´&&ÐPfv" E@@#  ˆÖ,€ç¹ ÞàîZZÕÕZJJZJJJZÕÕÅõõÅZJJrzffrJJJZÅÅÕÕÕÕZJJJZZZÅÅÕZZZZJrrrrrrJZZZZZZZJJJJJJJZZZZZÕÕZZZZZJJJZZZZZZZZZZZZZZZZJZZZZZZZZZZZJJrrzzrrrJZZÕÕÕÕÕZZZJJZZÕZJJJJrrrrrzzrJrrrJZZJJZZZZZZZZZÕÕÕZJJJrzzrrrrrJJJJJZÕZZZÕÕZZZÕÕZZZJJJrrJJrrrJJrrrrJJJJZZZZZÕÕÕÕÕÅÕÕÕÝé@=%*&&ÐPfv" E@@#  ˆÖ”¶€çúÞàîÕZZZJJJJrJJJJJJJZZÕÕÅÅÅÅÅÅÅÕÕÅÅÕÕZZZZJJJZZJJJZZZZÕÕÕÅÅõÅÅÅÅõõÅÅÅÅÅÕZÕÕZZZZZZZZZÕÕÕÕÅÅÅÕÅõÅÅÅÅÅÅÅõÅÕÅÅZZÕÕZJJÕÕJJÕÕZZÕÅÅÅÕÅõÅÅõÅÅõõÅÕÅÅÕÅÅÕÕÅÕÕÕÅÅÅÅÅÅÅÅÅÅõÅÅÕõÅÅÅÅÅÅÅÅÅÅõÕZÅÕÕÕÕÕõÅÕÕÅÕÕÕÕÅÕÅZÕÕÅÕÕÕÅõÕÕÅÕÕÅZÕÕZJJÕZZÕZJÕZZZZrÕZÅJZÕZZZJZÕZZZZZJÝé@=(Ÿ&&ÐPfv" E@@#  ˆÖÃf€çÄ»€ÞàîZZÕZÕJÅÕÕJÅÕÕÕZÅÕÕZZZZÕJÕZZÕÕZÕÅÕÕÅZZÅJJZZZJZJJJÕZÕZÕÕJrÕZrZZÕÅÅJrÅÅÕÕJZZZZZZÅZÕÅÕZÕZZÕZJÅÕJZÅÕrJJZÕrrÕZrJJZJJrZZÕZZÕZZÕÕÅÕJZÅýÕZZJZÅJJÕÕJÕJrJJZJJJJJZJZJJÕZÕÕÕZZZZZÕÅZZÕÅÕZZÕÅJÕÕÅJZZZJZZJrJJrrJrJJJrJJrÕÕZZÕZZÕZÅÕÅZÕÕÕÅÅõÕÕZÅÅÕZÅZZZJZZÕJÕJrJÝé@=ü&&ÐPfv" E@@#  ˆÖU€çżpÞàîJrJJzrJZJJJZJÕJZÕÕÕÅÕZÅõZÕÕÅÕÕÕZÕÕÕÅÅZÕZÕZZJZÕÕÕZJZJJJJJJJJZJJJJÕZrÕÕZÕÕÕÕÅÕÕÅÅÅZÕÕõÕZZÕÅÕJZÕZJÕÕJJZJJJZJZÕÕJÕZJZZJZJZJJZZÕÕÕÕÕÕZJÕZJÕÅZZÕZÕZJZJrZJrJJJrZZZrJZJJJJJJZJZZZZZZZÕZZÕÕZJÕÕÕZÕZÕJJZÕÅZZJÕZZZÕJZZZZÕÕZJÕZJJÕJZJZJJzJZZzrJJJZÕZZZZrZZÕJÝé@=QŠ&&ÐPfv" E@@#  ˆÖ=<€çƽ`ÞàîÕZZÅZrZÕJZZJJÅZZÕÕrÕrJrJJZrrÕJZJZZÕJZZJZzJZZZZÅZJJZZJrÕJZJJrZÕÕJJÕZZJÕZZÅõõZzJõõZrõÕZrZrZÅZJõZzÕJZõZZÕJÕõÅJJÅZJJJzJÕZÕÅÅÅõZÅÅÕJZJÅJýÅZõõZÕõÅåýÕJõõJJÅZÕÅJÕýÕõýZÕýÅrõýrJõÕÕÅZZÅÕJZÕJZõÕJÅÅJZZJZÕJrJrfrZZZÕZJJJZJrJÕÕZZÕZZZZJJZZJZZJJJZZZZZZÕZJJZÕÝé@=U&&ÐPfv" E@@#  ˆÖpЀçǾPÞàîÕÕÕÕõõÕZÕÅÅÅÅõýýýýýýýýåáååáåýõõÅÕZJrzbnbbnnnjjjnjjnbffzJÕá•‘“™†Œ‰µ´´´„ ?3ƒ‡™›‚›f65—íáéåJjJÕÅÕZJnnrzfffnnfZᑟ…‰ŠŒ2> 퇕‘“õ55‘€›åzfzzbí—åbnZrzÅÕfjjnbnfJÕJrzrõ—…€Œˆ‹‰j6=ÅŒŠ‘ýõZ ᄇéfbjjÅ•éZjzznjbJÅÝé@=Xv&&ÐPfv" E@@#  ˆÖ9¡€çÈ¿@ÞàîZnbzzrrJrbnfZ턃Œ‚€•67—‰ˆ…Õzfbá…ŸõjnjZåýzjfzfnfJZzfrJrzbnnbzÕá—Ÿ‡€€Ÿn5J‰áfnnnr•érbbnnfÕýÕnnzrzfzJrbjnrZJrfbnbzJÕåé“™‡††‡í 탙ÅbnbbjÕé‘ázbffbzZÕJfnnbzrrrJrzbjnzJZJzbbbzJÕõá—…††‡†„ᕃ‚™õfÝé@=Hì&&ÐPfv" E@@#  ˆÖ_€çÉÀ0ÞàînfznnZí—íJbrrzzJÕZJzbbzJZZJrzfnnbrZÕZrffzrZÅýá•“›‡††‡‘zÕ…ƒ‡•Zbnffnnzõé•ýzfrrrrJJZJzbbfJZÕJfbnnbzrJJrzffzrZÅåí—Ÿ…††††›åᇙáznbfbjjzÅííÅbbzzfzrZÕZznnbrZZrfnnnfrrrzzffzrJÕõá—›‡‡‘frŸ€‡‘ÕbnffjjrýéíÕjbzfbzZÅÅJbbzJZÝé@=b&&ÐPfv" E@@#  ˆÖà7€çÊÁ ÞàîrbbbffzzzzzzzrZÕõá—Ÿ„€ƒ€›åÕŸ†…•ÅrzzbfõééõzjjnnbrÅýõÕrfbbfzzrrJJJrrzzrrJJZÅýá•›†€ƒƒ†‘Jfå‘“—éíáåÅrjbJõýõÕÕZJzbnnzJÕõõÅÅÕZJzffzrJZÕÕÕÕÕÕÕÕÅõá•“™„†€†ŸáZzfzZÕÕÕõåíéíýZrzzzzbbbzrJJJrrJJJJJJJJJJJrrrrrrrJJJZZZZÕÅýåí•“™›Ÿ‘•ééíåýÅZJÝé@=Ó&&ÐPfv" E@@#  ˆÖdÍ€çËÂÞàîZÕÕÕZJrrJJJrzffffffbfffzrrrJJZZZJJJJJJrrrJJJJZÕõýí•‘™›™“‘—•íåõZzzrJrrzfzrJJrzffffbbnnbbfzzzrrJJJrrrrrzzzzzzrrJÕõåí—“™›Ÿ‘——éåõÕrfzrzzfffzJJrffffffnnnnnbfffzrrrJJrrrrzzzzzzrrJÕõåé—™…›‘——áýÅJffzrzffffrJJzfffffbnnnnbffzzrrJJrrrrrzzzzzzrrJÕõÝé@=ªH&&ÐPfv" E@@#  ˆÖ¥q€çÌÃÞàîåé‘›…™‘——éåõÕrbfzzffffzrJrfffbffbnnnbbfzzrrJJJJrrrrrrzzrJJÕÅýí•™…›‘‘•íåõJffrrrzzfzJZJrzzfzzfbbbbnnzzrJZÕÅÕZrrrrrrJZÕýé…‰™ÅfbnjnnZí‘‘•áÕZJzbJJrzJZZJzffrJJJrrJJJrrrJZÅÅýí“›Žˆ‚ífjjnfÕ—›™‘éýZJbbrZZJJÕÕrfffzJZJJÕÕÕZJrJZÕÕÅõá—ŸÝé@=Ò½&&ÐPfv" E@@#  ˆÖE‘€çÍÃðÞà•bjjbZ•™Ÿ•áýZrnffzzrJZJfbbfzzzzJÕÕZJJJZÕÕÅå•…†ƒŒƒéfnnbJí“•åáåZfjjbzrrfnnbbnbbfrJrrrJJZZÕõí—›‡€…årfbzõáõÅýýZfjjnbbbbfzfbbbfzzzzrJZZÅýí—“Ÿ›…—õZrbnzÕZJÕõÕJzbnnnjnnnbzrzzzzrrzzrJJZÕÅõåí•—‘—áõÅZzbbbfzzzrJJrrÝé@= 3&&ÐPfv" E@@#  ˆÖ†î€çÎÄàÞàîzffbnjjjnbfzzrrrrrJJJJZZÕÅõåáé•éíåýõÕJrzzzzzfzrrrrzffbbnnnnbbffzrrrJZZÕÕÕÕÅÅõõýáííééáåýõÕZJrrrrzzrrrrrrrrzfffffzzzrJJJZÕÕÅÅÅÅÅÅÅõõýååááåýõõÅÕJrrrrrrrrrrrzzzzffffzzzzrrJJZZÕÕÕÅÅÅõõýýýåáåýõÅÕZJrzfzzzzzrrrzzffffffzzzzrrJJJZÕÕÕÅÅõõýýåååååýÅZÝé@=9¨&&ÐPfv" E@@#  ˆÖ q€çÏÅÐÞàîJrzfbbfzzrrJJrzffbbbbbffzzrrrJJZÕÕÅÅõõýåáííé•éåÅZJzbjjbfrrJZÅÕJrzfnnjjjbbbfzrrJJZZÕÕÅÅýåí•‘™›‘õJZzbrZZõáíýÕZJznjnnnzZZJJJJJrzrJÕÅõå鑟›‡€‘rbznfÕýáí•“•ÅzffnzrzrÕÅZrzrzzfzJÕõýí—Ÿ…„ƒ‡Õffá•—‘““ýnfZZJZÕÕJfjnzzbfJÅõýÝé@=á &&ÐPfv" E@@#  ˆÖWL€çÐÆÀÞàî푟Ÿ™‡‘nzý“‘é•••åznJZzfrzbjjnffbzJÕÅýé“™›…‡fznõ“•íååÅfnbbbjjnjjbfzrÕåéé—Ÿ››™™—åé›™ýnzåáZnjfZJffZÅJjnzbnjjnfrzfzZÕZÕýáíáíé•éé•‘““‘‘‘‘•éáýõõõÅÕÕÕJzbnnbnnnbfbnnfzzzzrJZZÕÅÅÅÅåááááé•ééé•‘“‘‘‘“—‘“———Ýé@=´’ &&ÐPfv" E@@#  ˆÖ¤–€çÑǰÞàî—éé•éåýåýõõõõõÅÕZÕõÅÕýýõýííå•‘••—íé——“—•“—í—Ÿ“é‘‘‘•‘—é—•á—éáíííåáåýååýõÅÕõõZZÕJÕZrÕÅJZJZõÕZÅÕÕJJzrZZrrZrJZrzZZzrfzrJfrzrzJzJzzJzzÕzfrrzZzfJZnzzrzzzfrrfbzffzzbrzzÕJfrZfrZJJZÕJrrZJÕrJJZfrrrrÕÕzZÅÕJÅÕÕzÕrJZJzJrfZrfZZzrJzJrJrZrrJJJrrrZrrrZfJzJÝé@=Q &&ÐPfv" E@@#  ˆÖ ö€çÒÈ ÞàîJJrJrZÕZJJÕZrÕÅZZÅÅJZÕJÕZJZZJJÕJrJJrZZrJJrJÕZrJJrzZJrzJrrzzJzzJJrrJzzrzzzJrZzrrJfrrzzJzfzzzzffrznfzbzzbnbnjfnjbbbfzfbnbbbbzffnzzfbrfbfbffzzfzzffbnzffffzzbzzzffzrrffrzzzzrJJJJzJZrJJJZÅJÕÕJJÅZJÕÕZÅZrJZÕÕÕZJÕÕÕZZÕÕZÅÅÕÕZÕÅÅÅÅÅõÕÕõýÕÕõõõÅÅõõÅÅõÝé@=ÿ| &&ÐPfv" E@@#  ˆÖ(-€çÓÉÞàîýõÅÅÅÅÅÅÅýÅýýõÅÅÕõÅõÅÅÅõõÅÅÕÅÅåõõÅýÅýõõõõÕõÅÕõÅÕÅÅÅÕõÕÅÕÕZõÅÅõõÕÕÅZÕÅÅÕÅÕZÕÕZZÕJZÅÕZÕõÕÕÕZÅÅZZÅJZJZrÕJZÕÕZZZrZJJZJrrZJZZJrrZJJrJJzfzzrzfzrfzzrJrrrrJrrrrrrrrrzrrrrJJrJJzrrJrZrzZzZJrÕÕJJZrZJZJÕzÕJrJzJJJJZZÕJÕÕÕZZZÅZZÅJÕZÕJÕÕÕÕÅõZÕÅÕÅÕÕÕÕÕÅÕZZÝé@=ò &&ÐPfv" E@@#  ˆÖ"‰€çÔÊ€ÞàîÅÕÕÅõZõZÅõJÕõÅZõZZÕZZÕJÅZZÕZZÕÕJZÕÕJÕZÕZÕJÕÕÕZZZZZZZZJrÕZJZÅZrÕZJZZrÅZrÕÕrJZZZZJÕÕZZÅÕJÅÅZZÅZÕÕZÕÅJJÅÕÕÕÅÕÕZJÕÅZZÕÅZÕÕÕÅÕÕÅÕZÕÕZÕÕÅÕÕÅÅÅÕÕÅÕÕÕÅÕÕÕZZÕÕZZZZZÕÕÕÕÕZÕÕZZÕZZÕZZÕZÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÅÕÕÕÕÅÅÕÕÕÕÕÕÕÕZZZJJrrrrzrrzzzzzzzzzzrrJJJZZÕÅõýá•‘Ýé@=!k &&ÐPfv" E@@#  ˆÖ˜Y€çÕËpÞà‡†„›“ýzbjrÅåé‘‘íåõJnjbrZÕÕÕJrrbjnjnbfzzrJrJJrrJJZõå韆‚Žˆ€—õrzý„„…›‘õbrÅᑟ™—åÕfnrZÅõõÅZzfbjjbfzZÕõýýýýýõõýýåí‘…‚‚›‘íÅbnrᑟŸ“éÅzbjjzÕýí—“‘éáõJbjnfrZÅÕÕÕJrfbffzrZÅÅýååååååá•“„ƒ‚€ŸåÕznZí‘›…Ÿ‘éÕjÝé@=ƒÜ &&ÐPfv" E@@#  ˆÖ#ä€çÖÌ`Þàîjzõé—‘‘íõJbjnzJõáéíáÅnbJÅõõõJrJzbzrJÕÅÅýååáíáé“›‡…‘årnzÕ푟“íõJjbZå•———éýJfnnfJÅýáíåÕbbfÕõÅÕZzffnbrJJÅýåáíí铟„€…‘Jjjrõ—™™•õZbjzõí‘“•éåZbjbrÅåáííýZrbbZZJÕZzbjnfbrÕÅõýýýýõõåí•™‡™ýnjbJÝé@=6S &&ÐPfv" E@@#  ˆÖP€ç×ÍPÞàîí“™“åZrfÕå•‘—íåÅfnjbrZýáåáåÕrfnjzZZZZJrfnnffzZÅÅõýýýõÅÅõýᕟ„‡›‘õfbfrõé——•åÕZfnbrÕýáááåýÕrffbnbzrZÕÅÅÅÕJbbzrZZZÕZJrrzrJJJZÕÅõõõýýåáé•‘—åÕZrbnnbfzrÕÅõõõõõõÅÕZZJrJJJJZZZÕÕÕÕÕÅÕÕÕZZJJJrffffzzrJrrJJJJJJJZZZÕÕÕÕÕÕÅÅÝé@=FÉ &&ÐPfv" E@@#  ˆÖ@ê€çØÎ@ÞàîõõõõýååááííááååýÅÕÕZZJJJrzzzfzrrrrrrrrJJJZÕÕÅÅÕÕÕÕÕÕÅÕÕÕZZJJrrzzzzfffffzzzrrrJZZZÕÕÅÅÅõõõõõõýýååáááááááåýýÅÕZZJJrrzzzfzzrrrrrrJJZZZÕÕÕÅÅÅÅÅÅÅÕÕÕZZJJJrzzfffffbbfffzzzrrJJJZZÕÕÕÕÅÅÅÅÅÅÅÅÅÅÅÅõõõýýýýõõõÅÕÕZZJrrzzzzzzzzzzrrrrrJJJZZZZZZZZZZZZZJJJÝé@=J? &&ÐPfv" E@@#  ˆÖóÍ€çÙÏ0ÞàîJrzzffbbbbbbfffzzzrrJJZZZZÕÕÕÅÕÅÅÅÅÅÅÅÅÅÅõõõýýýõõÅÅÅÕZZJJJrrzzzzrrrrrJrrJJJZZZZZZZZZZZZZZZJJrrzzzzffffffzzzzrrrJJZZZZÕÕÕÕÕÅÕÕÅÅÅÅÅÅÅÅÅÅõõõýýýååýõõÅÅZJJrzzrrJJZZZZZZZZZZZZZZZZZZZZZZZZZJrzbbbffzzzzrrrJJJZZÕÕÕÅÅÕÅÅÅÅÅÅõõýýýåáíé•—‘‘—íåõÅJfbbnbfÝé@=6µ &&ÐPfv" E@@#  ˆÖc×€çÚÐ ÞàîrJÕÅõõýýõÅÅÕJrrrrrrJZÕÅÅÅõÅÕÕZJrfnnjjfrrrJJJZJJJZZJZZJJZÕÕÕÕÕÅÅõÅÅõõýí•—“ŸŸ•ýZzbjbzJõáéééáýÅZfbbnnbrZÅýåááåõÕJrfbbbffbbbnrzbzrzzrzrZZJJÕZJJÕÅÕÕÅõýýåé‘™›…åzfnnzÕåé——éýÅZfjnrÅåáéíáåõrbbbnbzJÕÅõõõZjnzrZÅÅÕZrzrzfzJZJZÅõÅõÝé@=…&&&ÐPfv" E@@#  ˆÖ6€çÛÑÞàîõõõõõýáí•™™™érbnzJÅé———éÅJrbbzrÅáíééåõÅJfnbfzJÅõýåýÅZrnjJõÅõõÕrzbnfzzrÅõÅÅýõÕÕZZÕÅÕÅåáí—ŸŸŸ‘åzjjrÕå•—•éýJfbjjfJÅåííáåÅJrfjnfrZÅõýýõÕrfrÅÅÅÅÕrfnjfzzJÅõõÅÅÕZJJJZZJÕÅÅÅõõÅõõõýáééåáíõzfrffrÕÅåáýõõÕzzrzzrZÕÅõÅÅÅZrzzzfzJJJJÝé@=­›&&ÐPfv" E@@#  ˆÖŠE€çÜÒÞàîjnjzrzJZJrrzzJJzJÅÕZÕÅZÕÅÅÅõõÅýááé™—árbbjbZÅåé•áýÅrffnnzZÅõååýõÅJzzzzJÕÅõýýõÅZrrzzzbjrJzJÅZJZJzrrrZÅÅÕõýÅÅÅÕZÕÕÕÅõÅÅõõÅÕÅÕZÕÕZZÕÕZZÕJJJJrJJJJZZZZZZJZJJZZZZÕÕZÕÕÕÅÅÅõýýýýåá•“Ÿ™‘õbbfbfZõýé•áõÅJffzfzÕõýååõÅÅZrrJJZõýõýåýÅÕJrJJJJÕÅÅÅÅÕZZÝé@=É&&ÐPfv" E@@#  ˆÖƬ€çÝÒðÞàîJJJJJZÅÅÕÕÅÕZJJrjfZZÕõõõõÅJrrrJZÕÕÅõÅÅÕZJZZJJÕÕÕÅÅÕÕÕZZZZZZÕÕÕÕÕÕZÕZZZÕÕZÕÕÕZÕZZÕÕZZÕZZÕÕÕZÕÕÕZZZZÕÕZZÕÕZÕÕÕÕÕÕZÕÕZZÕÕZÕÕÕZÕÕÕÕÕÕÕÕÕZZÕZÕÕÕÕÕÕZZZZÕÕÕÕZZÕÕÕZÕÕZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕZÕÕÕZÕÕÕÕÕÕZÕÕÕZÕÕZZÕÕÕÕÕÕÕÕÕÕZÕÕÕÕÕÕÕÕÕZZÕÕÕÕÕÕÅÕÕZZÕÕÕZZÞé@=D&&ÐPfv" E@@#  ˆÖ"€çÞÓàÞàîÕÕÕÕZZÕÕÕZZZZZZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÅÕÕÕÕÅÅÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZZJJJrrrrrrrrrJJJZZZÕÅÅýáé—““‘írzZÅýýáééÅzfbfrJZÕýåýÅZzzrrrJÕõýýõÕZZJJJJZÕÅÅÅÕÕZJJrrJZZÕÕÕZZZJJJJZZÕZZÕÕZZJJJZZZZZZZZZJJJZZZZZZZJJJJJJZZZZZZZZZJJZJZZZZZZZZJJJJJJJJJJJznZýõÕJZõÅrbfrÞé@=¹&&ÐPfv" E@@#  ˆÖo–€çßÔÐÞàîZÅõÕZÕÅÕJrzrÕÕZZÕÕZZJrrJZZZZJZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZJZZZZZZZZZZZJJZZZZZZZZZZZJJZZZJZZZZZZZZZJZZZZZÕZZZZZZZZZÕÕZZZÕZZZZZZÕÕÕZJJZÕÕZZZZZZZZZZZZZZZZZZZZZZZZÕZZZZÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕZZÕÕÕÕZÕÕÕZZZZZZZZZJJZZZJJJZZZZJZZÕÅÅÅÅÅõõåÞé@=\5&&ÐPfv" E@@#  ˆÖøí€çàÕÀÞàîáíé•••íõfjzÅåååýýõÕznjnzÅõõÅÅÕZJzbbzZÅýõÅÕZrfnbfzrJJJrzzfffzrrJJJJZZJrrJZZÕÕÕÕÕÕÕÕÕÕÕÅõõýýåáíéíýZbjnrÅýåýýõÅZfnjbrÕõõÅÕZJzjbJZZrzzbnjjbrZZZJrrrrJZÕÅýýåáí—Ÿ„„‘rfåéííííáÕjråíåõÅÅÕrnbÕáíáõÕZJrbjjfrJrzfffbjjnfrJJJJJJJJrrJZÕÕÕÕÅÅÅÕÕÞé@=P£&&ÐPfv" E@@#  ˆÖx€çáÖ°ÞàîÕÕÕÕÕÕÕÕÅÅÕÕÕÕÕÕÕÕÕÕÕZZZZJJrJrrrrzzzzfffffzzrrrrrrJJZZÕõåáé‘™„††‡™ínÕ“…™“•íõfÅ‘—áõÕJbrå—‘éýÕJfrõýÅJfbnfJÕÕZJrrzrJZÕõýåáí•“™†€„‘ÅÕé““““•ÅnnÅí••éáýZbnrÅááýZznjjjnfrZJrfnnbbbzrJZJrrrJÕÅýí—Ÿ„‚™ázퟟánrÞé@=‡&&ÐPfv" E@@#  ˆÖÚX€çâ× Þàîýí•—•áÅnbZýåáííJjzzzZÕrnnffzJJrzffzZÅõåé“›†€‚€†ŸÕÕ•‘‘…õrÅå•——éÕnfJÅõá•åznnbrÅZfnjnnfrZJrzfzJÕõå•‘Ÿ„€‚‚†ZZåé—™‡ŸýjnzÅé‘‘éõznjjnfrZýíýrbjfJrfbnjnnjnzrzzzfrJZÅýáé‘›‡€€ƒ™ÅfZÕá…ázrõíéáýÅÞé@=º&&ÐPfv" E@@#  ˆÖÁ€çãØÞàîÕJfbbfJZJffzzffnjbbnjjjnffbfzzzzfzrJZÅõáé—“™…‡€—znbjnnfí“•åõJrJzfJZõÅÕÅýõÕZJrJrnnbfzzfffbjjjbbbzzzrrrrJZÕÅõýåííé•—‘“ŸŸ™™—ÕfbjnzrrõáååýÕJrrzrJJJÕÅÕÕÅÕJrbjjnnbfzzzrzffbfzzzzrrJJJZÕÅõõýáááíááííáééé•—•éáÅÅõÅÅõÅÕÕÕZÕÕJJÕÅÕÕZJrJzfzrzrrrÞé@=Z&&ÐPfv" E@@#  ˆÖäî€çäÙ€ÞàîrrffzrzrrJrJJrJrJZÕÕÕZÕÅÕÅÅÅÅÅõõåýõõýýýýýõýýýõýõýýõõõõõõýýýýõÅýåõÅÅÅÕÕZJzrJrJZJJZÕrJZJrZZJJZrZÕÕZZÅÕÕõÅÕÅÅÕÕÕÕÅÕÅÕÅýõÅÅÅÅõÅÅÕÅÕõÅõÅÅÅõÕÅõZÕõZZõZZZÕZÕZZZÕZÕJJJZrJZJJJZJZZJJJJÕJJÕÕrZZJJJZJÕJZZJJZrJrJJZJZJJZZZJJJZÕZZZÕÕÕJÕÕÕÕõÕÕÕZÕÅZÕÕÕJÕZJJZrÞé@=3x&&ÐPfv" E@@#  ˆÖ§‹€çåÚpÞàîJJÕrJJZrZJZJÕJÕZZrÕZJÕÕJZÅÕÕÕJJJÕJZZZZÕÅJZZZJZzJrJzJrJJJrZrZJZrÕrÕJZJJrZZÕZJZZZÕJZÕÕJÕZrJJJJZJJZJJÕZrZZJÕZJJJrJJZJÕJZZÅJZJrJrZzZrJrZrZfJrZrJZrrJZzrrJrJrZJJrJJJJJZJZJZrZzZrZrZJÕrrrJZZrJZJJJJJrJJrJZZJZJÕJJJZJrJZJÕJZrZrZJÕrZJZJZJJZJZZÕZJJÅJZZJÞé@=Óí&&ÐPfv" E@@#  ˆÖÞ‚€çæÛ`ÞàîJÕJJZZrÕZZrZrZZÕrrZJJZZfÕrZrÕJZZJJJrZZrJrZJÕrJJZJJJZZJrrJJrJrJrJJJrZJzrrzrJrJzrrJJrzZJrJzzrrzrJfJJrzrrfJzrzrzzJrzrrzzrfrrrzrzzrJzzrrzJzrrfzzJfrJrrzJfZbJbZfÕfzzZzrzZzJzzzJfrJrrrzzJrzrrJrzzzzzrZrrzÕzJfJfJrÕJZrrJJJrZzJrJrJJfZrJrÕfZrJJZJrõZJJÕJÞé@=Ic&&ÐPfv" E@@#  ˆÖj]€ççÜPÞàîÕJÕJZrZZÅrÕÅÕZÅÕZÅZZJZZÕÅÕZýÅZÕÅJÕZZZÅÕÅÅÕÕÅZZÅÕÕZÕõõÅÕÅÕÕÅõÅýõÅõÅÕÅõÅÅõÅõýýõýõÅÅåõýýõÕåõÅõåÅýåýõíýõýåõååõýåõõáåýåýýååõõåýýåýõýåýáåýõåýýýýõýýõýýååáåýååýáåýýåýååááéáááéáíííåéíí••íééáííåýåõÕÅÕJZZJJZJJZZJÕZZZÕÕÕÅÅÅõýáííí•—Ÿ›„‡‡›‘•íýrrzzzrzZõÞé@=2Ù&&ÐPfv" E@@#  ˆÖ<|€çèÝ@ÞàîõõáíííáåýõÕJJrzzrzrJrrJrrJJzrrzzzzzrJJJZÕÕÅõýååí•••‘“™“éííÅZrrzzzzJZZÕÅõõõõÅÅÕJrrzfbjnbnjnbbffbfbnbzfbbfzzrrJÕZJõýõõáááíáé—‘•éáåõÕJrzffbfzzzzJJrJJrrzfbjjnbffbnbbbfbbbbfzzzJZÕJÅZõZÕÕõõÅõÅåõýýáåýåõÅÕZrZrfzrzrrrrrzbzbnjjnbnnnfbbzfffrsipp-3.6.1/pcap/dtmf_2833_2.pcap0000664000175000017500000000137413730472040015503 0ustar walterwalterÔò¡ÿÿ¡ñCÜ ::P¿™6 ‡¬$E,öþ@nÀ¨À¨À'Só€åjZ 8N ¡ñCÉi ::P¿™6 ‡¬$E,öÿ@mÀ¨À¨À'S2€ekZ 8N @¡ñCŠ· ::P¿™6 ‡¬$E,÷@lÀ¨À¨À'Qñ€elZ 8N €¡ñC² ::P¿™6 ‡¬$E,÷@kÀ¨À¨À'P°€emZ 8N À¡ñCÔS ::P¿™6 ‡¬$E,÷@jÀ¨À¨À'Oo€enZ 8N ¡ñC÷¡ ::P¿™6 ‡¬$E,÷@iÀ¨À¨À'N.€eoZ 8N @¡ñC;ð ::P¿™6 ‡¬$E,÷@hÀ¨À¨À'Lí€epZ 8N €¡ñC>::P¿™6 ‡¬$E,÷@gÀ¨À¨À'K,€eqZ 8NŠÀ¡ñCP>::P¿™6 ‡¬$E,÷@fÀ¨À¨À'K,€eqZ 8NŠÀ¡ñCZ>::P¿™6 ‡¬$E,÷@eÀ¨À¨À'K,€eqZ 8NŠÀsipp-3.6.1/pcap/dtmf_2833_star.pcap0000664000175000017500000000137413730472040016313 0ustar walterwalterÔò¡ÿÿ©ñCÜV ::P¿™6 ‡¬$E,ù£@ÿÈÀ¨À¨À'V/€å ÍO8N ©ñCǤ ::P¿™6 ‡¬$E,ù¤@ÿÇÀ¨À¨À'Un€e ÎO8N @©ñCóò ::P¿™6 ‡¬$E,ù¥@ÿÆÀ¨À¨À'T-€e ÏO8N €©ñCç@ ::P¿™6 ‡¬$E,ù¦@ÿÅÀ¨À¨À'Rì€e ÐO8N À©ñC& ::P¿™6 ‡¬$E,ù§@ÿÄÀ¨À¨À'Q«€e ÑO8N ©ñCbÝ ::P¿™6 ‡¬$E,ù¨@ÿÃÀ¨À¨À'Pj€e ÒO8N @©ñC?+ ::P¿™6 ‡¬$E,ù©@ÿÂÀ¨À¨À'O)€e ÓO8N €©ñCŸy ::P¿™6 ‡¬$E,ùª@ÿÁÀ¨À¨À'Mh€e ÔO8N ŠÀ©ñCz ::P¿™6 ‡¬$E,ù«@ÿÀÀ¨À¨À'Mh€e ÔO8N ŠÀ©ñCz ::P¿™6 ‡¬$E,ù¬@ÿ¿À¨À¨À'Mh€e ÔO8N ŠÀsipp-3.6.1/pcap/dtmf_2833_1.pcap0000664000175000017500000000137413730472040015502 0ustar walterwalterÔò¡ÿÿ ñC–s::P¿™6 ‡¬$E,ö™@ÓÀ¨À¨À'{í€å03à8N  ñC®Á::P¿™6 ‡¬$E,öš@ÒÀ¨À¨À'{,€e13à8N @ ñC_ ::P¿™6 ‡¬$E,ö›@ÑÀ¨À¨À'yë€e23à8N € ñC] ::P¿™6 ‡¬$E,öœ@ÐÀ¨À¨À'xª€e33à8N À ñC¬ ::P¿™6 ‡¬$E,ö@ÏÀ¨À¨À'wi€e43à8N  ñCëù ::P¿™6 ‡¬$E,öž@ÎÀ¨À¨À'v(€e53à8N @ ñCÏG ::P¿™6 ‡¬$E,öŸ@ÍÀ¨À¨À'tç€e63à8N € ñCÜ• ::P¿™6 ‡¬$E,ö @ÌÀ¨À¨À's&€e73à8NŠÀ ñC– ::P¿™6 ‡¬$E,ö¡@ËÀ¨À¨À's&€e73à8NŠÀ ñC/– ::P¿™6 ‡¬$E,ö¢@ÊÀ¨À¨À's&€e73à8NŠÀsipp-3.6.1/pcap/dtmf_2833_7.pcap0000664000175000017500000000137413730472040015510 0ustar walterwalterÔò¡ÿÿ¥ñCý. ::P¿™6 ‡¬$E,øV@À¨À¨À'Ó!€å ÕÀ8N ¥ñC} ::P¿™6 ‡¬$E,øW@À¨À¨À'Ò`€e ÕÀ8N @¥ñCpË ::P¿™6 ‡¬$E,øX@À¨À¨À'Ñ€e ÕÀ8N €¥ñC” ::P¿™6 ‡¬$E,øY@À¨À¨À'ÏÞ€e ÕÀ8N À¥ñC€g ::P¿™6 ‡¬$E,øZ@À¨À¨À'΀e ÕÀ8N ¥ñCص ::P¿™6 ‡¬$E,ø[@À¨À¨À'Í\€e ÕÀ8N @¥ñCŸ ::P¿™6 ‡¬$E,ø\@À¨À¨À'Ì€e !ÕÀ8N €¥ñC|Q ::P¿™6 ‡¬$E,ø]@À¨À¨À'ÊZ€e "ÕÀ8NŠÀ¥ñCÛQ ::P¿™6 ‡¬$E,ø^@À¨À¨À'ÊZ€e "ÕÀ8NŠÀ¥ñCæQ ::P¿™6 ‡¬$E,ø_@ À¨À¨À'ÊZ€e "ÕÀ8NŠÀsipp-3.6.1/pcap/dtmf_2833_6.pcap0000664000175000017500000000137413730472040015507 0ustar walterwalterÔò¡ÿÿ¤ñCª&::P¿™6 ‡¬$E,ø @cÀ¨À¨À'ëb€åú¾ 8N ¥ñCŸ2::P¿™6 ‡¬$E,ø @bÀ¨À¨À'ê¡€eû¾ 8N @¥ñCµ€::P¿™6 ‡¬$E,ø @aÀ¨À¨À'é`€eü¾ 8N €¥ñCÖÎ::P¿™6 ‡¬$E,ø@^À¨À¨À'è€eý¾ 8N À¥ñCÂ::P¿™6 ‡¬$E,ø%@GÀ¨À¨À'æÞ€eþ¾ 8N ¥ñCk::P¿™6 ‡¬$E,ø1@;À¨À¨À'å€eÿ¾ 8N @¥ñC¹::P¿™6 ‡¬$E,ø6@6À¨À¨À'ä\€e ¾ 8N €¥ñC'::P¿™6 ‡¬$E,ø7@5À¨À¨À'⛀e ¾ 8NŠÀ¥ñC}::P¿™6 ‡¬$E,ø8@4À¨À¨À'⛀e ¾ 8NŠÀ¥ñCˆ::P¿™6 ‡¬$E,ø9@3À¨À¨À'⛀e ¾ 8NŠÀsipp-3.6.1/pcap/dtmf_2833_9.pcap0000664000175000017500000000137413730472040015512 0ustar walterwalterÔò¡ÿÿ§ñC°::P¿™6 ‡¬$E,øô@xÀ¨À¨À'—€å e 8N §ñCeþ::P¿™6 ‡¬$E,øõ@wÀ¨À¨À'œÖ€e f 8N @§ñCïK::P¿™6 ‡¬$E,øö@vÀ¨À¨À'›•€e g 8N €§ñCþ™::P¿™6 ‡¬$E,ø÷@uÀ¨À¨À'šT€e h 8N À§ñCwè::P¿™6 ‡¬$E,øø@tÀ¨À¨À'™€e i 8N §ñCX6::P¿™6 ‡¬$E,øù@sÀ¨À¨À'—Ò€e j 8N @§ñCÁ„::P¿™6 ‡¬$E,øú@rÀ¨À¨À'–‘€e k 8N €§ñCÞÒ::P¿™6 ‡¬$E,øû@qÀ¨À¨À'”Ѐe l 8N ŠÀ§ñC@Ó::P¿™6 ‡¬$E,øü@pÀ¨À¨À'”Ѐe l 8N ŠÀ§ñCKÓ::P¿™6 ‡¬$E,øý@oÀ¨À¨À'”Ѐe l 8N ŠÀsipp-3.6.1/pcap/dtmf_2833_pound.pcap0000664000175000017500000000137413730472040016467 0ustar walterwalterÔò¡ÿÿªñCa3::P¿™6 ‡¬$E,ùô@ÿwÀ¨À¨À':(€å ôià8N ªñC::P¿™6 ‡¬$E,ùõ@ÿvÀ¨À¨À'9g€e õià8N @ªñC÷Î::P¿™6 ‡¬$E,ùö@ÿuÀ¨À¨À'8&€e öià8N €ªñC::P¿™6 ‡¬$E,ù÷@ÿtÀ¨À¨À'6å€e ÷ià8N ÀªñC|k::P¿™6 ‡¬$E,ùø@ÿsÀ¨À¨À'5¤€e øià8N ªñCã¹::P¿™6 ‡¬$E,ùù@ÿrÀ¨À¨À'4c€e ùià8N @ªñC‡ ::P¿™6 ‡¬$E,ùú@ÿqÀ¨À¨À'3"€e úià8N €ªñC|U ::P¿™6 ‡¬$E,ùû@ÿpÀ¨À¨À'1a€e ûià8N ŠÀªñCÌU ::P¿™6 ‡¬$E,ùü@ÿoÀ¨À¨À'1a€e ûià8N ŠÀªñC×U ::P¿™6 ‡¬$E,ùý@ÿnÀ¨À¨À'1a€e ûià8N ŠÀsipp-3.6.1/pcap/dtmf_2833_3.pcap0000664000175000017500000000137413730472040015504 0ustar walterwalterÔò¡ÿÿ¢ñCQÌ ::P¿™6 ‡¬$E,÷V@À¨À¨À'4&€å—y@8N ¢ñC¼ ::P¿™6 ‡¬$E,÷W@À¨À¨À'3e€e˜y@8N @¢ñCŠh ::P¿™6 ‡¬$E,÷X@À¨À¨À'2$€e™y@8N €¢ñCj¶ ::P¿™6 ‡¬$E,÷Y@À¨À¨À'0ã€ešy@8N À¢ñCÑ ::P¿™6 ‡¬$E,÷Z@À¨À¨À'/¢€e›y@8N ¢ñCÆR ::P¿™6 ‡¬$E,÷[@À¨À¨À'.a€eœy@8N @¢ñC¤  ::P¿™6 ‡¬$E,÷\@À¨À¨À'- €ey@8N €¢ñC?ï ::P¿™6 ‡¬$E,÷]@À¨À¨À'+_€ežy@8NŠÀ¢ñCï ::P¿™6 ‡¬$E,÷^@À¨À¨À'+_€ežy@8NŠÀ¢ñCäï ::P¿™6 ‡¬$E,÷_@ À¨À¨À'+_€ežy@8NŠÀsipp-3.6.1/THANKS0000664000175000017500000000173413730472040012774 0ustar walterwalterThe following people have contributed code or other elements to SIPp: Richard Gayraud Marc Lamberton Olivier Jacques Herve Pellan David Mansutti Francois-Xavier Kowalski Gerard Lyonnaz Francois Draperi F. Tarek Rogers Peter Higginson Vincent Luba Shriram Natarajan Guillaume Teissier Clement Chen Wolfgang Beck Charles P Wright Martin Van Leeuwen Andy Aicken Michael Hirschbichler Rob Day Dmitry Semyonov Jordan Walbesser Ken Crowell Peter Lemenkov Menyus Hegedűs Mitko Mitev Matt Williams Richard Brady Natanael Copa Walter Doekes (OSSO B.V.) Simon Gomizelj (Sangoma) Peter Wu Ben Langfeld Richard van den Berg Mustafa Kocaturk Konstantin S. Vishnivetsky SIPp also uses code originally by Aaron Turner (send_packets.c) and L Peter Deutsch (md5.c). The following people have contributed to SIPp in other useful ways (bug reports, testing, etc.): Paul Belanger Jan Stanek Enrico Hartung Daniel Swärd Paul D Smith Ferenc Wágner Carlo R. Carrano Kayode Olajide Daniel Goepp Vijay Goje sipp-3.6.1/gmock/0000775000175000017500000000000013730472040013154 5ustar walterwaltersipp-3.6.1/validate-src.sh0000755000175000017500000000336413730472040014775 0ustar walterwalter#!/bin/sh print_listing=true err=0 TAB=`printf '\t'` while getopts ':l' opt do case $opt in l) print_listing=false;; esac done list_sed='s/^/ /;s/:/: ^/' files_with_tabs=$( find include src '!' -name config.h \ '(' -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' ')' | xargs grep -l "$TAB") if test -n "$files_with_tabs"; then echo "tabs encountered in one or more source files:" >&2 for file in $files_with_tabs; do echo " $file" >&2 if $print_listing; then grep -n "$TAB" $file | sed -e "$list_sed" | cat -A >&2 fi done echo >&2 err=$((err+1)) fi files_with_trailing_ws=$( find include src '!' -name config.h \ '(' -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' ')' | xargs grep -l '[[:blank:][:cntrl:]]$') if test -n "$files_with_trailing_ws"; then echo "trailing whitespace found in one or more source files:" >&2 for file in $files_with_trailing_ws; do echo " $files_with_trailing_ws" >&2 if $print_listing; then grep -n '[[:blank:][:cntrl:]]$' $file | sed -e "$list_sed" | cat -A >&2 fi done echo >&2 err=$((err+1)) fi files_with_not_4_spaces=$( find include src '!' -name config.h \ '(' -name '*.cpp' -o -name '*.hpp' -o -name '*.c' -o -name '*.h' ')' | xargs grep -lE '^( \}| {1,3}[a-z_])') if test -n "$files_with_not_4_spaces"; then echo "files with non-standard indentation found:" >&2 for file in $files_with_not_4_spaces; do echo " $file" >&2 if $print_listing; then grep -nE '^( \}| {1,3}[a-z_])' $file | sed -e "$list_sed" | cat -A >&2 fi done echo >&2 err=$((err+1)) fi exit $err sipp-3.6.1/docs/0000775000175000017500000000000013730472040013004 5ustar walterwaltersipp-3.6.1/docs/uac.xml0000664000175000017500000001065713730472040014307 0ustar walterwalter ;tag=[pid]SIPpTag00[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag00[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.6.1/docs/sipp-03.gif0000664000175000017500000002477013730472040014700 0ustar walterwalterGIF89aS_p,S_‡™ÎÿâÎÚ¾òÚâòºÿÿÿÿ H° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\ɲ¥Ë—0cÊœI³¦Í›8sÖÀ³§ÏŸ@ƒ J´¨Ñ£Hè\Ê´©Ó§P£JJ•¦Ò‘W/f=¹µª×¯`ÊK¶¬Ù‚]s¦%¹ö¬Û·pãÊK·lמñæå¹—oß¼üx ÒÃz §l[·±ãÇ#KžÌpkVÁ‡ +.|y°âΚ¯ò x1åÓ¨S«^ÍZç]Òn6hÍ¥m«dܺ·ïßÀƒ _›96ÚÐÈ?ã^®{3WáУKŸNý­eó'{9çîÊMWÿO¾¼ùó,_Ó¾ì4w؃IkÏd}ôøóë߯ÿ¾dÿü(à€ž d¨à‚ 6XU‚Ž%%á„Vhá…f¨á†vèᇠ†(â„–(Òˆ(¦èS,¶èâ‹0Æ(ãŒ4Öhã8æ¨ãŽ<öèã@)äDiä‘.Bhâ’ €ä“PF)å”TViå•Xf©å–Q*Éä—9Éå˜d–iæ™h¦©æšl& æ›‰ÙæœtÖiçxæ©g^ §œ{*è „jè¡ôé'˜€vÙ(¢F*餔®©è¢L>£¦;ŠÉi¥ †*ꨤò‰é©•ñ)çŠ,ö”¨“<µÿ몟–jë­¸æŠç¥¨:øi¬²Â¸ê«­&Új£Àêªì²Ì6›%¯½2X+±ÇÒêê°Ãëì¶Üvë­ÐF«à´Å&Yn°ÅfKî·ìʪ¢„íÆ e¸â¸nµ€õ*¬¬Êû­Q–åïÀ6Ò[¯€÷¬°ŒCZÔÂñ|0 C,¯Pÿbl±²OÜ߯r’úŽì°ÇÑVlr©%¯|cË.Ú1Ê詳¤?ÝL¤Æ:Ó93ÍæÙܳ 9=%ÌFù3Ðä Íç‹Éî[´»îºJ-ÉFOô–ZomåÒLWç´°è–mlºçž ëÙl›½r¿^§)rÜ]†½èØT»/Ôe¯ÿm¶ßn‡l5ÝwÎM¸`Û-ÞQ§Ý6²}?n.ä~¸Ì]_þ²â"¾·¹z÷u¾ã«åšCšyêPsþ¦éÿ¬§‘«-»¿p³îpî¬'îzp°“ zÕRí¬ÈgË.êºßºzܾÿþ[ðªVÙ¸³×7Ïìó=G/}oÔk¯jøâOʽÉÞÏùå“Ì~û¡ž¿púê«ö~ùÙñü×_âýÍËŸþÐÇ»þù¯AL¨³v‹~¤L·@&§‹ ´,¨£ rnÌ ÇõÁ‚M°„ócž­ 8B¡pF'|aåT¿(† Ä¡ GÂH±Ð†Ñ¡Ëÿ„¸Ã!ÒðP?"]ˆ2±„= T•(&†ì‰X¤Q GÅYQaNÌ"·Ø¦)vÑ:(ü¢)F4™ñŒgQãÅÖHÇNµ‘Lo„£]>(Ç:*ðˆfÊ£Ç2ÁœïRã×!ïØ61òe|Ó"éÕÇØa­zª«Õ¬ð¥HMyj€ÌX%75HüÄthK¶ÎF»£Qi]Êû\‘H.öáS¿ÒÖí(§¼H6’`¡Ä’ K –S^p²Tå/Ý÷ÉO^íz“´–¶ ™¼ª%Ërê’%òŒ·Éjq’x Ö6‡w5Ñ=³šÒL$8…ůu&/w¹Üè(gI¶’hDÒ0‰éÿcÎÎX­ Ý:…GPqj1pÙ¬]¹®i;ÛîqÚ¨DêKMæ-™ »¤C%ª®Š†3•öì%9ÙIOVŽÔ¢Žkb>wÆÏ ½rr&-©ä4Y:…ÊÓ¬úäúÍ&“’×â(º€ J›Öò¤+Z2÷¼U64¥P%(DK:-‘¾m¥@ÚgK§âÏ…ÒN¦:-Øð¬:RŠú¤º|”'‡úÓ³ µ£h=èS—yV° Õ¬•k^{ÉWÞ.¥½)]¯*D­n5*…Lê8G—Sq:°_Ô¼¦Z­ÉÌâé²§YCÊØ†aS²ù‹,U'‹/fêë³ÖÂ¥fA›Îzf‘ä,Zé° LÚÿ:ò°ã¹'×iKu•jòd-+I=ÛJ5…Ä­Ø–Ì'éÖzÆåRsýøµè K¹Ôyî Íg8ÌaºZ²®¬°;í’ʼàMï¾XJÞè WTïUozkØöºFwñ•¯z§KJûÇQЬ'oY©Øì5S’¤½lFYUàþȃ 6aÞ²zÊü.ŸõõïM )àN)8¬ó´d_Ǻà+áòiníQðŽZVi­ú—Ê2¬áH)›“ÔèqwéÖxJnž]m;;ÎõN³»ƒ¬û*kä¨zóÈHí1ÔX;Ð_µóÉÄ‚™…5×F×x&nd,s„:3U¯VMsD *毚3ÿªU-èPÿ9×Sk³'%1\í)IÒ*v[þc-¿<=WŽö‘?­i‡ÇìÑ'Ut]ª2kç§”²Nþ+žºQ’nÚ©|tŒg©IBû愯!ùÝø¼ùën®yŠà«IxžxÆ-¤ÕÉâ ŸSb n ƒ<ã ß•—9¾Dàä(ÿO¹ÊWÎò–»œåØ~’fNóšÛüæ8ϹÎqr1‡äÿ1ùˇNô¢«<æHÚ¹Ò—Îô›÷<‹´zdËðÜ'(ïIÖÐr¬«\ë{ØÁ®NGêH>©yOfΧ]ím§yÜß.vKÖs«~¶¿Ë)ãWªñŽŒ=¶Óö­ÏØŒ\êöqe»tu®oÝè_9OÆNùÈ_›×g»ÜÐôÍß|îk÷<ÛmºxHâ[¯˜7ý,Ëâ|û¶ÛYå·Ï BÄÃÕ!½³c‘ù©Æo]ë^ûä}ÿûÉßñćÛo›\äd¡ó› Ü5O}º?îr¿,X©Œå$[­ÈªLpÿø£Ù}º‚ÿûCV¾ŽŒZªi™Év‡}–©Î¯ZûüÓámŸT¾¡Ju#Ä7€ÉçxÃ7|•w|•W€ÖbÀ¥)ׇ}¢ ÇvЧyhÙ'išöj¤Çz—ÖzÅkÕoºF`h‚;æTE€¤GSx€Wi –zJ£ûÇqbh±5g<%#ˆ€ vx€\§€‘MÖÖk1’8@ñ„8€¿ÖiÔækÄXÇÅhj…M¸v‚—¶LFi”…-êU%ˆ6VH`ßg†–²µ§ƒa{u僥„ÈW€’‡|FHyBx€ahT±ã„NX}ŸwŒ(OÿˆvÚ—‡qµ…ygk…H‰bV‚¶g#h~4H‚™–f/ˆf^õVsÖmæ9h‡Auð‡~Ô4#øÂ|DÑxX'vÄ÷2°¸{ ‰H8Ï—vQX… ¨nçnžÖIÞVPßv†¢%\unÝa8U`(µŒDQvÔfdïDqÚȆäuÈŠ\%tÁyìø‡ÃGo4RŒÐ×yôˆst75„÷z*¨w&¶3q.}–Iù÷爎O!q@ØŽ ÉŽ õøô˜ž³pé9*J²Šy # ù‘Eg%9’LÇw~÷]g‚qÛ¢’Û3¹‘jñt&V‘23Š¢‘0ÿ5 ]4¹“<'9Ù‰\Uç“ ¤8‰ŽTד«ç[ï3cKXƒ9]ØH4”øÓA“]’WÎÕA/æF†(xçb(Ö”Š'‰Fé[ñ’[Ù]…eú&[͆IųZç$UÑ8Ù¶|ìöjÚ4ƒ»ÇYœT–àxel‚y`é„¡…˜xù~ë·–ÃIi{q©…W8†â–‚EŠ»öLègzvk¢IQoh†„f!˜w§S³–‰pæfjSmI™›c™o f/õÏö‰÷Z=ŠKæfVgZ³JmeMn&iÍÖCª¶x‰‡©ÉT5l–¨7Xy93s™@—™®FkiÿåjôQ—8‰™Xž°—P%8YÕ PÝT‰šHi%ƒ§è•%Ææiš¸™›áçKJ‘y`¼å‹Íô‹–ZÖHdÜ8:"¶nÚ[rIC¢õŒmèfwÜçiÙØZ¥×Ÿ coÛ™–v©4ä7aóâaûhoJp" .'²›p‰;L?d–£ó£3:¢5j£»ñ£†f˜"‡’Dê\l!¤%—¤-ê¤V%Zc'úDU ¥edSê_WZD]Š¥ró†÷.d "# V6e¿ùNNÙA”t–Çô¥-¦6‰×&™§f±¥qfÞ·c’8cú8§òé0tš5âáÇA{zÿjgzcâVŸpÊž¨gn­©jª™2s¨CóCðŸ1¢*ìQªØ‘zQÞ¤ââ§ ¥˜ššª·g¢X©‘¨£œz3Iôs¹ñs™‘ò‘ßQÄŠ·Ú•z©v¦©uÖ €:œÄf¨¹:Déq¸á¡Ê½Ú¥J¯!¬KZ/~ú‰¿ôxev—Z«…•"§Õ*Lp¹¨ÃúóQ¯wÚÄôç··l©U2«s¡,Ú¬½(°ä˜qˆ‘ñŠ>p™¯Åñ{áÃÊ­øj Zš¬Û3J>ŠG ÛD‹û«ˆa¯ì±­§š­Þz¬ãÚª!–1;?óê¯ÿa³n¯xd¤Û3³`4¯ŠŠ8G>‹–Ek@+Q¦J»´LÛ´N hq´ð(µË“„Jµóë6ëåµÞ8?Xû/V{µc{lZ«¬!{giP¶öž: ©g»’ežs$–!·YxéçšùI›ü¹Sès·Í2EâG¸/–¶Ošgfxòi…vUœM„¸ÌU·fK¹*¦¸_IR˜e·˜§œqeWqÛ›˜ë<–ë¹¥ëŸ:©¶î)gdV¨n+e!6¸©Ë2§‹ºµ Cšë\úZôNzZ¿[vÐ6º¤›»'“´•†¼lig”Æ›»†{žÌKf»\;@Ñ›ºÓ»¼Õ{[Îÿû½¯+¾ªs»—K¾n¾&C’ì«tªƒ¾Hd¾¸+¾y»2í{¿<÷¾ðK(ÝK½ûÛ:ê;2ø;À4‡3ÿK4ò;¿Õ[¿¶p9}ózhsÖ7Œ¢g}Ÿ5™vä·/Ê7w‡·¶À lƒèËÀ›»¶6cET8ÁKÇÂׇÁü°³b)º¶¨çzÑJ¹ý뿜(×{l˜ºX–:¡ë¡š7P(ÓÁó(L숿+~zeÒÖ…ßø ò×|ÎŽç{¶;ì½=ììOrÆfêɉꊡ²ÂM¬ˆøÄUøÆ1̯چÓú·¬ÆLXkW»¥(jÌÆ7THW¡&2°ÿ¸¡a[òeÂfìQj|—9\Å£é"n Å?ñˆSŒ˜Ç”HZGõ¶ç †a蜨(ÂÊû4¿¨j°ÌV§ØšX¬_,³bˆÇKlƒºŸ-’ÉI¼ˆ,Çœ|«¡ùƒîÊzó)<´ŒŸÙ)É÷Ù‰Ò;ÂEŒV¼l0%›ŽÆŸàuË/V°u°ÎX¡]‹Yx Œœ,ŒN|ŒFQÌz¹—Ú¦’›˜Ð¨Œw ¶Ëz˜ŽLÂXKÈò¤|:»„Ig2¥d\èGÞì-µÁÜ¡7Œ º[¬‚·$£ƒLÍ›¸ÅV}®ÜL] ]qÔÐ\FÖ% cÜA­Í‡k®¤•jrˆÊ6 E?ÿ 1$¿‡¤f©pd”Ò<¤&Ëè… kC=_5½07}¿Î³½1ëÏýV¼QÉ|‘õaúµáèÔÉ›.³Ôí›+Z¯PMŽ‘š½5Ò>­ziÍ5+=ÓÈ‹Ö `—£XE—q¹]kÍÖ­ÌÁ= × l’`éÖ;ÝË üÍaÍÏyÍ^@Íך3ŽýØÙ’=Ù”]Ù–}٘٦RÆß|NÎ$@¼÷gU}˜ºÇYÃ{PÎú`EY(‡ ¦cÝ"W’ Ù„^]Ûõ8#™Ûº½Û¼½Û›ÍÕ]é7°OKDÈ((Ø1x’«9‰ý5ý{a'Û)'„'GÛ;×Ä©ÝüÐÿls0Ù>Ù=¡Û0ÙåíØã}ÙëÙç]Ù¿½º‹Ë ¼&܆ÚÍ ¢êšÚª­Ó¬ýܯÝÓM€ÔÝ‡× #¶ÍÝ÷Ár×à5©äáðÞîÞ~cnàÅ÷xb·ŽGˆ|—ÑLLÇsœˆÁ¨ˆXÁlgìI^áâÛîáJ®á•Ýá™íäß´„N ¼’»†«°l*•ï\X/Î]1®¤{-‹¸ˆÕyY·æÉwàÎ÷Æ|äE.…\çx>wxŽvލv‹¸)IÞäæ-Þé­Þ…ÎáIÿå–må 7æïZæ‰{æ ùuŧæîX„mN´MäÚÍàyîà|êŸ^çÇèç¦Þ Cè‡N劾áï-å­åì½è"ÎÙ£æßú éù&à”>à±ýënì–ŽÝ ¾ç¢îéÇîàr>ê ~ꢎꀎè²ÎäKå°.íÓë“ÍènȺbÖðûÚ,2‹ÂnéÁŽé¿¾ãË^ìÉ>êí¾îž®àÈŽìíþØÞäT.èÖè×þä¾ï´®»¶~ëümÀºžU3.|Õ­€´(ÝÇ—ã\WNN¼Ä=NÇÕÇyüÐk·ñ?ä0ÜŒ†â#Oò†®ê&¿ê‡ð(Oè(ÏêÜÞèdÿ?­í“âÔ½ôònÛ$ù)½ôB?ôš]ëÀmó…›ð™+é1’ó³àÛÝé.ìó%)#DõXOô1ߣdxb7V”à>> ôJ¯ÒLï‘NÏPOõlŸs•3âøZ$Øó^o¨Ì`]ó=wói¯ékßö€_À.’õ„_øE¶?µi¥TôMÕ^ æ³{Ä׆~õ·dç·È•Jd=íCe¯ºvj#}Oìø/bø¦úUéGߣcÆf÷©Êú¨ŸT%ËnèP­äÇ©ÍaŸ’zßp|_‹4Þð-'úŸ7’S¿R¯à¥Ÿííð¬ŽÞžï+ýÏï&ÿØ…—ø2‹ûlÿåí |°µk{ƒßx|oÉ(œÊíC½ë¼nî,ü~ÿ"µÝìíëÝô>s¥¿êžïîäüží1@ @4˜áÁ„<„QâDŠÀX@EŽ)n´2€ GJiRdD“*=ªt¹"Ì™2KžÄy²¥Ìœ/kr,™ÒãP¢Eõ‰TéR¦M>…UêT£3^ÅšUëV®‡ ,±aI–%K–äX#U 8KRÀܳs逋÷n^»pùÚ ¯\Á„ûêíK71Ɉ2tÜðqCƒ“'C&H9òfÍ•Rµx5&Õ¸,ƒ–F9ó¬ÅÕ¬[ŽV ³5ÿj·Œ]»>]:®nÛ¨iOl];eðà w‹\ùræÍ?ÙUúté°#Ž‹mÙµaÇ&Ÿyxñb½ˆ ›/¯°`õˆýŠOüþ°JΘíßï|?nå³ùí_¦ @ã¬ÂÈ:è€:0A;2pÁt0B '¤°(©ÃCµó.-Ñò°­‰ò2l=øä3ÏÄ÷ÜK±½Ýƃ1>‰ê;h?ý³l³Ç¬GÏ(P´ Q²mH#‘ƒðH$“T²I'Ÿtê §äjÃ=‘»í¾ñDõTlEYóËøä33±3Ól!ÛTHÇá¼1N͂̈I(÷ä3µ>›ÓóOA5RJ*ÿÒ#±ÜR­EÁ³ÈK4Åü2ÌôÊlïL)¬16%ãÌ2sdsGþæüÏÔO³@B_}5PX«’uV[oÍPDµr·íÎ2K­,]z±0Ã3¶0ï:V®ºHlöÙbïšq7‚úsÌÆlҶƱݖ?’¸wÜ„Zõ Wu•,rÝ(ku7^y+ÒuW*­d+_}÷Å®KMÏtoÓ&¸àñZÓ?…f¸a‡ïÌ5Ñy'V^Š!½8cg²·ãt;â7d‘)2XÙù.9e•eôôa—_†¹at-¦µÝ«Ð枘²¸A3þyc¡×­×cêð9i.‡f: i&j' ÿ•‚ð&¨‘|êj–v®iž¼{Т®î+¥Ï1ì¡bf»m·jêéåF[±Þh«;(Ü|¾7Ó^{Én½“kW6ÇCünbǧÜXÓipƒV»ò#Ç&»J³ÑNÚrÞ=ô…£”ø¸˜vê'Ô…Úy·­Wœu®é­)uð¤~=§›D¢;÷Ø‚ü§»¿¦˜rÏ0óC‘æœ_ä+=zé "=Ϲ»IêÅë~êÚaß»æÝþtàc“=wÝ×WÝvò‰¿ØøççŸ[ù{ÍÞ ÄæƒM["‚Q.Y‰V60d͇e¡Q·À¥°Q™Ë[oº–;³@VÅ­tê^ö2¨ÿ?½ovçÛö¨ö=Þ•{­Û îÖ'ÂŒOvZ#”üè7é`Î~XÁZô·?Ud€)à½D&”UK‚ªr%¸ªÿäGUtÂOõ\5·Â î4·Ù›±H¯Â¡ïoÃaPþˆ3ߔъdLÜk®È½/¦Q8•“! åȳf(‡!r°ÌÒ=öcÐB‹ƒž¿(¦Dt– u¢Ku «zbd'T½)’NüŒ­ç9Öð„ìJJ9 Ã9–r$uÔÐæôˆýeéJÝ ^¤°ó°E—"Ó^VŸ\îr>-‹$©&ª#RT’™I”'%鉪c2q‚Ê\ŠÜÚÏkt¤`(* š¯ƒîK‡ ÅX/ÓQ‡fê¡CéãÑOa4\öÄÌF{Ìdö¤T»àIŸgÒyUS©3Li{ÅÇ=RÕW}\Kã¦ÅNuž,Ë'³ÀZ¬žÌ‘´ÖË%*n-Э\b·jÔV irŸKr*^I/h‰©RÉk3ÿúÔâes:Ìãaçb²2Qˆÿ¥h¦7Ù·Iñc¦;_(«µ¬}ò(¸ë$”¯âvŽQ½!b²…P€ë„ll5eZ–$•4-ԛ⊔·ÛáL”µ!œl„³[Hɰw|ÒhåXÚ2 µöSíj÷ÕZÙV׺jjî)™‰Ùþ…ÙcÝöˆkBÛñtéÊâ¼ø'æÂQ¹Ù›a˦(é*º×ůlá+R¬u—}âu!üü”›¯ð„:nhE{Òö·©òíÊ{•’_ ë7»üå® Œ:ÿ²ï#ôøXˆÜƒÏÃìUjiÜ´ç*OÂ+¦†M××¼!}5Þ"pÌÈ›ÄaÇÆspc« ôÅ0Îÿ„5‡d&Ŷ§}ê‘I+å&WLÉ[¡r®FXeèȸŸEæ¤iÁÌåä^Y+Yö$RÐÌd/“Ù½b³›‡ÔâÌ­*µ²3’Û,çf67Î|vš™³’ç¸…Ѐzò¡½Fh\ýYÑ]4‰lèG+gÏ•v.£oåhL[:Ò‰vR{5½âKwzh£¶•ŠMM!:“ ÕéåÙªMjYOîÕ³ât­-ûi1öÚ׿v°…=lbÛØÇFv²•½lf—Zד#u®ŸýÙOW»c$±W«­½mníÖÓvŽ´ îi»ÛW6·¹Ï½nvÏ—Ü(Õ³¸ß’v×[#êV·½õÍîoÏ›9ýÞ´¼ÿçï}_;ß/x« p[šá©ø´®p;|âÇ8„ÞðþV9âµ¾xƱŒð‹ÜäÐå¸`ɬꔧ÷ä*½xÉ_>ól·¼Ïnf¹ÍCó:‡\æ<ºu¶/µè|þ¹ÂÏ"Õ 7ÝÚúŒó–']ßKW©Óµi¨G=ÃH?:›·alË×êcG;£©îõuÝ]k÷Ù¹u£Ýî†%´‚ÙniLû–Ürç:Ý• ø»;}°?ÖMf÷þo·75ìQÖz\^xʧözðÓûâù¾j¿›šðRüä+?zo_^vS¼æ¯k¸#ýå’_÷çIOsåŽïÀª—æ³;ïÿzŒÃ¾Ý²ŸýÌkï³Çã^Çqo=Œ_ñ²_=øÏßUã¿ù¿_ öö}Á—}“KúKòw›¼}ÑdŸâäç~ï¿OMï/:ùݾùEŽþôŸý1n?Jß_JÂï&èô¯?¥»?¨Š:ñc0¼“?žÀL¸ü@ðc;4%Ÿ ½ÈcÀ t7u‘3ú›õÚ15  è«²!+Añs¦ðÁ-×Q5T1A ì@„A 5\*ÉŸÊ[@ Ü7ô¬O±Ì"– 0BÐ ±ÿZÂÌÓô¥ôÙ@ò*Á»=Á9$ºåIÀÂëA¼:Æ;=2òÀáé"¡ãJ=ÿS4ÄØ@¯Ð²½ß"1À©Ã×á07lÃÆ1±+¤GËŸf:,ÿ«¿/Ãz-½Âÿ2Ÿ0®72Á*ä1â"‰Äâ¢Ä7,©D‚A<,!=ü0ZB?,1Î Ä‘1CC<Äß»<ÇI<ØÑ-($±6,0\0"ÄÂSTãÅû®^´Q¤D ÛÞ¯rB ü§TÔ@V|Å<™Æj¼, ¼X"¡düD6ܰm¤ `ÌFÿÊC ;1%T¡ªžnGbôEÕFêŪû5véµÐ°FÑÈÇi¾ ®Ýª±1¤ÁHÜD€,B‚¤4ÈKÌDõiÈ5Ì"ÆYÁ+ò+ÿ. ÈNªEÓ˜Á˜Ú½¡k64¶èØGÉWtƼ”tš{D—jtÅ’œ;•„3™œœ}|I˜Ü¶“¤ÉÜÛɳƛÄɧëÉ™J¢ùÉ 4É¢$J¥Ä5—DʤdJÂÚ¿¨d5~|J¨¤J©ÔÉJÑ»ÊVÌJ?ÛÊõëJ®ûJ¬Ä+7 ËF³¾µŒšVÚtIÛ”³ÿ©ôMÝ\ÞìMß»ÓT4á¬8â,N ¡:à4ÎBÁMÍSΣaΟŒEwýlÎ ƒÃ5tBLõd½ÈËO•ª¿È eÐS«O™dϕР½PztÐC«ÎíÐñüPËPãN-Q=ÑLÑ|=mQ}QŒѪë>­Q¼»Q²ÌÑw{LíQ›œ¤È6"ÁóäÃô ÇÏ œ )- Í$Ò"5RVÓHä:H;œG(ý7)½³ ­´›¬@,ÅÉÿ~TKe´Å_$ŸJd£-B½*Ì92´õHh|PDäQ453øi6xÅGƒuØAËV&å"ÿªYPüUô­V½H ׄu·YZ¥(‰µ.{Õ],ÆPÄTylÓŠõØS Ù˜=¼9.#EÔÖ).„Ç$ͱž¦àªY&ETP¤B$”Ï—Ež>Ù´»žAEZ[Æx ±”}Ÿ©U°òZGnL ŒìÃL-ÚýO¥¦mÚ­ƒÏÚ1q<Çó<Ô¡UBP½ÂETC‹ÄÅ3Ì1JH‰%Ûš<[‡M[fÌ šÕ;ìT®‰Û¯eY#œÕ® ÆZeÛNü׾嘿5Ø™ÅÁTdœr•DM<ÖVüMeÌЇ|BŠ\HG¤Üë³Ü‚µWë#%i›ÜÖE´×í×ÚuÌŽµÝÿHÅ]zÕÝk×ÞÍÍß^âEÞø1Þt ÞäW³]Þ×sÞé}»è•Öà­ÓcÜYóÕ•SÞ¥^N²^^ý6 ˼ÓÝZÂUT&ôÆð»ñõW ­ÙÍõ`í@‰Œ¥óeC mÛÈõ_÷Õ=ø¥TKµ¼ žbDTF-Å7D\O”G±\`Yƒ^¶?ÊTËeF½Ußí¬ÐF[È aÛ æ= .RþÒrDàÿ5Å(L!ö=ZstYÆ9ÆÒDÌÜ‚ì1\å\Ž,àÖ©ýÞ í\.SNa…Ý2-Eb²Tâzå“ýƒÎ'6Î Žbì³â-.”,nÑæåbÅb/æ·06ãÿ@#c ã3Î4Vc6†cHscý\ã8öÌ1žãœ´ã=ö´<Ï:æãîôã?äB®¡A¾N@6äµÄcD4E^ä¬ldGžÌH¶d¤¢dÚ„äKVÊIÎäÚääPv¹OÍMeØ$åR>åUÖËTžVV†åÊuå¾¼š*Šå öäYF¹ÎZà[&Þ\Öå:»³(4e_žº`ŽK­Ñ`m4f²fdÖÁavÈbnæ†æ^•æ®æÖ}æk޾l.ámNØnöæåÉšGç_.çJMçK&çuºvŽäw†gm’çE¦çzÎÀ{æã|Öç%S³1åg[õç>3Îâ[Î¢ææ§‚6hI«ÖÝiV¡íYffhuè‡Þ.K„᪠/mß‹n¸ŒÖèk<>"Ìȶh‘ŽN’Öè$iÓLZ™fiŒ.éÏõ.-ÚšþP—~h=YhŸÑ›&M¢6c 6è¡Fê÷5jÄlê¤~j¨Žj.Vêfꪹ©K­¶â«Öç¬öjÏãjë±î»²ÞK´Fb°®ç³fëäTë „ë¸>æ¹Àº¶kßÅëÔÜkvkxÖë¿æ²À^çÁ&ìñëëBLlê5ìr^ØÈ–ìɦìʶìËÆìÌÖìÍæìÎöìώ잀;sipp-3.6.1/docs/sipp-03.jpg0000664000175000017500000012430013730472040014701 0ustar walterwalterÿØÿàJFIFHHÿÛC  !"$"$ÿÛCÿÀmK"ÿÄ ÿÄP !"1”Ñ#25ARUs78QTaqu“²³´$3Bd‘Ò±4r6%CDÁEFSbc„ÃÿÄÿÄ= !1S‘4AQaq¡²5Rr±Á3"2ðBÑ#Cb’ñÿÚ ?ªë¿H·=w{‘-ÇŸð”{0a8›[m´Ü€DÞTUÌ䲫ÝQD‹fH4(E6Ÿæ:ãl€6ž¤URÇ­U¶W$Ÿ­j·lu¸v7&8$­°Ê¸H)•TÊãþ*ãgjÀ‹xœ–î]#hÐÂiÀo£Â›ˆ]AW 2›DÔ‡¸B½ƒ-å ݸpllæjŒ"ši§œf#³9×§[»Ü­2îj®¶'û¦g¼›¨0ä@FmÏ·-§]eÖaÀTl„I29Âäұ߿þβ·î¬—VíÒ#Áè­2!HŒn¡/2“Dy|æf¦F‚½Ë²¯ºæÿ£=Ùo èÈsŸ¸¤ýX3jCO5"" îi@•íÅÝ|íá´Êì\²¤ÙÝ8ûíO÷LF4á8Dp±ÃÂ'v†ÆN³®óÅ]*Š£Ÿòǯ ~8os¯ÿgÙ[÷SÅÿÙÃöVýÕm»iK$,Ù·ê[•Êf“¨’L$mÆ™æ2Ãn0¢Š-ˆ ¡;ËÙUbúªßoô1l~]‚ß>á&ÁÛÂZîÑÚy$ñŸ‡õ«´•¶Ð h¨á9…7Šï%Ø´Ë—:"ºçœzµá×Û‡WŽáŒ1Ó‘ïU4DiŒ:û\ÅÿÙÃöVýÔñöpý•¿uu4ÓZZÿèÃKÞ™¶IµG¦ïw·Á·ZrL”*6qþ ܪٸ"»|ŠCØU zÝèNÀ×q ǹ\UÔÙžWØŒú±f´b¶@Š"¨ ¸Oq&äÙÙZs’åM5pã £…¢céœ:±×ûgä+ÄÌa¦' 8öÿÓ‡x¿û8~Êߺž/þβ·î«òè=6Z}¢»„æýµ«œ”ä¦Íµ5îL#(Ю°|²žUÅa»hø Y¯ PYŠÍ¦TøNRqeÌr#2]Wv+Šß ¬9@ˆ€5³F\¹Uþº»|<4÷1U‘¯Îzõöº;ÔÿgÙ[÷SÅÿÙÃöVýÕÙ¬ÞŽ4{îÉ»ÒÞäèZÝ“§BE¼mA ä ½[UGžüÉú‘7 ªcô-¡ôÃ’´ ø¢·¨¡ÞZq©ŽÈ˜×i½3®¬sˆL*žr…È™Qßµjµ¦qÜ颪¢™œ#X}Z4èñ˜×¯F)Èvó11¦{|4ú¸ï‹ÿ³‡ì­û©âÿìáû+~ê¹i]íèÚáªfõ6ã;UÂñiš/›±â+[ÄÁUøÍª|€»•QEZ‰¹éxf ŽÙ” E=š‚CqVsî ¦áÆUÀ$q•PÜ!Ï«*ª(²yRéiTÓªp¼gÓºbqÁ«^M¶¢"f5é:ü?>?ÿgÙ[÷SÅÿÙÃöVýÕÑ´¶œÒÌhØÐï—‹Su_…¥×Œ]NŠ ¸ô#$EA7E'¼À‚hªª~Éè¦ß"L]=z VÕ-\íµÖcJ‹%Äm-G(O©XÂâîQ@òŠ ¦GwzÒ¶Î ”ÕÂ׆‰ˆÃŽÜ;7ë†ÍžD·®)Ã\õc¦5ëÜã^/þβ·î§‹ÿ³‡ì­û«¢Yt6“¸høzÀÛ½3N—ºÞ’Ü“š'á:ØíçáDÚbêvâȪ/rõU{\h¿º[۰ƸÊf^}y¥r†Û¨Hhf(B$ çPÁ&Sé]»¾Vº^-xªtN˜ÓqŽ1ã¢{»ÚöÙ.ÚÆÏŒª4xõvú¹âÿìáû+~êx¿û8~Êߺ».…‹¦¯:³EéËöžÓçy“0î%ÛkP­‰I†¤–åWqÖÁì8N‰í¨[VKOÙ,.F·_\°YÎtÛ.zB”É’9Ó•™D,ªq˜6)‘ÆUG ª«oœÖM5ÙN>XiªiŒ'³DéÝ‹zË ZZÇ šã>ÌØpÿgÙ[÷SÅÿÙÃöVýÕÕ4ö‡°9#Ò!Zœ™5ü-0ݺ0ÇqYeçAÃÝÝH°ã˜âÜK“L~†ìZ}!Þg –Dã7+Vä,`Z•pž- &7 ®¼¤Š«[5çÖ,ë´¦™ž«êœ<°ÇN>X°Ó‘m¦ºhªpǹêÿ¿ÿ\¿ÅÿÙÃöVýÔñöpý•¿ut]DÜm/¤ur»m´¯[{§ô¼G­±ÍÆÁ¹#òÕÃ}Î&ÑM$G‡ÕÏCö Qïrˆ­7÷ãÜìqó€é² ɸ O‚–…ˆÐW^REU«ªËvTÝë¼p?¶&";ñÃvÆ>xc‚‘‘ë›Zl¸Zg,1üá£ôåÞ/þβ·î¯ïŒ/èpý•¿uukŒ;LËN­ÓÝ>–/ƒvûÜôºt1_)­±=æÅ¤æhÜTƒj<.‚‰îLÔ®:±ê¯üVîN¾Ó~áaGÞÉÕ>m[åËúN5cglk&çŒ/èpý•¿u˜þ©Å:cú¥ÿ⻌iínxÂþ‡Ù[÷SÆô8~Êߺ´úcú¥ÿéê—üSŠî1§µ¹ã ú?eoÝO_Ðáû+~êÓéê—üS¦?ª_ñN+¸ÆžÖçŒ/èpý•¿u˜þ©Å:cú¥ÿ⻌iínxÂþ‡Ù[÷SÆô8~Êߺ´úcú¥ÿéê—üSŠî1§µ¹ã ú?eoÝ_Ÿ_Ñ"{+õ­^˜þ©Å:sú«N'¸ÆžÖß‹¯è‘}•¿úÓÅ×ôH¾ÊßýkS€þ­8êÓ‰î1§µ·âëú$_eoþ´ñuý/²·ÿZÔà?«Nú´â{Œiímøºþ‰Ù[ÿ­<]D‹ì­ÿÖµ8êÓ€þ­8žã{[~.¿¢EöVÿëO_Ñ"û+õ­Nú´à?«N'¸ÆžÖß‹¯è‘}•¿úÓÅ×ôH¾ÊßýkS€þ­8êÓ‰î1§µ·âëú$_eoþ´ñuý/²·ÿZÔà?«Nú´â{Œiímøºþ‰Ù[ÿ­<]D‹ì­ÿÖµ8êÓ€þ­8žã{[~.¿¢EöVÿëO_Ñ"û+õ­Nú´à?«N'¸ÆžÖß‹¯è‘}•¿úÓÅ×ôH¾ÊßýkS€þ­8êÓ‰î1§µºÝÑÇۃ̕Db6ªª¾¤O-[HjÆÅûm‚9} ýÂÞÑê!'ER¢*TO¢æI=&ieǪóúÁ]AËV«½èFµ `9lj1º²æÓÈÞä5Ú¥»¶Òú>ЇÊ7ºîִщÃM]³Žc±%r¹ÙÛÑUSŒáÙþÏk—Þ Þ­ÁJ¶[œ ­´äE)²4ÿFæ”… ºyUsÝ;w©cÒ:±°N{^ŸŽKë  {F?¨„œEýJˆ©R¾ŒÜ„8R_aUÇÑÿ»÷WD‡{¾‰°È²h[}ü†FãïZNJïG »‡õöõ÷­ ÞS¼ØÕÀ¦šq‰ÂftF˜™Ž½»gÕ†N»ÚDÕ5Ncþèp}AûgHå.Ùmp$¶Ó‘:Y@F˜òniHP»§•W=ÓµJ¹¡õÂ+Ök ©ÜohÇõ“ˆ¢¿©Q*í¨áé.å+M´ÔoH¶ÛlQÙȈˆ‘0‰êüÉ]SC%žï¤ì²¯±-€û¯ZQ×öu䈨 ê ¯• 2¹ï„ÇtCç²þvÞòtÙQeE8Õ8ÌNñìåÛ8º‰›WKí––•U…3¢c<.ß·Ê4õ>RÕ156˜à—6h¢â³*ß!‡ ¸£±ÉPK Ür‹…õajbÑéÆÕÚËÈw®˜yÈì6€]û*dÓ¾1žÈ™Î¥½' ÎéRÔ¨ŒD”Þ¢„Gedøç¡¢*¢ *a0«Ù¾x¹²‰9ÔTø­ü—~«/d›;åµ1LÌh½>Kò†I±É9FÒéES1§¾"z´uë\à\­PmÑZ¼´Ã|pã''ƒ¾Èb‡ÝS²ªe;þÏ]Y®‘ô£Ç%ȶK$ûd®/;-¸ë™Ü*N4ØÐIQs•Î}UÊ=$|ϧ¾éïæ¤Ô6uåHå*ì«¢gƒ*˜Ñ†:£ÅnKºLXMTU‡ g=x~Îߪ¬Ñ­ñ£=y!Æš'MáÜâ¢")/™{¯¯×[¥¯ã¥­ûK¦T{t„4~${¡²ËÈb‚H`ˆYD\¢öí_?Ò©9ñkUgU…3Õ8ί½9³eE|:m*‰îÑøw¹æ‹(XÝÔ­œQ-És$‰„=èŠÊÅóy»®¶ôŠ{WÎp-î°š;»ŠÔcQlö‚ˆª¢*"c=«çÊU¼ô«¦£^>}¾+ù½NqÕjÃ_Wgƒè ¾ºäq'kK¬¾XÎD>¢úó™eÅq¿3‹å-¹=Kµ3šÈ¾‘Ée¬ÅÖw.¡d”¾T½;»˜™áW3Éò¸¼ˆ¾´É„¯žéVFxáEÖËç!Lë·¯{½|6ƒÀã)©d£n[ÂØIâ‡ÿ³ÂGO?f»¯‘;.W(¹©;¿¥(÷+¶gg[<—Ù†3$Jsx‘„aÃ$E#h”—$J¤ªUó•)^xMxcw§FƩǫ¿NŠr-jÓÛ§«¾íë}ÿ¨©ÖD˜š¾r? ’b¥ÝÄX­ˆ³çø¤QEÙŽÈ•®šæ5±ãÔ¸v€í¼·24„(‚‰Â„jöî8_*W¥_ÏJ±Çúj1ÿîw¬æõ8aÇU‡‹¿—¤+t«rë ŠD˜N”¶Rôê •q0⻇>1I;.ìå;VZÒÙ.@È—|I. Çm_›ÉÆÐgc`„J‚)¹p)„îµÁ©U£=í(«…MÞˆŸö? U›”WO«Z¦<|ÿ:_@3醆ô}Q%‡ ¶ëPÕ›™L.®\âÚiÆ¥ô¨áWé­í#éM½1{w{fKÑ¥¿8Z›4ÝeÉO4mî"+Ž(™&å]߯ó*Ë\ò›jfšîÔN:'ÏZû<SEµQ‡{èË¥ivwî7µZͽO³¤®sç¸ûì4J„¤Ñ+¨@HY!Âás…¨ÓÖÐMë“îjY.Hº3Á>AÝ ž”ÖÔ7SÜA´Q6ªã ŒW¥YFwESU7j1˜ÃÉ}ykˆŠ­ª×޾¿÷WgSèy^“—!—æk{¬£aôÊ?{uÁmÔÎ DœTL®·ÑZñ} 5å.éVÎjå0DdÎ »‰%ÑA'·ïTDLg’¸+$gœÄaj0ðí×½ŽrLã6ÕoìÕ¹ÝajÛ4{öø€‡DrŠøG›ÅÊÉ|¦ÉD‘HKÑ{/ÓšÚ·ëè¶× Û^¤rÜëÆN¸+(ånà$\e¶~Šà¬“Ÿ6³aN¢{áŽ3jÎ&'Œ«ÓçÚlÃO‹³j#7sL ’ì-¢¤HŠª‰•TïšÚ·ëè¶× Û^¤rÜëÆN¸+(ånà$\e¶~Šà§>mxGNÕÔNmYÍq\ÚUŒuõ»°jûKvƒíß »:š8pà£ÒEB6жšärH½Ñ+øº¶ÇöŒO㾸U*´gݵž3E…1Š•æÍ•¦;J§×uø[cûF'ñ‡ßO…¶?´b}õ©Y> ^¶TԻýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûéð¶ÇöŒO㾸U)ñõ²§|œÔ»ýrî¿ lhÄþ0ûëùð¶Éö„_㾸])ñõ²§|œÔ»ýrîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}RîŸ ¬ŸhEþ0{éðºÉö„_㾸])ñõ²§|œÕ»}Rï¶­wl¶]"\¢\¢ ˆƒíº ˆ`HH¸ÏçD©U× rô—†7®U¸ú†8¶*¾´(äH?™‰ZúëæÚV½¶zÚÛN5XÆ=ÕU‰†ÅŽoYØÄÅÎÝù‡ÒQ}%i›0²dú4 ìÜ É·¤ºo3»‹¸ƒb‚;ϲWråW ákF¤¤©îÎþêÛŠ8¶*¾´(ÄH?™‰qô¯®¾n¥bœðªôÇÿjñŸÇóÔɘÇÿ$îÆ<ŸLØ5œnP­­Óœw®ÑJ òŸz8š·µl< „âá=ÕUU1s[uÈ·/þ’úHkrm‡H[üÂ… ‰ó"‘/ë_]q_À·òšÒüåÿhõ}©®u­·O·Žsf¾;úv Äi;›«ù‘ {úÕ>„BTÖ¼å+®XÂ/wZjàꙚ±Ó¯N13«­»s‹ÞJ™›­½Tð°ÇŒ4jцožu]»S†‘£[j¶rŒÙ².ÜxÕZAU4e±qÅõ©UUp˜à×uÍÉåEíŸÿjûÛR\âÎôYª.çÁØÒ,(}‘UÕùÓ*˜^éÝ+àk§ÎÿóZër-­—ôÓØÙEU¢#¸ÆuÌÏ[BóU­µîmíëšëª4Ì÷h~½$|ϧ¾åïæ¤ÕÛÒGÌú{îžþaªMq™Ûó‹o/l7òWE§Æ¯t”¥+œH¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(;à`H„¶’2Î¥ªá3ÿèßú+íö Z¢]¸ï³ÁÖ…—_I¬¡©;—ìýkÏÉL|?øþRšOöÌþÍúôª?ÑYì/3a3„c‹¥—Öãz†ÕnèÓWE´ ¹¶Î×!Åj+ퟛ±‘*®©ßžQO¡+àÛŸÎýâ×§>–Z«÷<¯éysùÂGÞ/þk¿Í›y¶ºÚU†Ý„MæÏ‹¶ˆîý¿^’>gÓßt÷ó Rjÿ¬ÎqtÑÝ#I“‘¨Ò— ºc@hð¿%r™NÙÊW:­#✞|ðþ ¼0×7.ï•ÉÓcnÞÛvg=÷}ÎgoÎ-¼½°ÛÉ]Ÿ½Òƒ¥NG•¤Ft£‘c¾9¶tÍ7xho çÞkPò½Ó8NË»×H´ˆu}ŽøþçȘà¼4ÖÆ»mÌbÜIß$›Q~ªW8AÒ¦#HÓ#fV¤Ú.î\ö$†îƒk«Ä¬)a;e9;á{Ž{$ÈÓ%fF£Z.íÜö,‡.› iËÄŒ!a{á9;e;–;„=*r|­"}?Ac¾1µñ'ùï ;½®û„qv’öÁ.äOª´‘+H”è§Ç|n ïêZrðѸæSɰÒ2 a{®D²“o®‚•9Õiäð;ç‡ðmàñ†¹¹w|®N›vöÛ³9ï»è¤yZDgJ9;ã‘ gLÓw†Æðž}汕+Ý0#„컽tt©È´ˆu}ŽøþçȘà¼4ÖÆ»mÌbÜIß$›Q~ªV8Ò4ÈÙ•©6‹»—=†‰!»£`Âçjñ+ XNÙNNø^ãžÁJ˜“#L•™h»·sزº6l)¦7/0……ï„äí”îXï’|­"}?Ac¾1µñ'ùï ;½®û„qv’öÁ.äOª´t©É´‰NŠqìwÆâþ¥§/ Že<› #"ºäK)Ù6úéÕiäð;ç‡ðmàñ†¹¹w|®N›vöÛ³9ï»è ƒ¥NG•¤Ft£‘c¾9¶tÍ7xho çÞkPò½Ó8NË»×H´ˆu}ŽøþçȘà¼4ÖÆ»mÌbÜIß$›Q~ªPAÒ¦#HÓ#fV¤Ú.î\ö$†îƒk«Ä¬)a;e9;á{Ž{$ÈÓ%fF£Z.íÜö,‡.› iËÄŒ!a{á9;e;–;„=*r|­"}?Ac¾1µñ'ùï ;½®û„qv’öÁ.äOª´‘+H”è§Ç|n ïêZrðѸæSɰÒ2 a{®D²“o®‚•9Õiäð;ç‡ðmàñ†¹¹w|®N›vöÛ³9ï»è¤yZDgJ9;ã‘ gLÓw†Æðž}汕+Ý0#„컽tt©È´ˆu}ŽøþçȘà¼4ÖÆ»mÌbÜIß$›Q~ªV8Ò4ÈÙ•©6‹»—=†‰!»£`Âçjñ+ XNÙNNø^ãžÁJ˜“#L•™h»·sزº6l)¦7/0……ï„äí”îXï‹JõM§æÏýë?9ÿí>Zÿø¾·ÿÛšÊWTÔÒa].ö(WÛ¿RÖ;Ò˼Fž¦ú´<üæE×¶• ›³¼…GZ×mÒÏêöã_-v;VËZ›QàÝÆd9Ry—¹¸²Åø¥/*È3b½÷£dÒ•Õ%Z4ŒXw·¡Z,sŸfé »{s/í5¸]eQô!jQ!0{T\Ü;¼î’6âVöšÑšo7k}Îéhz‹¹<Ó¼Æ'‰äP|q%‘EQs;öÈE!TVAEÀã´«ì‹e¢£Væ9jÓÒ¯MÊ—Cƒ~1g-‹o#öMÄ5q6l Ä„&˜µ¹³ôid–ÊXâ¿÷ÎLx·ÀuÃlÛŠ¼Læ¢é8†€"¨€›„PR‚J¼kÛ~š‹ktíalolÑ S±'«ïN‡µÍÏICáu0ÂíÚÏwMž\G££6õ|Gr f ò‰Ë–1DU<(<^Vžÿúf½…ÍŠ¾ª õ+¨^œ·]µPÆd¤¸Ö„Hp§Ü˜VBÈU&žšÂ´ÛÊ­›Ò97Š«†€DDŠ…ŠËk°Kºja—§ôÓSƒu8=Væ…·‰ü8Ñ1˜î"Dp ¡Í)]/Ñå³C]ís$^ŒÌ·fš9%6ÏKDT9ȘÊnʺ™>£HSÿÌÅc´h©\–ó;vH²}VK->ÌSâG%7¹µiU;ª»Í÷asJê²Ã¦\‹vnÛO[݉h‰=·cê&ßå–HÒ?´'‰±D»<înT=¦¼Ò!GL”¶v:« ÓˆAžè$¨H*©êUEÇæ_U*Tä‰ZD§E8ö;ãqRÓ—†Ç2žM†‘‘ Ýr%”ì›}tê´ŠrxóÃø6ðxÃ\Ü»¾W'M»{mÙœ÷ÝôPAÒ§#ÊÒ#:Qȱ߈[:f›¼47„óï5Œ¨y^é'eÝë¤ ZD:޾Ç|säLp^kc]¶‰f1n$ï’M¨¿U( éS¤i‘³+Rmw.{ CwFÁ„5ÎÕâV°²œð½Ç=’di’³#Q­vî{C—FÍ…4ÆåâF°½ðœ²Ë•1z‘¦]Š#e´]áÈCE#—tn@(arˆ"Ãj‹œwÊú—·|¤=•×+³…,Éón>Põ ¾ûóçŒUhú&!Ü*É" wöEç±Ø³Ú'J…© I¸º8ÊÕy`[¦W. o úÇäªaQQr¾ ƒ¥uDG¤~ïÉNÚ¦I‹ph]›Ò˜W#m¶:ë()¹ÝPr[EZw5è]þŒÝ¹:ŸøCƒvî^ž_&ߣvÞ-Øï™ú(*©;‹[lV·:+c;ù¾=‰|’Á'ùÍò=Cä ÉßÍ묺%ûl]gc“zŠØÕÆ9ÌGZäe=Ã…Ü›s”ÂçÕŠzW^Ó-Nåý¨·YƸÆ[]Ðdõ,r%mèÜhÚÉ"&üî [AH”Uµ5O1hÚ4î™—vµÜ¥ž] ¥½oÓjÓ2ýˆÞÓ’•h›sa¾ÚîSTUãVT9}+¯^´î‘µC¹¿𻏳`•´]Ôm•æU$6ãmÊUâiÕLçq:à e¥´~‘s[ø²ùµ ÆqÇ/­4àÁRic¸Á ˆ¼NnqC$@ADh‰CÒº]ºÙ¡§z?“qVã7s6%<ëA)¶ÜŠú«M5Í0HšÚ*üKĨFˆj}ƒšP)]{[Ì…+¡µßï±£Û\½±Õ7m¹Æ¸²±‡IÈÊÓjäV.8î)v{Ê9Ìdu•Íc¥áÝôæ·Äzi¥ÀâjNXîFT ©’J5h9WzoUDA-ªŠҕдü ,þ·“ó Ùob,,De«ˆÊfSÛ…Q^©m²."5UY àˆƒŸ‹)+5“A.«¿Gx£µÓ¬ò¥0L(“jO˜Îl<§±z— • ˆ•Һͺâ»\C‰h@•~—é’u"ü{z¨HpíœÈ‚ò §Å.ÔÈ9ÈÚ¯+–ÁÅ”ôg ¢6Œ€•§EÀUEÂí!UǪª‹ëE ÅJ˜“#L•™h»·sزº6l)¦7/0……ï„äí”îXî½HÓ.ŲÚ.ðä!¢‘˺7 0¹DaµEÎ;å}KÛ¾P:GàgùJi?Û3û7ëÒ¨ÿEy«øþRšOöÌþÍúôª?ÑVεP^–Z«÷<¯éysùÂGÞ/þkÓŸK?Š­UûžWôŠ¼Æ¹üá#ï½4z§Ý„=÷ùãÃöþúGùŸO}ÓßÌ•J«¯¤™ô÷Ý=üÉTªƒÎßœ[y{aŸ%tZ|j÷JNÇb¸ÞyŠÆ™ÚŽ=*[Q™,íGHGrí%AÎU•¸Ö»@—jºKµÏk†\7͇ÛÜ…±À%L¢ª.º.*_N\-q°Þž<¹Q¥Œ˜‘‚A‰²¶N6˜$}WvîÛ0»²6ÿF:×Ii¨¯³9›ºG~âN= Yæâ`ZpÁøèâã‘ï‘Üâ8sÑ´Î+—Ñm¢€Ü¡ˆf†ñt€ŒQ[ÎôE@,6®ÕLå)*Ó:-š ÝÖÚè§­°`ø©µ·xŠ©¦ð\&P‘S(¹« 4‹:"íf¥ð¥ÊšÔ¶UmM#2A±%ê2›ÑñRTEب¨›ýuŠå7L¹èúßhp»Î$§¥¨9nlX#x#">¥àUBÙæÊ&×AU¥X\Õ7 Ò¡$³ƒn%6úJµÙâÆÊŠü°&…²ULåMUºa-W½sk¸úToP»t¾=eäÊ¡4ÙBqÝêˆÓBöß•ÆDè›nî4!qè9Ë1ä<ÛÎ2îpG Tl„w§©7¦Wé$O¦±W^ºzJ´­ÉǬ÷Kl+­!\fFdäu¹(óR|· âÙ–<*íU‹H}­¬ÖÍñ÷îk<Ú0º8s G&î #¤hâ§) ¨ *8¨Ž Pí°%ܤxMrº :ùä6Ódã‹ÝSÔKZã •íYÓ8¬_E¶Šr†!š>ÅÒ1Eo;ѰXÚ»U3”T©È·›k—mZ%Å ê,€`š‹Ä ¨¾ZdT¶¡"Òå@äÎåAî¶ÍÓ-ú>¸Z$Ü.ás—)™h Û›&Ù !ȯ¡`¹ÑT¶yp©‚õÐUi]AífE@³4S­R#@ž+ޤg6÷É`–`¶Ã=ÂÀ«†*~bqrjM}§.·K…ÎZ\îÂ:…‹¶Ý.ÞËqÂ2ñÕQÂFùUÏ:ˆõhHòF»C˜F"SŠÜfxÐ ÅÁIPTˆ°ŸBªªýŠ¿ElÁ´Îl¸Üc6ÑÇ·9+/€˜š6$€ª„I¸…E“8ÊW^¼ë™ZZ=š zÚïsuø­Åö$3ϱøí2˦Qä:¸Ò¡!º†›|ƒ°ª«oÕÖ¸35À/Ú–]Öu¬"Ƹ̊ӯ8ú<Û¨ánuU¨ËmŠ¡8B¿**"xp%Ì6Df·µ„~InDØÚ¸ ¢÷^þw0™^ùõ"­jÕöáÓ«ªn7è':§a8ÛfDF\—-ØÉÓUq2HnIR%ɯuCZ”¥æ£ÒwÍ>Ëo\˜Ä{<ñ¦³$G7·¹Z2Ù¼2C»‘G(‹ˆ:èZ‹[YJïdU³­´Cð×. LÄåÅÈm#"."ºè ìÞ‰µ0ê¡!¨¡.Ìw6±Ò÷IÝKtð‰§*MÖK"“]h1_,µä>êæ?Ä9äõïrÌy6óŒ°ëLjU!Ä©êMÄ)•úIé¬UЭZú\Õݱ՚”móá7™±ÛFˆ€è:áhwÑõªÐËÓ–çã*SÀqØð2Å%Tàî êŸéÉ[äzKóo¼[»Ê8×v® C|‘–íqÇzÆ&bà‚ª iµÈ.p!ËëzÁi}¼Å´[iɲ€qði ×Ô;Pr¾¤L÷UDLª¢U½OhMkj¼Üuª¾$Pw’tö€Ÿ!qm«Ä¨‚k¹L^ód8ÌPÖ^ã¬4\ßHzkU‹à•¯‰ÙŠ6ä7%ºÔŽ@U'f8k]ŠFd¢‚"(ª+¬¥@ÅnY0èÇpɰuAvŠ ¢ú•Q USèÜŸ*^=à,S¥€ãNˆîÍ®Ýl±\qpŸ@9ÊÝU<¥Ý~„K ¯\›ºH¬W[桹Rd,[J‹lLmô шPÄm Sà§1ù;`‚Ž,ErJ[1EtPÕI SÊžUÊ¢a;"ªnL⫤kí¡ßEïi›÷Pœ„”ܸ‘z0r$cld&À%}QÅxH‰0¢½‹×V­e¯4ÿKK"ã©g\l´üÛ{E—Z¨D² SÌÌ•ÊeG­{ÉoCJèÞ“µÅ›TX‹oŒìc BóqœŒâ„&ö+,¸rœm2)µ¶ZØ Ø"œæ‚cJi«Î©¸ $f¤HC!rKl¦ Àl|΢ª›€(™Ê©&µo¶™ÖK™Û®-´sâŸAÀ„ÁTIHU}u½ .P,ÚÞËyº”‰ošÔ·3Bã…ÆHh(„BÔQ3žÈª½ñ…“Ñ÷ø?‰ó|´õ¶Q®öèâ’øƒ‘’x6‹›ÀÉÅD&E0]ˆBªQä V哌w ›Ta © /©UÅU>ÉùÒ±WF]ušm©éú†Ål[ŒÙlÛ-/'Ñäm^Œ•H¦Çi"6â|q®ÎØ-­!¬´” ðrìwuGâÉnSMBäis‘tQ%6Ù¨e¢Eu£4  ·°9ì«Lè¶h7w[k¢œn¶Áƒàj¦ÖÝâB*¤ ›ÁpH™BEL¢æ´jÕr›¦\ô}o´F¸]ÎçSÒÔ·6,¼Àƒ‘RÀð*¡lóeë¬lêùÝ-’.¶›Èæµ)ÈÌZ"Eê *Ùm"¨’e)ß*‹„ ¬Öõ‚Ó:ûy‹h¶6Ó“eãàÒ¯¨w å}H™îªˆ™UD«Âk8e¨lrnš»U_áÛŽKÈsà7ÏÃlPUµYD›€pmmÜÙ ®äÞ¸ë 7ÒšÕG"ø%kâvb¹ Énµ#IÙŽäb‘™(£`ˆŠ*‚Îlö™×w$·¶œ8Ñ]–à›àÚñ6*F¢„©½PQWhä°Š¸ìµ£Vý%3HÚ/óäȺ_!B‘1·ji\sž3Œ™¬„@Ø®e·"wÛRzWÚtÅ®í«¥ð9Ÿ5ѰQ^ppO5(v’ýàH×*([9í)J ÍG¤ïš}–Þ¹1ˆöyãMfH oor´e³xd‡v7"*ŽQuÒïZÛK‹pB¶I—…<øIŒ-ø“ÛFU·Å]tDUµ5 "nQ0s(àË»é2Ìz†5ŻΡŒ­Û§E9Qá¹Õ¾Úb¦ô÷H›l²èŽáA1Ê&MHCŽÖQ ¢¹,Xt£¶bÙº€»É DU}Hª€J‰ôí_̵ÓïÚÛH»©–çbvùk'¡=&3¦¤ÛÿÅs4Lì͵œEÆæö´‰ŒÅ¦´_H˜+¼/ú–Çcñ$ Û™÷]e…e×mBªŽ™rîEßòÐ9¥+¥ú<×ZOZæ[ƒ&3NÍ7ÑU§%õ,ˆ‹¹…Ñ ¥ØÄĹOʤ‡L¨®ìËin= £¨)¹hŠo೟Ž&ÕÌ÷\.ì§l*a(5o¶™ÖK™Û®-´sâŸAÀ„ÁTIHU}u£]/Pë 9rµÜm^%|v‚B‰nŽå­˜À´*ù¢>¸"vryâ’Ú!kj}gk»pˆÉ¹ºÑ]˜Ë2á´ó6F~è±›']iw‚mThU#¶Š8_ P£G‘)Ån3¼hâ‹`¤¨*DXO¡UU~„E_¢±WT¹zOÕpn6ë¾¥F¨oº²ŸmmC{m»%åܱÏóE [m0ÚŽò¡êéñîW’–ÍÊïu2G§]9ß4í•#Úˆ;ELþNr™Ú![ÖëÅÞÛ\KuÖt8óCŽ[LH6Áð¦ÓDTBL&>µüõ£J)J)J)J)J)JÁøþRšOöÌþÍúôª?Ñ^jþ”¦“ý³?³~½*ôU³­T¥ŸÅVªýÏ+úE^c\þp‘÷‹^œúYüUj¯Üò¿¤Uæ5Ïç xµè9£Ðí>èü!ï¿Ï·ëÒGÌú{îžþaªM]}#üϧ¾éïæJ¥TvüâÛËÛ ù+¢ÓãWºJR•Î$ R”šv8HÓ-Æ»4!}ŠÛÊôÐHíÅ5ã†HEñbM*ïíå\íOUN]=È…6ÝÈwxP¥„ÇÅmKPFQ÷ˆYä$4PT@]é¸Ð…v¢nX‹æ«ujŽZRÇ«f‘aÉ~vyp™-Ï—”ÓUTÁ÷Â"b±&¬˜ÕÎ4ˆp`Ã…i»k(çO±àVßEÜjâ«€ª$j{Ñ6  kjkD{Y@‘c²à\bõQ\y„e݈ë! ïhñ‚,ŽÕ쪨˜¬v+瘡 `i¨ãÒ¥µ‘RÎÑät„w.ÒTåPIQ0+† ¼9xz7øHУDc‚,XÛÕ¶[ÞN*"™®MÃ,‘*䕈‰–Ã}[\YPd[ Ý`I6Ýr,µtC•´4š0<¢8âcvÕÞ¹ET@’sÑæ®Ͷ4¨Ÿ„¨3XUêZ-¥*òªù[O;‰Ü“½FÆÓwGìËvÿÄ]†àu7qТ“m¸hn&D…rBB™TT«,efµé(mµ4·ø·yêëp[ÕÄiX$ªj Ãk‡€Ñs…ÎÕßYMƒ£¤iˆ°£i;•÷ù%Ȥ©’&UÕaI¸²;D‘P… 3¶éoyþ>Y½$Vvg›hox·"ùvoa0©æåíòJ¥õ^“f‹p(×7eÈ´\FÙs"£@ Ç !’¸ÞXwÌHÚü/uAˆñ]Ú[ÀÞc“ŠoWíøáܨžmû\ªùx»|¢­íG«&^â¾Ë°`Å9’’mÅæÍód"#§¼ÈE~5ÕÃhñ‹åì;CGNX®:†s­cŸmƒ|‘ùmG0MÆ»!EÀä•3œ"¯©Sgà– §†½oéŸF9Íd¼Û-6Þ튦á’`þ-P•ËkƒMµIßWO\Ýœ6È7r+ÑU¹jîÄA[5N3ÊúûnUõ¢*Yn“§ÍÔ±uéûCãmÈÒ'0¹yÓtËpHBEÜëÞ¥DÃ¤ŠŠˆ;B½N<ì륹ÉqºèPŽ[MÆy¹ ÈãAqÁG[%ÚÏ+™Ê÷mCå*%AÕ‡áLƒ¼Þo/Ek¯¸Åv;n6J‰—á`·+ŠL«­©©*¸¦¤¦›–½@¥)Ax¹èžF™·G•s[† èúc—là„½Cm’ì}%sa:Xm>ŸÌˆºÌh¿‘iM+6MÅ‹“òc‚ʇÀðvÁ×W³uHQ·Sj©’¡"vîÖ-g=®”íP-–‡ÙšÍÁÇa²IÏ%ÜnÕ7±óªm u„–äB[}¦Ùn…¨ÿÇ12ïPÚ4þâqÂsÎØˆ.ÓL"dv–ICe'ßHÖÍ"s/zÇÚŒéÜ,âĆupc™r>f×*H¸%TEÂn¨Uªß­^…©mצì6ƒKPÛašÈàˆ¢ê¼„*Ž£„¼„eç2O:¦0‚‰_ºIfdç$G·Æ·4xÛ18M†²¸f]ñžä½Õq„ÂPjÒ” R” “Ò–¯Õ6›?Oâ3Y‰Í³! nÛ”Î3œe3S’4œ/WÒ&_!DµBwºÎ,Los­¶" +Ê…•y¥ÉùT—¾ µk—ÐÎnRÅ,G(lIo{n ¢¡ §eL¢¯˜UW *$ˆ©9ðÂOYŸ ¶xgEÐx_ÇtüÜû7rsñ™äÎ{gg–‚3SZ¼îP…þ¡¢a‰,¸¡°‰§š[Ü9]¥±Á܈ªˆ¹DRNë/¥4œ{Ì[yI¹»EÞâVË`7(Öy‰LU¶òû^aGåù{"lëêϹË>ÙJ½b°Ù+¢[i[Úhª "œŠyÆKr÷­9«&Y"°ËP`Ê8r–m¹çÑÍð¤*+¡°ÄI~)¥Ãˆcñiåî[ƒY›Dwtd»ðÌu$E¸±ã+ ±AÖÝ14svrŠÉ"Žß¥+ݬ0µ3Qt”:ºrÐðI1uÉnžutÑmÏ+ÈxÑnÕí¹ ^ ˜½i»¥š(¿qèZU4l£ÁƒÑáUEÆDÕÆÕ0¨¨b›W²á{T=Yµf²›¨mpmnÂ$# ¶ü—ö"ÔY¸­Š'ú[QEÀîBÚk4 R” R” R”z^ÕãwØöÎ~mË” æ[EKca”ÞéchSqŽS9«+:“]EÓ}UÌ:˜OKâ;f. q´éñ^O+¥Å‘ýÅÆË)» U±]$Yîa:84â 8ÓºŠ ëN6ãe…ED "QT$ÎQQp©&ö¨W® ºõŠÐäñV#6ÓU†ÚW Ü «œÈ¼„G¹ÝæQÎÅÙA'eÑqî—›´HÎê‚Þ`ÒÆbÊŽÜ”×vå(¨÷‘° Q"Þ¸Rm1çò­zC±n·9N‘g·Jz!N²@[€´ˆFæw‹(*%¼‰3¸v¡"†Šk .Hš· M²ã WOþþae®µi¤Û‚瑲 MƹEÉn,d…®îìÊrt¨ÐnüEˤiO‰Ä˜âŠ›À-‚ª«m®ÓؘE$PªÒ” œ»Ù"C<ëWOˆ»Â&ì˜êØÆ^wY4T5!h—r&U<¨½«zvŸÓ°îvᑨ§G.)È2~؉-¤@RkâEÒ;å@RpW¹PA@qÖ-K+*Ž’ÓÑÒÎ`±ÀRK€ãB麬¸.¼hM‘¸J½·wÆQ;Tn§½5|•Õ¥– ¾A¸û¬=%Ãd¨ªF¯ºâªç+”Æw.sÛ“XYàYÞ·xuÆLƦÂj2¢ w™Üf‚&Ú8xÜ îîú•2ÓÖxísn÷{Œ˜Vø³V,Añ:èºAä'M»Ys+»(»Qrª˜åßVv§zùq¶A˜Ž™BjèGÛ´Á6  aSÔ˜¥†ú¶¸² È¶AºÀ’mºäYjè‡+hh!4`yDqÄÆí«½rЍ*«¨-r,—û–Y´r Jv+¤Òª‚›f¢ª*¨‹Œ§l¢Tä 'Mš3§su»œÛt›œHé …›~÷w¡ ‹Ó=FÉÉ’M˶"má˃×Yw(‘¦Ü.OóœÇ7‹8¦¦j!æUTT!$Dù;kz¬™Ì܃ä1èQ§š9Ï;Üœ !£j…Ì÷r$ä\`v…z¦5¢=²’\iŽÉ ¥¹%ª8Â6­<ë$ˆ·"*¨]²Š“ÕZ2$²ì±Â߇XßÉ%²q\‘¹r›ÐE6ú“`o^W½KêmLÕîÙéËEµ w"•4iMǵåxÑQMÒ,ãw©3ŽÔ­qéšÞÛ¥äKŒÓ³&±ŸŒór›RÜ„Ù(ž7z½h¨¸\Ôœ'‹ÕÆ4‰—ÈQ-P†]Üëmˆƒ ò¡e^irF>U%ï„BƒñVBûâLYmŒ±êX\r:ŠŽÒŒ24Êew!î\ŠŠ ªIü0“ÖgÂmžÑtñÝ?7>ÍÜœßç|fy3žÙÙå ’¶ú;Ÿ:ï9¸és›mˆÄYí¾ÜRdJia8“ ¹,ší$C%؇­§´Œ ¿I¶ñ%Ÿº9k³æ¯+£Å‚‘ñŸ+Î×påTóö]©»Y5„—$M[…¦Ùq…+§ÿÿ0²×NÚ´ÆÒmÁsÈÙ&ã\¢ä· 2B×wve9:Th7 þ"åÒ4§ÀÄâLqEMàÈAUU¶×i‰‚lL "’(D[­]MŠéwyþ ð¶ß“w3î’íkבø°|÷aSâ¶öRµ?£tË®bôð-îŠcfmLÂB³€ÛÔ¢"ŠIg¾{ùþª)Õm×^šÅt´<Ç;¸\oÏ·…ö‰v»êÉ|Y¾r‰ñ»»¨I®°’pF;Ö›cΛ E—$¹‘É‘™VÕ¶DqE8YM͈£iæÊ’lêí#Ét˜Ü{Ä™¶ûeÓÂîo¬mæÜ~`o‘QÁ!iÅÞ‹$$ Š–]W£m¶+üx¥¨+g‹È´Ëšô†Ã‘Í´yÄhL÷¶‚èù‹›G«£©u„›íÐæH´Û#0üÕŸ2~afcêJ¤n*¸¦½ˆ‘ ‹b ‘*µ¯“ª5»z“RÅëšWÀž‚2žFÕ¤-ÄËdddØ–K².Iv¢&‚í]ªé.×=®pß6orÇ”I2Ѝ¸T^踭ZÚ»O—uºKºOwš\ÇÍ÷ÜÚƒ½Ã%"\""&UW²&+VJRJRƒ°~”¦“ý³?³~½*ôWš¿Ÿå)¤ÿlÏì߯J£ýlëUégñUª¿sÊþ‘W˜×?œ$}âÿæ½9ô³øªÕ_¹åH«ÌkŸÎ>ñkÐsG¡Ú}ÑøCßžèü!ï¿Ï·ëÒGÌú{îžþaªM]½$|ϧ¾éïæ¤ÔvüâÛËÛ ù+¢ÓãWºWODÍÊ[­ÑÆ®p`ÇrÑ:é&æÌTy^Šð´pÇz+¨ß«(*‚«ŽËSžŒnŒéè¯ìšL;‰ä~ŽÙÎ@BaÖ ¦²»v‚ #îMˆ‚…c±\o<ÅcLíG•-¨ÌŠ–v#¤#¹v’ ç*‚J‰\lÀÒZ‚oP-[øÝaòŒ¬Èy¶qñÆæ[mÂuÔÈ¢¶DŠB˜ÉyÄ‚ßd¸¶ÞŽ„Ú]£5dnÉ=›¤šÍp5•Ó™FRÜñ&ø˜uv™8×o>´Žû¤@àŒþçÁ8¤½ÄÉù“Êg¸vŠú”·+”õÖô-7t—`rúßBÜ7ÞýÁ†MÃlÌA³47ð¢ü¤DïÚ¡è:¦¤ÓÚ®—dí–»5¿P±‰{nT‰6÷H•×w’žÄVvq†Qù“-Òß àjp¥²Ñ %ŘÑ4WH¢ú6‹UXžù¶¤êì"qÁoaev‘§&¥«Òzn)Z iá‚ò±º¹Ñ‰ïñF®š6d&á*²Œš€ãi8bXQÚ- Û&-À݉h¸\ÀÙHñ.·Ž:²¨ç+›ùZË‚HÊ ïî†k´±‘ªÒƒ¬èm;¢®²®EzE²;÷wcé—tQÁ6E%”$5ÃØ’‡³ä&Ü;ÛpE7›s×kB ]ؘÌOŠ«†š”Ûæ òåUUZDÚ™q6¨îL-P©Ax¾Dš^‰l"ýÒØÿC6cã/Üy–¼h,£Ši“GUA"»”‘3šËéݧmö xÚcZ”Fˆê35%HT@ï—”óD™TÜJÛ Kµ@1¼B…YbG‘.S1"0쉶ÓM‘¸d¸ANꪫ„D “ÑÌ[dê»]:ƒªë¼m¸ò6JËnScfê‘n *înN¬µÝm–ë|…±øŸÁáoUÔMHrZžfLò£Ü`ÒÆp¶#ʸA#ÝÈiAÖRäŠó}Xñ4¬Ø«h.؃¨ºfÖYpò¶*óÈ|j½B˜8ˆâŠ"¶¤*°zF{¾–ÕÜm=mzH"C7/©ÍýDsXüoHD6PÉļžu$DªoA´Îl¸Üc6ÑÇ·9+/€˜š6$€ª„I¸…E“8ÊPt(Ö3:Íeˆ‘4ôI³l2$K™ð‰±Ve5Ȭ ½µ·ÌtpO(¨e°ZVÍS6m5ðïsj¶Nèì‘Ý-nËÔu.»·ÕØÂâq›Ž e°›‘ÝÂUBLâ°9}Ú( ʆhøoHżïDTÁcjíTÎQR^.ïY™²½uœå²9«ŒÃ9¬6k»Ì «µÌ]Ñ?Ô¿žƒZ#*S1›&„Ý1W]ÁW ¸‰PE?:ª¢'­V®~”âM›« ÊvélžüøP)xŽþ_Œ6êºbâ |bLÕp«•DUª=o-¦p•µm¦’æ‘ ×Àƒ”ÚÜDªˆ ½³ERTÆ2½»ÐXt=m¹_¢Æ¸Æ{è•‹l±žÛBÛã%•5nNäË"úoÞˆHª(«½l®ÜÞr]ìtÍþ4Dç…rÍ £pÉî8†±$ŒEÍÒU²,òª!¦äMÉÎeÚgD¿½b–ÛQç³(¢:¾ º'±PœU؈ŠÉWoÓœw­Åg¼Zþëö+¬}€u\¹Wxé "«I*ÓjÉ(¬†ö ”FDMSr¦ÔÞˆ\v²²ÁºÛÎ4ˆÈ!š¢*©¸GÊŠ¨¤¹$ì9\eq„UCÑä2Û.<í„€W#Dpˆw ¯­7 &SéO¢‚ñx·iØÞcIf5¡»£ Ò‘œÔ~I*ª*íá”H*©ßk‘›@Šš¸‚§èÖ,þtýºç ©°¦ÜYŠû.Š8h ¹EMÙNþ´Lå2‹^«U»NêëEÎÏ&Ù!¨w;°%Ý—™ŠÞDäm ;’ `‘S4‰hÑèlqî£܆rFC.a9²l[ˆN¼.ìäqÝàædPE ¢ï­ä±hÒô€P’-°íŽÙuá;ÜxÍ·%ÄkÔ”ð‰) )µ×îfJÛQºé]Ns£È–±§¿t}õ,ÝcÊpMâqÐp6‰¡‘¢"*’®V´di«Ë7?XÍ8òÅv`2[u§l Ü0tI@ÐQ·v’ù€‡å"¥«Elj6ÿ¨vÛ¦­ÑI` /«c¼±žF äáÑqͨJªè`¿Ò%ß´iÕŠ¬ÞFÐñ…ÅÆ.ï?s@8QDz&ÇDd8¹}p(ò/x2o¡Rƒªz4jÖ͕‡"ØËó¬—Ӥɾµ Ð’m> ÇVœpQÆzrÊg “ œ®” R” °è§1C·\Ú·ÈÙþbÍN˜o0jI¢‹(¡yÕG#¸|Û¶Ò,ˆòµ|·ã¾Óè Ê8àçQG78žsz÷‡Êénq‘+GNX®:†s­cŸmƒ|‘ùmG0MÆ»!EÀä•3œ"¯©SZé]²s¦µÄûxUD$!$TBET!!T!!UBEEETTZŸèóMhëηð´ô)E)ÑzC—ÐbB²¯Gm›7°* ä³"ecµ”$%b Ùì=Ïzs5»7Ìó× DHJ­‹ćÕEaÑUÁ©ñ—“žÖõÖÓ:ÖÜ&6Єø£.16ø8†Ò‘r »U WŠ*ЉA£]/RÚt\_ƒÑÜKe¾3ÓY ‹±f¤¹m±Ù-ìÉ}£Â*ª’´Ê‘mPmSx4¥^Ó%ý¦¶é«R-wF'-³V³Ç5§#lm’W$: \ª8UÊ*’Ü4«Z6‹ŽzíkŸs[DP¸Z @Û˜¸ƒ¬7-Úé@m¢ÇTw> É”E\qW/¥K‹nÓõ]ÝléÉ1Ÿˆp[~øÌ1i²mI÷˜/“hënq£HNº¢„»…ÅQœ¹ÀÑ÷Ik"sºic&¨šWi݉“C”wAß1yÝOŠÂˆ«È(„«Êìv+瘡 `i¨ãÒ¥µ‘RÎÑät„w.ÒTåPIQ0+˜KPMꫬ>Q•™6ÃÎ>8ÜËm¸BNº™VÁ‘HS!ÈXc[nè¦ónzíhT »‰ã‘UpÓR›|Á¾\ªª«H›S.&ÕÉ…­›å£E£Vç[\iۈŎâ:’YÖA(#À`R”ɱRu0/” y F6š¼É³-Ù¨Í,}†è‚Él_q°Î÷•.ClvžLEE6U6!è/”¡ÙmÅ ‹=¦ÆÀHa‡~ ç­!€9Úi%Æô"UQ5*Dz7m™®ìÑoMArØì¡‰2WNв¿,•Íá…É'›¹"& ;V½Yb0r¥3²hMÓuÑlUp›ˆ•Sóªª"zÕh/º~–[Éy…l·±"2ÕÄe3)í¨¯?Ô¶Ùªƒ¬†ðDAÏÅ–õ¢Ç¤ZÞØ(Ð]·2 «UÒ/‚ ÎkznÎÕlÚhd[†M^djÕÒÆho (á«Il˜UECyrŠ‰ßºá*©Pô L[ô¤®c˜7+£¡pt§ºÇCɱè@ã€o¢¾¨$/ŶŠ,­®$×½Ý#-ÒØÒ4øðݼG6¶Ì xÑ’q rÒcnãM¸BDJ£Òƒ¥­·J=õy‹M#Ek….ÝÛ£ çPÚ±Ö4€¯! ®_M®/!¦ÕeIVµ½ ±i“ªB}šÙ¤#ÛäÝ `±âIÖ0g'Ç™R( íEàTä\"m]œö¤Ú°ÝƒcQ7Œçш ‚¯È%UVÙÏ! ïAQÜŠ9ܘ ¹êKM£ÿP »1ûA[¤ÛšF™böYMÁHæâ-Pí:…€ìŽüV$m‡9¹M²á¶*ëH`eØäB%-¢*ªYBZõ*NÇb¸ÞyŠÆ™ÚŽ=*[Q™,íGHGrí%AÎU•¸ÊR” R” R” R” R”ƒð3ü¥4Ÿí™ý›õéT¢¼Õü ÿ)M'ûffýzUè«gZ¨/K?Š­UûžWôŠ¼Æ¹üá#ï½9ô³øªÕ_¹åH«ÌkŸÎ>ñkÐsG¡Ú}ÑøCßžÎåÌþÞܺÛöEñÛ¸0Û¹sâ?öÃ’_ó²íMÐzzÏ]®mÞïq“ ßöc*ň2']H<„ãi·k.ewej".USGPZäY/÷,³hä@”ìWI¥U6ÍETUQNÙD®q °Úuž>ˆvÍ1$É/0„ä µ}ÁAÂVQæ6ìiU±BCVp¥µÅA¨TÄm5y“f[³QšXû Ñ’ؾãaî*\†Øí<˜ŠŠl<ªl,l¹£/íÊÙ‚'#;!Vå`·!Aq “i6¼­äÅTGz)*f‚Ë|×påʇ!.{•±»»ÓS£6c6Úš¤v\ä<6"jÒ"4C…QM¨5½jôk‹waÙºƒRÝ6BžÃ·yù !¡m¸î4ãä&ÃD*ò!¢‘*lNä´ÍO£ï6 ÿ‚>°gM)G¶íÓ”fàšÝ’˜*ª¢ ˜‰/«EDÅ#Hj˜³¢Â§®p—½YI±Ê8’n2Üâ" €ùˆ•Q;ª¢w µ³´,‹|ò»j½9hXsï)[:„uhù·:¼y’&ÕüéñiípܲŸë¨5,ö:ØW(“„‰$üf ¤NBFˆ„…ôW 8@”MWµzߣï2õm»Læ 2î&ã˜ÙFq Tw ÀD™B"’îE$ÛYfhë´‹ìë~˜sÔ,CãÞü8Âþ7ŽSrÇ7A;îDÁ¯É_R¢¢º¤ Z3«Êͨ5-½ûµínÖõ "°ªî4,‘“¨ã}ÔLå A ÃW6Ï¢¸ºr-úùàËòˆÚ Œ›ñ‰GG¹w‹X2 ¦f™*DYt^¤»Ê(‘ ´Ì”°Ñ©²Ùˆg!Yx…MÄUQ¨î¢nLëFÓW™6e»5¥°ÝY-‹î6Þà2¥ÈmŽÓɈ¨¦ÃʦÂÀ^×ögôT 3E:Õ"0Déâ¸êFq£o|– f `ñl3Ü,*¸b§æ'záé>ÖºŽf¢6ù"K…›¦1Äj;lÆó¬ˆûÁÕâååT5.EhHò¦»yÌûDv4•ªúÌÇ]9²¥Eyƒa6Q’È’ïB‡Öƒ…ENþºj+D{d+$¸Ó’KrKTq„mZ4yÖH;nD&UP»e;'ªƒg[]cÝ¥GxoÚ†ý Dæ]ð&œ‹bŽ*".åÝ¿¾ümª¤Ð·¨öK„·^“:ÈŠ¬3q‚(Ra #h¤R&—>WKºü’ÑÓ–+Ž¡œä+XÆ'Û`ß$~[QÇŒq®çHQp9%Lç«êETÙø%¨éá¯[úgÑŽsY/6ËM·»b©¸d€?‹T%EG2ÚàÓmÓMëËlCyºÜ¯Ú†l¹A–'¬.7Ým¶ö;Ã-§|­§gˆOnã-ª%wÐGrÖ.µ*ù%ósQbÎÂŒf–K1Ô yU¢m;c”ù)Y¤ï’gJ…ˆÒ‹± £ÍeÖÌÍ2 6àš‹®–k`¤d¢Hˆª+ý¦uŠó*Ñsm¦æÅ>7Á·ÁÔOXîQÊz•3ÙQQp¨©A9m›¦[ô}p´I¸]Âç.S2з6L ²C‘_BÁs¢©lòáSë©;ư‚ÿ£XÚj<û¹¼ Л 2mT%B’m8ˆ©ô2ÑaÂ-Ûê¶ëWSbº]Þˆ<-·äÝÌû¤»Zõä~,=ØTø­½”†¥âh©Ç¤.Ú‚a;!EbSM ›ÁÇ›mÜö‘Ñq½Áñ‚„£Ù2¡U«5öe–ñw´Ì—:Lv׉‰/3‘œŽ×N(ˆDÚñe·2„ˆ(ê§uú·Í3q¶LµAãêåܘG޵$]UyÆ„Z6LÑ̨cè]ÙvET+wftXB¶É/ÊÞ ‘.±¤ˆ &ã' · ’‘¨¢"çªo¬lÿLÒ5œ Ö¥‰o“4§¾Û€wt„t¢’$ŒJ(ˆ¤«ÝSä}5—Cjý3§,wk*ßõQE“)‹ÅnlP #:غ¡Ôà\œmįx­.Qq² öŒ¿µpf³Uè«0 ”rŒŒò{ÉôqZä1'›ëTEAÑz’t«ŒXÐZ7mÀÊE–ÈìdÕ6¼Š¤ˆLà„•ÑÈ ’’ ¢ÐZ‹Sécojñ©`Ù/‘Ä·1liX—Øy©ÜN)ò2f²ñ= Ér¾êTè!,1¶´îbJæãN8d~]›Àvq–ED!Ê(khmqÔš‹aŸg&ôG&95!UÎÈJÐr8†DŒ›R@Þ½±˜ˆÚjó&Ì·f£4±ö¢ %±}ÆÃ;ÜT¹ ±Úy1ØyTØXz·ë=Ag»YbGŒ’eKg„zL#¹†ÚãFÖ—2û#iÈà‚Š2˜ä$LQ4TãÒmA0Œ¢±)¦ÐÍàãͶˆî{ Hè¸ÞàøÁBQì™X‹ý«Ã<=ÖßêcO„ܶÙ³vrܪ¦ÇAÖò¸Ý³rv$ œ©àD¸ÚØi™!h‹k~ˆ‚<¼²£›rdmÊ ’ªƒÜT›e‘%EMɳnÔ6Xº¿KJaù+oÓ,oeé,q¹1ÆÞzX‚€£{ÜqÎãDLý"”zP)JP)JP)JPY½Ýl¶]JSïÃ%Ȧ8˜Ëȯ2là“•¥Aʪ¢H½°˜Îä°hHQm×;¬Ë«ÅzXFn<˜öøÌ° ØÇ9,8­¨qåIÒÏ©!’¡9¥YÐÚ÷IY¥\œœÌä6îì—¡Ä·qÇ~"¨íi@%¶ˆ˜äN'yÛTANîoÖ=m§bèybÓrÔ1•-Kn4s e>ú´ðu$ÎÛ‘qwŸ›f[>_J ±$H‰)™qv<†L\iÖDÛ1\¡ §tTTÊ*WO¿úMvyÆÉoĹÞãÜní“Âð¨6fJÛŠMžÆÚA!@T„É(ùÔåt ë:‹]é{ÏOÜÂ]Ý'ÛeÄC‘P©Aan-¶îÞ¡¿\o®ÃVÏ|Xï¯W2kΑ(¢—‘•Ç—UL ©"Uz” ê5–’•èÕÍ2áÝÉä·2„áe¶%‚¶DbR4(J.ð`\Úâ©)’Ÿ& ô‰áñô<ˆýK-ý;ÃÏoâc;µÇH¶šgÓßt÷ó Rj;~qmåí†|•Ñiñ«Ý)‹ õmqeA‘lƒu$ÛuȲÕÑVÐÐBhÀòˆã‰ÛWzåPU4nÓåÝn’î“Ýæ—1ó}÷6 ïpÉH—ˆ‰•U쉊—ÐQ`κΉ>R€­[S3eÖ¢ºð8;I2¨M¢`²*н½J•êç =Y25™¸# Èb+УO4sž<.€¸¢¬¸Ú®äm¼¡)"lLc%™Í3é*åaÓƒag¶=X}‡ÄߘØÉw¡«­´ø6e´ö¡¨îDÂùGþÛmržgVêm€y‘z¬ÈêÚ<áäÛ$m9ASj/›Ê˜MÛ× +eAÛõ2»áf–Dt˜ˆIJ3½Á'·²*‚Ö\ÛÅ•$LÐ ¯MÔÍJÒQ´êiËC!ÉÖå¶ryÑÓF…Ç<Ï(dÑEM»S¾ÔÓSjf¯vÈGNZ-©8£¹ä©£Jn8­¯+ÆŠŠn‘g½Iœv©{Ž……mÓ‘®— I§Éˆ²ŸŠÜFØIÄß:¾Û•}hŠ–[‡¤éóu,]@z~ÐÄøÁ [r4‰Ì.^tÝ2Ü‘w:÷©Q0é"¢¢Øs§!X:2p“pjFôW‰¸Ü(£·Ê.Gð‘&äRQQBÆ *³At²êÈ3/7gu\v·\Œ$¹¸fò,†÷ eåËŠ¨:Šdá))).ô¯j»¯Žê›µóƒ§ñ¯Káß¿ÔöîÂgÆp™¨ÊPIÛ®½5ŠéhyŽv'p¸ßŸo ííwÕ’ø³|6åãwwQ— j÷ƒLƒ"Ãh“"e¹»sÓÜYù2ß`]FÑC…œ.Îüi»vKuV”kæ«ujŽZRÇ«f‘aÉ~vyp™-Ï—”ÓUTÁ÷Â"b¶o>oKìK´ˆñ·ÆaØêÛŽ¾ÿ3N‰­›:om "¨hƒ¸ˆ6‘-B”«.³K-ü®ö5h„kc£l½0xÕM ]9ùAÅDت&ˆ¢¤˜ó.w§úF~átº\nbÇ-Û¤&!KGš[Ûd€…TºÊKÄÎIIUxÓéSR£Ò‚ͤu„=ÑâÓl¹ø|Þ¾YÌ3þMÆñkÓŸK?Š­UûžWôŠ¼Æ¹üá#ï½4z§Ý„=÷ùãÃöýzHùŸO}ÓßÌ5I«·¤™ô÷Ý=üÃTšƒÎßœ[y{aŸ%tZ|j÷JNǨ/ö.o¾\í|ûyº9fÏ&ÜíݵS8Êã>¬­hË‘"\§¥Ë}Ù2q×]5#pÉr¤J½ÕUW*«X©\âA¼ÍâîÍ™ë+7YÍÛ$8ô0hÆ›|Ä»U|£ÝSý)ùªcYë[Ƨf$IÑÛa°Ë,[£I|£l@uÃÁlòå1žê½Ô•k4 œkXêæ™†ËZ¦ø AÇHpu>A8Ów—D=±ÙU=KZ×A¹AHåÎdD|¤#Ë7GIIIͪª›•LÕKÖªKùÖ£)A½e¼]ì’Š]–ë:Û Á['bH6MAUETUD\~¤¬°5þßt‘u|¹Ä¸IÝÏ)‰f;¸‹q¢ä²Hй^ꙨÊPIØõþÅÍàw˯Ÿo7G,ÙäÛ»¶ªg\gÕ•¬£ªu0År ê+¸Çr(Ã6’k› 8¡ ²©œ+h†HƒêMËÛºÔ=($þ_ü À|rçá ugÓü­ÿåçoÊóz½}ýtP_ßðî{åÍß Ç‡ï–kÒcn8²¿£¸ù)ù’£)A½z¼]ïr†]êë:å eÈ7U•WU\~µ­RJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRƒ°~”¦“ý³?³~½*ôWš¿Ÿå)¤ÿlÏì߯J£ýlëUégñUª¿sÊþ‘W˜×?œ$}â×§>–Z«÷<¯éysùÂGÞ-zhô;Oº?{ïóLJíúô‘ó>žû§¿˜j“WoI3éïº{ù†©5¿8¶òöÃ>Jè´øÕî’”¥s‰”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥¦4öš¼ßâÜäÚc5 -qJdÄY-ƒ"Фh$HF‰ŽûQq”O¥3V­‡§áj™:<ù¶ä‹‚#o6KÌÛ¤.ï4N2F‘µ¦„.QQ6Fêí5yÒw’³ß£5x™²[yA ºnã"AUNø\.TÌ=uèž‘ôü]Kp¸Œ›ãîÍ…£¹¼Ã-ŽÏ ‚¼"p*«¸Ü(Ѝ†‘šk]Zm\jUÎ_5çã±jˆP° ˆŽ5(xýxp$XÈ¡n4 攫íŸXA·ú5“§’}Üe>г3k½U0nަ>W$r"‹B@ “™¬´”¯F®i—îO%¹Œ' -±,²#ê‘¡BQp7ƒæ×IL”ù—Ò»>¡Õð¡éÆfïbã{ƒpK¹Ûj \®,6œqx¡ÝÐW*-çz F¢×vk–§Ó÷0—9RÞoÒž¶¸ìŒŽÁBz{†HŠ‹·k+JD`»×(¾•fô‘¨Ôš¯&ç!¡`Zœó†]²¸q×H¿ÉWO¾âE$¬ÐNXôò÷kzålb3ì2øÇQY¬ƒÄé 6 ¡™!mRQ$ª*T^4ª²é­91$F“.îWHsb 5†ÙXüˆŽ ¼øâQÞӀР¸M Ë'nÖú~£ù:a¦¤¦”Ào†á·5L[}Ð`Ð:ˆ@ˆ¤ÓÊ Ðmç2cÈŠâ7%‡Y5qÀQU!,/ТH¨¿J*/ÓXªñ­µ—‹++q¾êYrňͥ½÷wCiÖšÊFõ2WÐL”xÁPžUÞ[W~ö±Ö¶ëÖ¡±Óá•Â<˜,¸l6ÛdþÕqÀ}ãA5˨¸EU!NÁÎi]±ßIZIÙ±®qÔ#5¨³¢<âÚú„’̆PmÔ“9Õ6Û4W8ÉTW䊪’×­úÒÊÕêË|‘w¾;tfÖqîrÝ·ò<ûêé’<Û{°"ÆDU €CšR¦5Ô/zžuÑ·§<š(œÇ‰×UQ*DD‰Û°©šˆàTÏ–ViÚSÂÓŸ$ÂŒ6ôa·Ô†|rq>%VÔÓ´†SnQ\e*³V÷ C¾­V†^œ·8·RžŒÆÇÀ¸Ž)*§¯pLïTÿNH Þ`Úm— šTxÁÑ%DÜCæDUQ\Šö,.0¸Â¢®*ꃭì«ôv£wSjùÒì{Z—!ø Ii}ÕMýQ/™TUq³+•ù –ã®´wŠØ.ŒÞã.Þn îM‚ ‘#‘[m^C jàÈ%"!«š£€Y2MJéwMui“«ÜºG•sˆOZÒ Ý£D!ÔyåNIN„ œ;•áTlÔQ6Š k=­àJ)ñdH¾1­…rŒôS}ÙQ˜&ÒÉa—d)r¨®¦Qß]£È­Ë&î6¨.Â1AR_Rª!Šª}“ó¥b®©¨="@ºø´¿êøv‡®“gµ>Öú¶¥l#Šâs(´"Bâ!":‹ÌD ‹‘.W@­ë¦uöóÑlm§&Ê>6ÇÁ¤3_Pî5AÊú‘3ÝU2ª‰Z5'¥.¾ªm7Σæ³/‡~ÎN3CÛ» ŒãÂâƒ-ÏMÝ-Öf®òz„ì§"6lÜyIÖþZ ©a;.ìmÁ‚ç*°õÒô–°Óšp£[âܯ‡oi‹’¸ùZÙR}Él4Ê4är}@Úd\ó¡* ¨&7V[F¼±D»Zîò޻Ϻ­ ¡Ü.°Hûr9ùEàq¹ ë«Æ½>UÆ—Ö*­ÐrúWPµkëD-gy¼õ—v¢M8$1áÃÅãiÆŒŸp]}ÅæpIÍî.\]¤à•«T3mЖۅÇTN¸wé¯$%(îȸF>á’*úºÃ/o)Ž×”·‘H8í+£XõÅš/£W4Ó±fGK!’ŒãÌJpÔÔ4IM¶Žà"eÒ $UÀˆëKÖÆš`"Úu.¡µ˜ÅŠÊZ`z q’iUàs•·ÚæR·‘&W*JyqäD”ôIl;C&MºÓ ¢m˜®Iº**aQkt-Y­à^uLE6Eòû²è“ÊÕs1é"´§½È­äœÞ%å Û[D©5ýš~¡³\[)ÒV Jlå;Ä”ÂºÞÆÝzcäjÉ*º¹´ETTSR@ç·[Lë[p\˜ÛB⌸ÄÛàâJD9È*íT $Q\(ª*%I齩5±ë– Rã³)˜Žâ["`ëÆ-´ŠHH„Dˆ…¾¾þUÄæ¯ÖQ_‘f™g–íÎlKq–õâÅQÜÈqäwa¢®/"¡¦åQRR%2¦ŸÖŒÅ°]…ˤë]Úãq†ñ²ÓYUM±G—p ‡BB]à ý¦uŠó*Ñsm¦æÅ>7Á·ÁÔOXîQÊz•3ÙQQp¨©Z5ÖYô…b‡yÕW7BÀ]î/Me¸ñÊ#éÉæÂ¾Ì±Â¡òâ8Þç0W£ÝDÍ»Fо»g[uº|QŠÓ‘ש–ãoqJåGÁäW›im¢Tð® ) &¥)AØ??ÊSIþٟٿ^•Gú+Í_ÀÏò”Ò¶göo×¥Qþжuª‚ô³øªÕ_¹åH«ÌkŸÎ>ñkÓŸK?Š­UûžWôŠ¼Æ¹üá#ï½4z§Ý„=÷ùãÃöýzHùŸO}ÓßÌ5I«·¤™ô÷Ý=üÃTšƒÎßœ[y{aŸ%tZ|j÷IJR¹ÄJR‚ûFé™wm+o‰©®äºŒÅ7lÍîÇE$I+•åi2‰þ’ÎUSjï/£›$ãrõMÌI¹ DÍœmæ¡°2y€–@ä]dÛ0TL*—¯n b_#W èÚGOGvÂhP¶ÅDAqÇDKt…Ê#®)þ|¢&väWÚþèoi··[ø;4¦CAIMTÛ!HHÄ–›ù@®h6mz:Á*õ¥ìïêœywæ" XŽºêBH¯¢¨’eÄ,"¨D‚»üºÚgNékÕÆåá=Î;QØ7âæÐ$ô†ÚŽëï) ?´6ñmDÞJJcêMØÖ¶k 0õ‹:¡ËM²d˜Ü=ó#1¸P¨ ÁlE7‘e%¹{Ö+>¦jÕ¨dÝâéËFÇâ»!ÉV[VœQ^nL¨§s\o\c€°éeÔ,ß%3¨®k Ó7ÉQìÜ­ôŠšK4'„€PX5Q–Twa9íZ´nµ{L[݆͆ÑpGe“9‹!T”[6Ðt›Ú먢H¨Há!nL"Uh&$émMÌ—©:vîͱ@IŽBpXPŽÕÒgJK_—gRÃv÷IÆ7¦Cx äw'tÏ­=T£µuèè4µò_LùG‚Þéñ:8ÜÙ`|¤™LŠ÷LÔ(&#imM*Ì·¨Úvîõ±ÜYÂp˜@ î-è›p›W+žØ_ÍI:[SE³%êN»³lPcœKz¦Ü.äÂç¾SóÔ=(''é-GjéÝ¿YnvH>,õw/4È*çÖ»WЏUÂ.kZùa»YxJác7tòZp^ŽþÜnãxAͪ¨‹´— Ùp½«F#çS2[ˆÚ11GZU)¸II?:**/©R²Ý.Ÿt&t·qÈü—ILj™"UUÂ"'ìD Õ¥)@¥)@¥)@©Í`øEtv,”âa^⇩”þGc,îBMÛ•7& »íÂÁÔžŸ¼9gzOøHÓcKc‚TY;Ñ·›Þ."* ¦ °,‰"äQ(ªŠ[>ëõMêÍÕ\ßðÈC/e¾ÙÔË=ÆÈñƒµÐæøÁÜ» ³L®3Xô¶‰¨"Ê—ã9cŒ£a§BÞ†œ¨æ"²¨YÞœŸå½ëÙæŒ]P¯Üä˹X­&ž[σ¢  ´ m¸.¢ hƒ…5Ý„SÜH„›0µÝÝ™NN• ˆ¹t)ð18“QSx²UUmµÚb`›ˆ¤ŠÚ:@h¥ÕW6îíBx ¡»ز]¦­¢¼ò˜‹(®&ÄùeÙUEC}V¬0õdÈÖfàŒ'!ˆ¯B<ÑÎxñÞäähQ T.g»'"à“¶½@¥)@¥)AjÒšN=æ-¼¤Ü݉"ïq+e°ŠŽÈk<Ħ*Ûy}¯0£‹òü½‘ «V9«&Y"°ËP`Ê8r–m¹çÑÍð¤*+¡°ÄI~)¥Ãˆcñiåî[«Ô R” R” R” µOÒqãY¤º7\¹Â·F¹ËޱPXHò8vlwz‘8K9lQ<ø%Ú›ªµa™«&I³9 Á Åf™àŽsÈŽÏDŠjÚ ð³Ü@IxÓ$¹-Á^¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ??ÊSIþٟٿ^•Gú+Í_ÀÏò”Ò¶göo×¥Qþжuª‚ô³øªÕ_¹åH«ÌkŸÎ>ñkÓŸK?Š­UûžWôŠ¼Æ¹üá#ï½4z§Ý„=÷ùãÃöýzHùŸO}ÓßÌ5I«·¤™ô÷Ý=üÃTšƒÎßœ[y{aŸ%tZ|j÷IJR¹ÄJRJR‚ÃèÖ,þtýºç ©°¦ÜYŠû.Š8h ¹EMÙNþ´Lå2‹—M3n‹¥®º‚mª5ÕØÓbCn<§Q GñF¹8ÍŒep©e¼]ì’Š]–ë:Û Á['bH6MAUETUD\~¤­¦uN¦fòõéEwnç ·¦×÷6ùHÑw*yG²¯úSóP_ÒZ~ߪXÓîÛúö®Z¾^ŸIqŠÃGÆøÈE]^rUS€ùQ7!rºÞ·^.öزâ[®³¡ÇšrÚbA¶†6š"¢`‰0¹õ¯ç­ 1éè èçoLÜ|RJlCfˆŒ$% “Èxup«Ç‘o‹s‡•|‹«>ÇlÓðëÞWÅ£àfbpŠç.ö"ŸJâïØV éA9"ÇljtXá¬lo´þþI-³1µ2›Ð˜B]Þ¤Ø%ßׄïO¶x§GðÆÇÁÁËÖpÌáÝ»xàß»ó·n?ÕžÕJ Èö;c³¥G=cca¦6qÉq™ŠÜɕ؂’mõ.ñþ¬§z@±Û$õÚÆÇ ‰òh9Ù˜¼Â˜Ãƒ±‚À¯Ð…´»w¨:PLF´[ݳ,ç5M¢<„$€ãR•õQÎ"Ê·’ÂcÏŽé•NøI´[Ú³$æõM¢D…%€ÛR‘ôRÆG$Ê7‘ÊçÏŽË…^Ù‡¥äû²7Oìlsy_™‰Â+œ¸[ØŠ}(;‹¿aZÖ¾[a[¸zMAl¼rnÝÑ· x±Œnæi¿^WÏ©sŽÙŒ¥”¥”¥”¥¬:2,¾\çBjzZ­Ã)¸Ï‹N™Iaœ áâ$ÚCæÎS(µêÚµÜ'Ú§7>×:LmgøÎ“nQQpB¨©”UOØ«A%®`D·_ѨMp°ü(s¤%!i_ŒÓĪª»DœTÊ«„Lª®U`ë,¹%Êz\·Ý‘!ã'uÓR7 —*D«ÝUUrªµŠJRJRJR‚û ¬Öiqtë7 cSPßœ´¸óޏ'±H¨Ž3°„y?Ä’übùËò·P«zÝx»ÛbˉnºÎ‡hqËi‰Ø>TÚhŠˆI‚$ÂçÖ¿ž´h¥(¥(¥(¥(¥(¥(¥(1¢mqïzÎÇe–n„y÷ñ]&•ÐpETUQS8^ÙE¨zPYµ+6éZZÕ¨!Z£Z“6\7#ÅqÒeQŽbf[—œ‘|ØÀŽ*µšÞ½^.÷¹C.õur‹²äÆ€Šª‚ŠJ«Œª®?ZÖ”¥”¥”¥”¥”¥”¥`ü ÿ)M'ûffýzUè¯5?ÊSIþٟٿ^•Gú*ÙÖª ÒÏâ«U~ç•ý"¯1®8HûůN},þ*µWîy_Ò*óçóŒ¼_ü× æC´û£ð‡¾ÿžû§¿˜j“PyÛó‹o/l3䮋O^é)JW8)JP)JP[ý “ÌzK±OjãÜÔ9¬¿%÷ç·E„pQÔÜd;²H ™RE^Ê™©Gœ¶'/Võ¹5ç¾:‰[uh[\SÙ9TšVòMîe2D[)ÄH´{-¦uâQG‚ÛJ  ãŽ<ø2ÓA”MÆãŠ •E%L‘ 'uD]èúNøôéPÕˆÌ]œ®ÊšË yÓs{^pѲÞ(¤IwŠ)Eh:œ»À ¢;l¼Û"ÚTJ“}m—3-„LqŠF2By­£' '"ŠŽ2i»Ôä %¨&õÕ¿Ö(Ê̇›açne¶Ü!']LŠ+`„H¤)Œæ‚ÌrôÒèçc@‹Ò]üœç1µOŠ(nF\L Y,Õkršs’êÏ•¤O§è,wÆ6¾$ÿ=á§wµßpŽ#Ò^Ø%܉õV¢!æÞq–p#‚8ñ*£`¤#¸•=I¸…2¿I"}5Š‚rD­"S¢œ{ñ¸ƒ¿©iËÃFã™O&ÃHÈ…î¹ÊvM¾ºuZGÅ9<ùáüx8¸­‡*’£JhkÚ;ꉷ*¦p´šTæ£ÒwÍ>Ëo\˜Ä{<ñ¦³$G7·¹Z2Ù¼2C»‘G(‹ˆ:[ýËn%êiu},“„¡ÂpA{“•µTjQù#–Ä<‘"¡öÓ»ˆ©P­ë-¦uâQG‚ÛJ  ãŽ<ø2ÓA”MÆãŠ •E%L‘ 'uDPév{Â1®õ«mÅ¥vUº jÛ!â ×[‚­<â8ʃ†ŠœÊá8 µWn]u®EÔ½c {}ÙÓœ0úÈËǵéL:ªsÚ4GW¦CLºŠ¹xqÏaé;䫤Ûh1§à°’$‰¬²È´¤.#¦h$®7´• Tr‹š@ÒwÉýGHÄgx_(㉬§Rèã-ÇÉÿˆ.ãåkz®ðúã·Ú¤ÚÚôb Û®òm¹ OŠ»ðÓ]Dîñ²äM¨ü&øAKˆ9H±”ww4©8+ŒÛ\‹£C¸ŒnErD¶˜äQ"ÑÂt‘r „©¸{y‡1” R” R”/ÑŸ¶»@·v#·ÔŒ¹4êíê1ö¶D‹(p2“ˆPÕ7møÄÝÍ*NÇb¸ÞyŠÆ™ÚŽ=*[Q™,íGHGrí%AÎU•¸ËM^dÙ–ìÔf–>ÃtAd¶/¸Øg{€Ê—!¶;O&"¢›*› JRJRJRJRJRJRJRS%ûl]gc“zŠØÕÆ9ÌGZäe=Ã…Ü›s”ÂçÕŠ‡¬±#È—)˜‘vD‡Œ[i¦HÜ2\ Š'uUUÂ"P[õü¹ÏX- _ï ]¯íÊ”nÈKˆO4Š@Â2 ð¦ÆB nÈîUÂoEZ]Iß,W7 MÆÓÛ‘·¢ËjK$£ÃÈÑîMª9Ê! ª`“1” R” R” R” R” R” R”ƒð3ü¥4Ÿí™ý›õéT¢¼Õü ÿ)M'ûffýzUè«gZ¨/K?Š­UûžWôŠ¼Æ¹üã#ïÿ5éÏ¥ŸÅVªýÏ+úE^c\þp‘÷‹^ƒš=ÓîÂûüñáû~½$|ϧ¾éïæ¤ÕÛÒGÌú{îžþaªMAçoÎ-¼½°Ï’º->5{¤¥)\â@¥)@¥)A9¢äX"]‘I;aV"µ %¿¸v“‚ ï]ªXRAÜ„;…dÛºØf÷i¸Ý¯D¸M=.e’ã­ƒÈHãJþ<Ë ×*¯‘;.åÛ£­ïš…‹t¹ŽÂŠ@ë²$¶Â<¬4Ûdá¹±Hw ˆ*ª"îÂ.Ô%À®ôM0Ü_sSK“ljÑ4-òB,`”òI>\Õpjp9’C^ûpŠŠª`®íoá~¸ÆDK'PÄŒÀÁ Þ6O€ÜRmXäA<ïUÚ›p\æºe™kjòÆ¢¹¹f¤¨RîCfÄx¬²"Bù)<‹ñˆã[[TCUSB!:±Ž2žŽºìt2FÖÐ ƒ=”…USÖˆ«Î¾º É:¦DÍ$švdV¸Y0v1Æ%Ž‚bˆq ø§WfäWyUTrâˆì\SõMÎoOÍÆ;âøpXá³’á cI¼{÷ȯҋXäémMÌ—©:vîͱ@IŽBpXP©¹±:TЋcWelä,pͱژM“J-þ½ˆ›—ºåiTÜáu1lgÔ>OŸ=ŽØ"ÆPw´»·` }”£µt™Ò GÒ×Ç¥ÃÙ԰ݽÒqéÞ(9ÉÝ3ëOU hí]pê: -|—Ó>Qßà·º|NŽ76X)&S"½Ó4ãj+„{2Ú[h(ê nZ"›ø,ç㉵s=× »)Û ˜JIÔW d´¹Ð1ÐÛ´Eð8ÇÇ hæ{&WvW¾Ur´¥µ4«2Þ£iÛ»ÖÄqf7 Âa3¸·¢mÂm\®{a5$émMÌ—©:vîͱ@IŽBpXPµØª¸DUÀ¢®p‹Z×Ë ÚËÂW›‘»§’Ó‚ôwöãwÀªmUD]¤¸^Ë…íA9èÚùg±ñn’$´ìèA…nÒÄöÓãÚtˆÛ|ÄWü¤DL/rÏeÎ+®¯u5 Ùö¨ÐM—Á†¿üB×J¯hÞá@Å„,)q6»AA2 5U¥÷EëE’Ír‘2#²oOÝâ\cQA¦&y<í8œÄ¢*˦ÄEBÀÄ7>Àƈ½Ùš—sr\‹¤y1¡¶m2/r/*¨#ä¸D$EL®ìf”Y©,ÌÄÓî\ä»pµÂµÈ)ÑAi¨Á7‡¸ŒãåUr`Ô†JP*áo0ì× e;”Z•Xå­ÊF—qGuQ·ÑvmÚkRCO0 WªsEØ>]‡Ë%8˜W¸¡Æêe?‚ØË;‡“våMÉ€.ûp¡a‰«m_ îÓÖDèLÌŠÄTsÂãN›l[EŠò£m¡M¸"µ¨Ø¡rég`¶s*@“†®ŽÏ‹ ¶B^æËfÖ÷I! 6‰Ô2Šán^íjÙô_ªoVnªæÿ†B{-öΦYî6Gˆ˜ä®‡7ÆåØMšeqšÇ¥´L}AT¸—Ëe :ô0Œˆ¨üåG1•BÎôäÿ-ï^Ï0dgUÙÝбlל¯Á„ôF˜ðÖ ”×]ºÅTy­„ò/¢‰qaUÂÅ­Q´t€ÑKª®mÝÚ„ðCv%±d0»M[Eyå1Q\M‰ò˲ªŠ"†ú­”¥”¥ãGê»< ,[ÌNvšôµgÃX‘Ö  Ð“<Î*NÌã•­Åñ™Â(V­Yk‹£‚ÜèIêÙ…&´‘.Uu]Ã-K™^ì6*°·œåhém"ë`•¨¦7w gÓßt÷ó Rj;~qmåí†|•Ñiñ«Ý%)Jç)J)J Í'©dé¾¹Øã,Ù,+,Î#x$BϬØ6ܤ½²«œ¢(ü’$);†º9ó¦?/LØÝ7…É1¥#o>Ò„‚${‘]Úá¡.ü’‘"—šª µEÖó#Ŷ²ÝžÐ‡âôõpZp:…y^eÆÄÑ®VÄ6Ž-Õ©nƒÒžy¸íF ˆYiIA´UÊîU,'©2ª¿V±RJ°Ë‹¯GÙà ¥Ÿ*ï-£”¦{Ѧ™Œ¢Úí˜Ux•WnîÉÝ(µê+,GA™L¼äv¤ƒf$Lº¤€â"åD¶ªÔ¸T_Ì©W‰Ök5»[kÔKcRaiãPá<ëœD‰9¨â&BHâ ƒª©ƒEÜ#•TÊ(P©] éj±Ù#]/idžû§¿˜j“PyÛó‹o/l3䮋O^é)JW8)JP)JP)]ФùPXÕÕÝØ õ¹ ÙÔ ÚyŽ:¸Ê½•Ú¸!ÊoLÌZ¯òäëK½š=ö0Úa»ƒ‚íÝ"¶WñÀ›U}uÒ: ˆê©<(*€m‡!¥wE¶ù:Û¨.W]_ÁrŸtièîÈÔ ÃßHÒÆL“{esb8$DFÕµáôÅvœVìDãE¹E,X âé*9芀9í]¨¸Ê"ÖLI‘¦JÌF´]Û¹ìY]6Ó—‰BÂ÷ÂrvÊw,wÉ>V‘>Ÿ ±ßÚø“ü÷†Þ×}Â8Œ;I{`—r'ÕZÛ]Â}ªssís¤Á–Öxߌé6àe*Š™ETýе¼ö©ÔÏ^Y½=¨®î\ã‚¶ÌÃšâ¾Ø.ï(š®äO1vEÿRþzÉ"V‘)ÑN=ŽøÜAßÔ´åá£q̧“a¤d@Â÷\‰e;&ß]:­#✞|ðþ ¼0×7.ï•ÉÓcnÞÛvg=÷}Ð5þßt‘u|¹Ä¸IÝÏ)‰f;¸‹q¢ä²Hй^ꙬVëÅÞÛ\KuÖt8óCŽ[LH6Áð¦ÓDTBL&>µüõ½V‘ÒŽEŽøäBÙÓ4Ýá q¼'Ÿy¬eCÊ÷Lá;.ï] JÒ!Ôuö;ãûŸ"c‚ðÓ[í´K1‹q'|’mEú©AVzçZËŠôIzÃPÈŽðn´íÉâ“ $ŠXTT\*-kF‘¦FÌ­I´]ܹì4I Ý×;W‰XRÂvÊrwÂ÷öI‘¦JÌF´]Û¹ìY]6Ó—‰BÂ÷ÂrvÊw,w WÍA¾ðøåòçtàÝÃÖK7¸÷cvÝʸÎ8õá+öí:÷s;ÅÆŽAƒmüSÈ 6Ø€"¢Š""'ª¤Ü•¦JT%²ÛçZä ¦È¤]'768‚/u6†0©".{Q6–k°“¦¤ÎÓvù1OóÂQlû64Fá76w<æå\¦Äò AÒ” R” R” R•oôW-¸—©¥Õô²N„S ÁîNVÕQ©GäŽ[òDŠ„ÛNî" T)]A‹Œ¥××9kóQЂ:KéolÛ¥HQck˜œâ(8¨ïù¤‹‡Ïã0­Éi;Å¡©WC‹u‚Ì¿IzJFèç T6+ÑU9±ÕͱPÅ9UUáÀqÚUâ×kއélFŠé|xnÞ#ƒ›[fPgÓßt÷ó Rjíé#æ}=÷O0Õ& ó·çÞ^ØgÉ]Ÿ½ÒR”®q R” R” Þ²Úg^%x-´ª®8ãσ-4DÜn8¢™QRTÉ¢wTEÞ¤ïN• XŒÁEÙÊ쩬°Ç77µç -âŠA´—x¢äQVš.E‚%ÑÙô“±¶b+PÂP‹û‡i8ј!ŠõÚ¥…$ÈC¸VM»­öov›ÚøôK„ØóÒæPAÉ.:Ø<„Ž4¯ã̲ wòªù²î]¡Ij ½@µoãu‡Ê2³!æØyÇÇ™m·I×S"ŠØ!) c$9ƒ®]Ú$ßÂýq:,ˆ:–N¡‰€‚A¼lŸ¸¤ Ú"°)È‚yÞ«µ6à¹ÍVcÈy·œe‡\àŽ<@ ¨Ø)î%ORn!L¯ÒHŸMb« S"f’M;2+\,˜;ãÇA1D ¸Ð|S«³r+Š<ªª9qDv.)ú¦ç7§æ‹cñ|8,pÙÉp…±¤Þ=ûdWéE ƒ¥NHÕ77çEšqlhì]übÝŽ6[“ ½±iÏÕ½j÷L->ÜüSĺ[?߇÷vìñqlÝŸõíÝŽÙÇj:Tä}Ssbt©¡Æ®ÊÙÈ.Xá›cµ0›&”[ý{7/uÊÒ©¹Âê8bØÏ¨|Ÿ>{7°EŒ ïivnÀ8ú( éSµÂ=™m-Ç´u7-MüsñÄÚ¹žë…Ý”í…L%$ê+„‹2Z\hèmÚ"ƒøcã…´s=“+»+ß*¹ZzTäýSs›ÓóE±‡Nø¾8lä‡8BØÒoýÀ²+ô¢Öµòù6óÃÕ³lk‡vÞŽÙ&sŒîáÝêLg8ïŒehrÅqÔ3œ…kÄûläËj8ñ‚n5Üé .$©œá}Hª˜¯¶™ÖK™Û®-´sâŸAÀ„ÁTIHU}u1èÖïk±jR¹]]’ÛC S-ðAjV\y“e76ሧ"’¢ånÕLªlêZ o7{QŸbèúIun–x¯)&ç6ˆê6<„xD%]»P”•3AcÒwËݭ땱ˆÏ°ËãEf²¤*@Ø4F†dH…´DUIDr¨©[Pt&§›§>F…­êáøâⶪJ)¡¯hïª&ܪ4j™ÂÔ¾‹Ö6‹%šå"dGdÞŸ»Ä¸Æ¢ƒL0LòyÛ 4q9‰DU—MˆŠ…ˆn}{³5.æä¹Hòb)BlÚd^ä^UP"GÉpˆHŠ™]ÙÕÔzNù§Ùm듸gž4Ödˆòö÷+F[7†Hwcr"¨åqWY©,ÌÄÓî\ä»pµÂµÈ)ÑAi¨Á7‡¸ŒãåUr`Ô†@­ë-¦uâQG‚ÛJ  ãŽ<ø2ÓA”MÆãŠ •E%L‘ 'uD]°è[Ì;5ÂYNå¥EV9B+r‘¥äÜQÝTmô]›všàTÓÌAŠ>“¾=:T5b3g+²¦²ÃtÜÞל4l·Š)Ò]âŠC‘EZ@ÒZ‚oP-[øÝaòŒ¬Èy¶qñÆæ[mÂuÔÈ¢¶DŠB˜Éfý§åêù7yG:*2^ðØó@¶2øŽ’6xCQg$ˆ$2V=wh*dÉ1§0«w~æÓ 8ÝI/9ÄÙ…’Ò+…¹ {´q´Î+—Ñm¢€Ü¡ˆf†ñt€ŒQ[ÎôE@,6®ÕLå+F­VÙºe¿G× D›…Ü.rå3-»sdÀ›! 9ô,:*–Ï.0^ºªÐ)JP)JPLYtÕæñ¤Ûã4`†­6ŽIm£â"*¶È™!<ç˜|¡œaÌ=]4^¦´ZâيⓆE†îwh€Ã àK2F>(ÉLU”Ìaó¢9þbù|¸*]”¥”¥”¥”¥”¥”¥”¥²Ä"\¦bDaÙ1m¦š#pÉp‚(ÕUW‰XªOJ]| TÚoœG‡Mf_ýœœf‡·vÆ3…Åùb¸Ù¸JhÆ6žÜ½[RY%nFˆ‡rnQÎQUS™Œ«£¸Z<Ýa²½:\x’¤Ë)2ãs#xl\q0(Â.íÝ÷ªa6ä«Ô R” R” R” R” R” R”ƒð3ü¥4Ÿí™ý›õéT¢¼Õü ÿ)M'ûffýzUè«gZ¨/K?Š­UûžWôŠ¼Æº|ã#ï½9ô³øªÕ_¹åH«ÌkŸÎ2>ñó^ƒš=ÓîÂûüñáû}#üϧ¾éïæJ¥U×ÒGÌú{îžþaªUAçoÎ-¼½°Ï’º->5{¤¥)\â@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ?$Uü%4š'uÌ¿ì߯KLd ?ûWš¿oå5¤?ùËþÑêûМ'[si’ŠeTS½g°»qó:pÁŽÒÓ€—ô´‹ÿ¥Z«÷<¯éysùÆGÞ-z®Ü6ýjõl¶fÎò.ÔÆQTSÿ ¿ó^\þp‘÷‹]þlØq7[JqÇû£ðˆ¼Úpí¢{¿m½i™‘tÔyÖæ§÷I’.a…Eî—|c°¯uLá2µ\ð;gŠt l|½g Î۱ǎ û±ß;vãýYíS^’>gÓßt÷ó RkœÎßœ[y{a·’º->5{¥9Çlvt¨ç¬ll4ÆÎ9.31[‘¹2»XRM¾¥Þ#ßÕ”ïH;dž£›XØáq>M;3˜Spv0Xú¶—nâ•Jç ˆÖ‹{¶eœæ©´G€djR¾ª9ÀäYVòXLyñÝ2©ß 6‹{VdœÞ©´H °jR>ŠXÈä™Fò9\ùñÙp«Û0ô U«TÚíc©-¶Æ ’:Ú :ô—•óuؼnÔp²Dj(€()åìÖªµ½x»N»¹Éî4áÆŠÔFÈ׉±A%Mꂈ›‹%„DÏd ºOÓ8Þž£é†^6Ðî¡§‚y8ZYJÚ°Dh$¤ƒÙHT“¿bUª…ŽÛ ãÍÕê eŸnÞ±¹Ëœço Nz°™Î=iŒ÷Æ(—{”[û7ææ:W6¥ Á’êò¼‡½ ·grîî¹Î~šÑ °Ùmp]« ÃjbÛ­ÂìI )ˆ)õ±šäTTPpð„‰ò»¢*vÙ·ˆø -'EœZ~X!ž¢L§ÈàQD^@]ªj¥Â~v¼¢å~ùpãÍÝNa’;Qw¶ŽˆÓ·°\¦¶=J©Z´“`DoAZn€Ö%ȺNa×7/™¶Úˆ@˜Î ëÑ2»»ú“ Ä–½9mmæ³wŸ™Ž*’æ[–¶-fîbG}×Úojy\p[\ã+”i¾Ê¸M½½k•Ò|»œç&Íw•÷0Ѝ("(ˆ‚""ˆˆ""ˆ""ˆ‚ˆˆˆˆˆ”šÞ¦6®ŽÛ¼2ß"¾K­ÆB{‡š#.¹±<—˜ÉQÿ2e=u^–1ÆSÃ×]ކHѺÚg²¢’ ªzÑqù××[×+õÚá} ãòøî ñq=±ÅÄ" ìÐP6ˆ &ÔLa+F\‰å=.[îÈñ“Žºé©†K•"U¹UZ ¢‹ ¶ùp¸èàÇ€1Q”•5þØy×ÐÕÀp•äl•y“L®áÞôk¤mÏj;(꿊 ·²µ ¾C¢ñ#Âé ´H‚‘M0{V¡nÔûln¾\áÄWÆB°Ä³mµtTT\ÚŠ‰¹P½h¢Ÿ™+f>±ÕѧJŸTß—3gRûwEÇö&y!d¶§dÏ©=TV«U–v‹»ËjlŸ·°ÜÇZrFú€c`:wÏ8îk9 "¢gvõÏDÇQK‰q"=¦Ýo¸4éÛÐЖŒªŠŽ4â#訉¿vÃwu,“´L‡ÑÆIw ¥ÂäFó’f º(¸¦â‡Ë]Â"K±2«’ݬƠ¿±`1|¹µv8°Ì[TW[AEÆÓTE!õ¢g4;N‚Óï^tÜgué±n—ðä?tÊ'eUóP_ï¼>9|¹Ý87põ’Íî=ØÝ·r®3„Î=xJÅz¼]ïr†]êë:å eÈ7U•WU\~µ ÞÑ6Hš†îõºUÓÃ6“%·Ê:ºÞYhTsjîÚæ5Î0+žÒzkKYï“®ÍöK¨¼bdÆa‡3ETEGd#ò·2™""ˆª!¨W¬·‹½’QK²Ýg[d+dìIɨ*¢¨ªŠ¢ã(‹Ô•µnÕ:šÛ*\»v¢»Ã‘4ù%ºÄ×7Ï*»QQIrD¹\ú×óÐYnº&Ñl³ y7»ÚÅçÚR,+x>ÃÒ#ñ €¸'‚WS͵UwcbmÉëCÐ컢›¿Ê¿Á‰"DW¥ÅŒëñÁ ¶ÍÀQ]Ï#Ü„M F*ª ¹2[ ÏTêcràáj+¹È¹ä³\U”*(.®|肪˜,ö\V(‚ÿoµÈµ@¾\â[äîçŠÄ³]Ü(%¸pYD\§tLPFTäû²7Oìlsy_™‰Â+œ¸[ØŠ}(;‹¿aZƒ¥ä‹±©Ñc†±±¾Óûù$¶ÌÄn>ÔÊoBa wz“`—^½<ÙâÃ/YÃ3‡vìqãƒ~ìwÎݸÿV{T(6®‘™‡9Èñî®-6ÉŒ. g”EìŽl㸧t\e0µ/¤bÁzÕª%̄Գ…hGc#†bºR£³Éä$ʈºJˆ¹ã(¾ª¯Võªí:ÖÜæá¸Ð„ø¥H¸À8†Ò–0h»UP“Š(¨©AfÓš6Û>,'.zØK·JºMAçÝ:>„‚»Ç㔣ž¶†Þü›° s‹=X®-Bi™®\gE}à3U|Æ HD¢ŠœÆžT¦3•î¹tfµ¼i†eÄŽ]e¶c2ýºL—Æ1ò†Ã5œ –Ï.W8ì©ÝRUÚt«4C®5ÑA7\`€Cwnò"B5]€™%\ ¢&1AŽx@Ÿ “%ýÌ ?ÏZØï}Â82Ü)Û»U~ªVöŽV¡a»“½× @ …66D„â7çáEW<ÈÚtJÑŸpŸpéúùÒeôÌ v9Ý#âhsµ±ÊùE2¸왥®á>Õ9¹ö¹Ò`ËkÚCêÛTrÞ7žrŠ"ižA­| ¿øïxåÏÅÿOêϨù;?ÌÎï“åõú»z©P_í÷IXËœK„Üò˜–`󻉷.K$ˆ«• ,«–™§#Ý­Òï‰p¹]&ă5¬M³PáV[DY c”x9qrª˜]›œ£Ô›‚þLjð_.mx¦|Cd³N¯;³Ë…øÌî,îÏÊ_εAt¾hvmZI»³·ø=zÅ,¡+ñÑI·ÐW•Ä• D5BTASÅ©´½–ËÛ7Æîo±p|¼ÚxÈc+-8FH®áEwÖ|‚æäRƒøAð/ñËŸ„~ÕŸOò·ÿ—¿+Íêõ÷õÖY:§SJq“¨®ïJ ˆNMp•$ ‹ÙUÿ1Q½hˆ‰žÔVt¶w]=h—pœÍ¸l)qaè°“Õ-Ã)Û7‰U2¤ˆjŠI´v!"…Bõé:Lénã‘ù.“Ž2Dª«„DO؉A³c¶Â¸óuz‚ÙgãÛ·¬nArç9ÛÂÓž¬&sZc=ñ³Çl“Ôsk.'É çfbó cÆ ¿BÒíÜR éA1Ñov̳œÕ6ˆò ’JWÕG8‹*ÞK >;¦U;á&ÑojÌ“›Õ6‰–mJGÑK“(ÞG+Ÿ>;.{f” ¾ÈÓ6kž¬diö¬@i‰ÝB4ãàëÉe¸näÔ&™lWE*µB«Uƒ]ÝíZ†-ô£A›6%»Ã£€l+m#|H[£“d®#j †J¤‰ŽùT í7§­-úCÒ]O²_Ÿg`Êl£¸MƒŒ{Å·a!¨í2ED_ZŠT`ê:ù2XÚÁÅÝîöÚ%“¢½òI¹Sê­oDÔרúL·e^1p%ÌA”{Äv‰/**Š"aW8Â*aQ+FÂ}¿¨è'I‰Ô°Qßàtƒ•¢ÆæË æÂdW²âƒVºš×§ßô\ÝÎám¶Gu¸°åÁf¸2‰Æ˜eÈÈÛ*îÓÜë¨Ù¨¶H ¨«±ruÏkyëÅÝë36W®³œ¶G5q˜g Õ†Íwy„v¢ù‹º'ú—óÐZ—BFoJ ñëÞÒa†%ÎŽ-²®5×&ÛW‘â$æm|í¶Ù&T\QP#Ç|ÐÁk‹x}Ë« ÍKàe!èD1Í0ê«Èb¤Y,“h$¨† ŠYÅCü1ÕÞá_ ožÁÓô¾ ïݼ{7cnÞÛqŒv¬mêLÛ`ÛzŠîØ0ØÍqA’ÜÈ¢g²6Jª)þ•²¿ "¹¨m–»må×Ù–vcÁa¾WjSˆª¢fÞÝÍ©(‰îJö´±Æ°]íæÃÌ#ÂãN²dߘ…AÎ,Š®ËÊB«…UÖŸ¨/÷ ¤{¬ûåÎ]Â6Þ OË3y­¤¤;MW#‚UTÂöUÍb½^.÷¹C.õur‹²äÆ€Šª‚ŠJ«Œª®?ZÐNiË.™•£.7«ÕÖïD[Œh¢1 6ølu·‰PUUe~”Û„ù[¼‹N˜¶•‚çs¿Ý§ZµÜZ…*†ò:ªà:¢€Šàüb&„'±QU K°v[ÅÞÉ(¥Ùn³­² ²v$ƒdÔQTUEQq”EÇêJËP_íö¹¨ËœK|ÜñX–`Ë»…·. "ˆ‹” B¥ÓI9w»LÍédLf#œ@†Ëdkçt]%ÃNa[iÀÊ")Š£œxº 5Ñ[WSê_½Ü&½¾HËÆÇ.Ç»©¶y_* mÞQ–½c«­P[kÕ7È1Ï®¶ØeUW$ˆ™UUýªµ­ð‚ÿà^ã—?ý«>Ÿåoÿ/;~W›Õëïë °ßmÚJÓnÎY÷@³$”,›Ms<Ƀâ ZÊ ‚|¥pQxÇËLDs[ž½RÖÕÀ¤tÊ'ÀS…Æ®ƨ!yw ©®@WËî©ÔÎÅ…ÍEw8ð ·!´S\PŒm¦›LàS²*cE.:§S\¥D—qÔWy’!$G_šã†Áåp*ª¨®DW)R~j Z‚5–3Ѽí&âìoudÂéœeÍä;PÍÊ"YBTÁ¢vTT¥ŽÛ ãÍÕê eŸnÞ±¹Ëœço Nz°™Î=iŒ÷Æ+Õâï{”2ïWY×)Ø».A¼hª¨(¤ª¸Êªãõ­hÐN@±Û$õÚÆÇ ‰òh9Ù˜¼Â˜Ãƒ±‚À¯Ð…´»w¬q­÷lË9ÍSh!É 8Ô¥}TsȲ­ä°˜óãºeS¾!éA1&ÑojÌ“›Õ6‰–mJGÑK“(ÞG+Ÿ>;.{ez´[àE¢j›EØÈÐU˜J®ååd±ÙsÝ;zñJÅøþSZCÿœ¿í¯½ZH°ãÙL*Š9Û²}FÏóý8¯‚¿ßÊkHó—ý£Õ÷k#ÑÙBFÓhå70ÙúÑ>¸®=_EHäøÆjòýµ¯:":õT½êõPP_ y6®{`Çë"/ü¢WÀW?œ$}â×ßzé´kÑÆ­ÆÒòöîb¾¡DDÿì•ð%Ïç x¿ù®û!t{Oº=¨‹OåÛõé#æ}=÷O0Õ&»Î‰Óv]Qy´Ûï°º¸ÍÚ¤¼!ÊaƒG™D\‚¢ú‰æ ?§4Ÿx»£êºŽoŽqÍÛx¶ü²\crú¿=yîwå› sžÖá1<9ÂqчøDöãÕØÒÉÙ~íEêÏ&M5q•p§#ƒÿ)íÇTv9-)JÒu¥)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JÁø8-~ºMÓÝ´Y.ÑR\$7ýH×ö%}Þ²"£kq¶ªŠadzÿzøKð-ü¦´‡ÿ9Ú=_yž¦"äíµ«©,öYž`$‰z—¿³ödsŒ¦wî3V3‡s^ñ†ŒUír¿èóV#b¿]aëbý‹jöQ÷W7—sržqU–i¼E1«ƒÁŸ§ƒ¯=NnÃ#Wg•hÊ3?ãŒpp׌UñïìQ©W…™þ köa÷Wó«‰ö5³Ù‡Ý[|Ï«m¥×r[?XR)W~®'ØÖÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâýlöa÷S«‹ö=³Ù‡ÝNgÕ¶Òr[?XR)W~®/ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‹ö=³Ù‡ÝNgÕ·Ò¯(Õ³õ…"•wêâýlöa÷S«‹ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ*r[?XR)W~®/ØöÏfu:¸Ÿc[=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®/ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ*ò…[?XR)W~®/ØÖÏfu:¸Ÿc[=˜}Ôæ}[xÝ*r[?XR)W~®'ØÖÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö5³Ù‡ÝNgÕ·Òr[?XR)W~®/ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ*ò…[?XR)W~®/ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ*r[?XR)W~®'ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâýlöa÷S«‹ö=³Ù‡ÝNgÕ·Ò¯(U³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Ò§(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸¿cÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâýlöa÷S«‹ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…"•wêâ}löa÷S«‰ö=³Ù‡ÝNgÕ·Òr[?XR)W~®'ØöÏfu:¸ŸcÛ=˜}Ôæ}[xÝ'(Õ³õ…«ð/!Â_H™’‰KUU\"'Fõ}¥«ô"ê6㜈wsXN>¦;J†M/cmWQQROÕŸ¥Q~³_äÙnM\ìÌ1mœÎx¤Ä'[Ê(®Ó*eQp¾¥T©Sô‡ªÌ”Žï8•}j²œ_ÿzÚ»f´Øã´iÿã?öÅk|®¼?³ÖjêÛ:Ú}êXQíÎCŠÝÖÙáP—î«"UV¾¹üá#ïÿ5dOHš±}¼Ï¤6M< )ÄG»(’gº/æZª¼âºé8¾²\­OÜnÑÙUG …Œã«¬ØÕ]|)Œ4a¯·ÿÙsipp-3.6.1/docs/beep_1sec_50x160b.ilbc300000664000175000017500000000324413730472040016720 0ustar walterwalter[u•·Y>mÿïàþ8@ÍÚg ·EƒåU½fS¥ZÊ¥ÿäV9µ5?oj“VG5ÑäXUµ·[\g"àÿÀ/ð0A—š{£•^þYéÔ $•»qqŸ˜˜š›­ÔôXUµ·[\r¢þüÿE@Ê#ªþUIkWê™@ùðL¿.°*>ÞÛûø ÔúãLŸH_a$Ú_ú€?Ðç ã 4X3>îðD ë¿GºÞóBt³ÒbAZXƒ¢h¦1¬t†«œeÉÓ V ~ÓÙ¿l°Ê 'Yv34ËÂb@ª³¿:äþãx€JhÈ`Ú*ŠÕ¶Ûi·ß.&bÁ¸±QEeUUUU VEYÖ³Lþ{J†Ú¨¨(À6Œb!•·s}§~ÿ€cü¾ 1ú‡›U:fO¡EÕu[qÚ¦Rð"9MÝw;­®ÃXÄYýµ·[lr"àþàÄAÚ“¬]ë—¡Qî†T'Jè±w4ɦæ÷wÏÏ';Ì|Yýµ·[Lr"à?þàÄ@Ê'š“P`]ú— Qû†P'K¾Ý³Ridäÿ »¾»QåtYý¾œ«œ®*ú¾ò‚ùñ£š3ó3ÿÌ3þÿ4"5­è|!qÁÃSP^ÕËú å¸7¦2ÄN¤pŠ(ŽJ Áƒ7–Ar!Z¶W³oÐð΢¡„q’DâÂ)>øB€Jh¦p’6+ZóØï±šð¹ÁŃnb–ÅAMe°2²E†¥éæ Ñ,â;0ËÞpPƒzh¦`V$jùÈ}ÕëRÄœÜ$D¢`Xb1B€ I:æ+:è{×>8éFOzh7¦`ls›½ÇÕ¦ÏÞJb½E`Å–XRƒj€D)‘êCƒ<-ßÔ½Ù¦Ùù†à€h7¦p¸WñœÃÔ«ïXhiü‚@)(66AÊ–a™‹ ¥½.œâ…4æ§žÀ¹€€Jh:&`±oÉ8ômím‚o$¢rÅ ¨f”‚‘ÈšÓç‹é"QL*ç?Þ€Lˆ7¦`ƒlFϵ[êþ|Д|N#PÝjI”¥ ­“Å'ád6BÐ[šÝâÀ.Ozd÷¦p“|®ZìïÛ›rp4Ü· «g©‘¤ª‚2)Àa Šϧ×gÑdöÈЃxø¦`¦>KíÉÙë³_@;%L@Z¡B€G¶”UµU¥õÕ°Ï}‹ zd÷p›(›¡=fýöÝ* 8B±1É@$ª  BAÁrž;”k ¸å»#FØÚ@Òh&`„x]õå©1|•¸PÎ,A‚fb»A¶ÝÀ+E. ع²é¬~;ÎrX½øNOzh7¦€µ2¿¦ê»s]Ülq™Ñ@J©J‰h¦†€a ’àêD8·ê[ãôÞ€Lˆ7`±>¥äÙÎzõ¦)^¶‘gï1œgf+Mp–õ½\G%´dñÀ(ÿ@Òh7¦pKZ\÷[mí¸ÛøŒ‡![‘ôm ‚À„ pŽp¬!;¤6²Ô3gÛøP€Jh7¦`°Ck~\ÍF¾yŠ‹‰íE$He R­­‚PeZ«Z16àBûÈ¿{‚RØGâƒRh7¦p†uœ1;?¥Ýâ¸Ãòr(B¬XRj™ª&U…C·0[›åBr .ö1 ƒzh7¦`”2cNq¾×w]Ê·I¡… ,C)Á@(‘’ÊÔòÖl¦;³oæ;ý÷~Ozh7¦p@ ÖÖºÕý²©ÈaÙÿj$†h®D !¨ À®Ú>ÕfÛ1¢ð:Ù?àxŒƒzh7¦`ŠKwó*÷ðæmR( MˆY%¥…ù)i1h$¡S§â,n"W ñ¢ž€ h&pÁœmæ¼Çã{‹Í©Ì-½UQUm\YÄ\ @q=Õµe¦r% ¼Ä)Ü€Jh7¦p¢výëZý×]71eûy†URQj‚)T*Öw^¿¯B$Uº»ÀGÀÒh7¦p©B“[L«²­ßåû9‚²†Ä)A™_X™–¦ Ö6eæ°+J˜Ê±þ;¬€5ئ`Ë {)kú_Ýòr, ÒwUEµeU•AdPD‘ߥ'¹¡Äe¸³=?@Rƒ¦3zc0›lqîW·Üò®úÙ³e)F`')‘iÕÔãÝïìM†ÆÃcbaØvsipp-3.6.1/docs/error.rst0000664000175000017500000001057213730472040014674 0ustar walterwalterError handling ============== SIPp has advanced feature to handle errors and unexpected events. They are detailed in the following sections. Unexpected messages ``````````````````` + When a SIP message that can be correlated to an existing call (with the Call-ID: header) but is not expected in the scenario is received, SIPp will send a CANCEL message if no 200 OK message has been received or a BYE message if a 200 OK message has been received. The call will be marked as failed. If the unexpected message is a 4XX or 5XX, SIPp will send an ACK to this message, close the call and mark the call as failed. + When a SIP message that can't be correlated to an existing call (with the Call-ID: header) is received, SIPp will send a BYE message. The call will not be counted at all. + When a SIP "PING" message is received, SIPp will send an ACK message in response. This message is not counted as being an unexpected message. But it is counted in the "AutoAnswered" statistic counter. + An unexpected message that is not a SIP message will be simply dropped. Retransmissions (UDP only) `````````````````````````` A retransmission mechanism exists in UDP transport mode. To activate the retransmission mechanism, the "send" command must include the "retrans" attribute. When it is activated and a SIP message is sent and no ACK or response is received in answer to this message, the message is re-sent. .. note:: The retransmission mechanism follows :RFC:`3261`, section 17.1.1.2. Retransmissions are differentiated between INVITE and non-INVITE methods. : will initiate the T1 timer to 500 milliseconds. Even if retrans is specified in your scenarios, you can override this by using the -nr command line option to globally disable the retransmission mechanism. Log files ````````` There are several ways to trace what is going on during your SIPp runs. + You can log sent and received SIP messages in __messages.log by using the command line parameter -trace_msg. The messages are time-stamped so that you can track them back. + You also can trace it using the -trace_shortmsg parameter. This logs the most important values of a message as CSV into one line of the __shortmessages.log + You can trace all unexpected messages or events in __errors.log by using the command line parameter -trace_err. + You can trace the SIP response codes of unexpected messages in __error_codes.log by using the command line parameter -trace_error_codes. + You can trace the counts from the main scenario screen in __counts.csv by using the command line parameter -trace_counts. + You can trace the messages and state transitions of failed calls in __calldebug.log using the -trace_calldebug command line parameter. This is useful, because it has less overhead than -trace_msg yet allows you to debug call flows that were not completed successfully. + You can save in a file the statistics screens, as displayed in the interface. This is especially useful when running SIPp in background mode. This can be done in different ways: + When SIPp exits to get a final status report (-trace_screen option) + On demand by using USR2 signal (example: kill -SIGUSR2 738) + By pressing 's' key (if -trace_screen option is set) + If the -trace_logs option is set, you can use the action to print some scenario traces in the __logs.log file. See the Log action section SIPp can treat the messages, short messages, logs, and error logs as ring buffers. This allows you to limit the total amount of space used by these log files and keep only the most recent messages. To set the maximum file size use the -ringbuffer_size option. Once the file exceeds this size (the file size can be exceeded up to the size of a single log message), it is rotated. SIPp can keep several of the most recent files, to specify the number of files to keep use the -ringbuffer_files option. The rotated files have a name of the form ___.log, where is the number of seconds since the epoch. If more than one log file is rotated during a one second period, then the date is expressed as , where serial is an increasing integer identifier. sipp-3.6.1/docs/branchs.xml0000664000175000017500000001055113730472040015150 0ustar walterwalter Content-Length: 0 Expires: 300 ]]> Content-Length: 0 ]]> Content-Length: 0 ]]> sipp-3.6.1/docs/perftest.rst0000664000175000017500000001054113730472040015373 0ustar walterwalterPerformance testing with SIPp ============================= Advice to run performance tests with SIPp ````````````````````````````````````````` SIPp has been originally designed for SIP performance testing. Reaching high call rates and/or high number of simultaneous SIP calls is possible with SIPp, provided that you follow some guidelines: + Use a Linux system to reach high performances. The Windows port of SIPp (through CYGWIN) cannot handle high performances. + Limit the traces to a minimum (usage of ``-trace_msg``, ``-trace_logs`` should be limited to scenario debugging only) + Understand internal SIPp's scheduling mechanism and use the ``-timer_resol``, ``-max_recv_loops`` and ``-max_sched_loops`` command line parameters to tune SIPp given the system it is running on. Generally, running performance tests also implies measuring response times. You can use SIPp's timers (start_rtd, rtd in scenarios and -trace_rtt command line option) to measure those response times. The precision of those measures are entirely dependent on the timer_resol parameter (as described in `SIPp's internal scheduling`_ section). You might want to use another "objective" method if you want to measure those response times with a high precision (a tool like Wireshark will allow you to do so). SIPp's internal scheduling `````````````````````````` SIPp has a single-threaded event-loop architecture, which allows it to handle high SIP traffic loads. SIPp's event loop tracks various tasks, most of which are the calls that are defined in your scenario. In addition to tasks that represent calls there are several special tasks: a screen update task, a statistics update task, a call opening task, and a watchdog task. SIPp's main execution loop consists of: #. Waking up tasks that have expired timers. #. Running up to max_sched_loop tasks that are in a running state (each call is executed until it is no longer runnable). #. Handling each of the sockets in turn, reading max_recv_loops messages from the various sockets. SIPp executes this loop continuously, until some condition tells it to stop (e.g., the user pressing the 'q' key or the global call limit or timeout being reached). Several parameters can be specified on the command line to fine tune this scheduling. + timer_resol: during the main loop, the management of calls (management of wait, retransmission ...) is done for all calls, every "timer_resol" ms at best. The delay of retransmission must be higher than "timer_resol". The default timer resolution is 1 millisecond, and that is the most precise resolution that SIPp currently supports. If you increase this parameter, SIPp's traffic will be burstier and you are likely to encounter retransmissions at high load. If you have too many calls, or each call takes too long, the timer resolution will not be respected. + max_recv_loops and max_sched_loops: received messages are read and treated in batch. "max_recv_loops" is the maximum number of messages that can be read at one time. "max sched loops" is the maximum number of processing calls loops. These limits prevent SIPp from reading and processing new messages from sockets to the exclusion of processing existing calls, and vice versa. For heavy call rate, increase both values. Be careful, those two parameters have a large influence on the CPU occupation of SIPp. + watchdog_interval, watchdog_minor_threshold, watchdog_major_threshold, watchdog_minor_maxtriggers, and watchdog_major_maxtriggers: The watchdog timer is designed to provide feedback if your call load is causing SIPp's scheduler to be overwhelmed. The watchdog task sets a timer that should fire every watchdog_interval milliseconds (by defualt 400ms). If the timer is not serviced for more than watchdog_minor_threshold milliseconds (by default 500s), then a "minor" trigger is recorded. If the number of minor triggers is more than watchdog_minor_maxtriggers; the watchdog task terminates SIPp. Similarly, if the timer is not serviced for more than watchdog_major_threshold milliseconds (by default 3000ms), then a major trigger is recorded; and if more than watchdog_major_maxtriggers are recorded SIPp is terminated. If you only see occasional messages, your test is likely acceptable, but if these events are frequent you need to consider using a more powerful machine or set of machines to run your scenario.sipp-3.6.1/docs/3PCC_extended.rst0000664000175000017500000000364713730472040016120 0ustar walterwalter3PCC Extended ============= An extension of the 3pcc mode is implemented in SIPp. This feature allows any number of SIPp instances to communicate with each other, each one of them being connected to a remote host. The SIPp instance which initiates the call is launched in "master" mode. The others are launched in "slave" mode. Slave SIPp instances have names, given in the command line (for example, s1, s2...sN for the slaves and m for the master) Correspondances between instances names and their addresses must be stored in a file (provided by ``-slave_cfg`` command line argument), in the following format:: s1;127.0.0.1:8080 s2;127.0.0.1:7080 m;127.0.0.1:6080 Each SIPp instance must access a different copy of this file. sendCmd and recvCmd have additional attributes:: Will send a command to the "s1" peer instance, which can be either master or slave, depending on the command line argument, which must be consistent with the scenario: a slave instance cannot have a sendCmd action before having any recvCmd. Note that the message must contain a "From" field, filled with the name of the sender. :: Indicates that the twin command is expected to be received from the "m" peer instance. Note that the master must be the launched at last. There is no integrated scenarios for the 3pcc extended mode, but you can easily adapt those from 3pcc. **Example:** the following drawing illustrate the entire procedure. The arrows that are shown between SIPp master and slaves depict only the synchronization commands exchanged between the different SIPp instances. The SIP message exchange takes place as usual. .. image:: master_slave.png sipp-3.6.1/docs/installation.rst0000664000175000017500000000730313730472040016242 0ustar walterwalterInstallation ~~~~~~~~~~~~ Getting SIPp ```````````` SIPp is released under the `GNU GPL license`_. All the terms of the license apply. It was originally created and provided to the SIP community by `Hewlett-Packard`_ engineers in hope it can be useful, but HP does not provide any support nor warranty concerning SIPp. SIPp releases ````````````` Like many other "open source" projects, there are two versions of SIPp: a stable and unstable release. Stable release: before being labelled as "stable", a SIPp release is thoroughly tested. So you can be confident that all mentioned features will work :) .. note:: Use the stable release for your everyday use and if you are not blocked by a specific feature present in the "unstable release" (see below). `SIPp stable download page `_ Unstable release ```````````````` Unstable release: all new features and bug fixes are checked in `SIPp's master tree`_ repository as soon as they are available. .. note:: Use the unstable release if you absolutely need a bug fix or a feature that is not in the stable release. Available platforms ``````````````````` SIPp is available on Linux and Cygwin. Other Unix distributions are likely to work, but are not tested every release cycle. .. note:: SIPp on Cygwin works only on Windows XP and later versions and will not work on Win2000. This is because of IPv6 support. Installing SIPp ``````````````` + On Linux, SIPp is provided in the form of source code. You will need to compile SIPp to actually use it. + Pre-requisites to compile SIPp are: + C++ Compiler + curses or ncurses library + For TLS support: OpenSSL >= 0.9.8 + For pcap play support: libpcap and libnet + For SCTP support: lksctp-tools + For distributed pauses: `Gnu Scientific Libraries`_ + You have four options to compile SIPp: + Without TLS (Transport Layer Security), SCTP or PCAP support -- this is the recommended setup if you don't need to handle SCTP, TLS or PCAP:: tar -xvzf sipp-xxx.tar cd sipp ./configure make + With TLS support, you must have installed `OpenSSL library`_ (>=0.9.8) (which may come with your system). Building SIPp consists only of adding the ``--with-openssl`` option to the configure command:: tar -xvzf sipp-xxx.tar.gz cd sipp ./configure --with-openssl make + With PCAP play support:: tar -xvzf sipp-xxx.tar.gz cd sipp ./configure --with-pcap make + With SCTP support:: tar -xvzf sipp-xxx.tar.gz cd sipp ./configure --with-sctp make + You can also combine these various options, e.g.:: tar -xvzf sipp-xxx.tar.gz cd sipp ./configure --with-sctp --with-pcap --with-openssl make .. warning:: SIPp compiles under CYGWIN on Windows, provided that you installed IPv6 extension for `CYGWIN `_, as well as libncurses and (optionally OpenSSL and WinPcap). SCTP is not currently supported. + To compile SIPp on Windows with pcap (media support), you must: + Copy the `WinPcap developer package`_ to "C:\cygwin\lib\WpdPack" + Remove or rename "pthread.h" in "C:\cygwin\lib\WpdPack\Include", as it interfers with pthread.h from cygwin + Compile according to the instructions above. .. _GNU GPL license: https://www.gnu.org/copyleft/gpl.html .. _Gnu Scientific Libraries: https://www.gnu.org/software/gsl/ .. _WinPcap developer package: https://www.winpcap.org/devel.htm .. _hewlett-packard: https://www.hp.com/ .. _SIPp's master tree: https://github.com/SIPp/sipp/tree/master .. _OpenSSL library: https://www.openssl.org/ sipp-3.6.1/docs/sipp-01.jpg0000664000175000017500000012176513730472040014713 0ustar walterwalterÿØÿàJFIFHHÿþCreated with The GIMPÿÛC  !"$"$ÿÛCÿÀjH"ÿÄ ÿÄP!"1RÒ#2AQ‘’678BSTstu¡²³´$3aq¢4U£%±ábrCDc‚ƒ”ÁÿÄÿÄ@á!14AQRacdq‘¡¢±²â35r‚ÑÒbÁ"B#2ñÿÚ ?Ín:®ç©/Ò/n(#ü´¯j‹ d”6:c? êzžµ3"â¨V¤Î˜ø I’¥-iã ÂR 'z’1Ó¡'=0i–÷•Àäô0Û©ÈZSåF3qœ`dõÍ^l·«;w Ú[çp‚Žp𥵇ЎpZz(£ãå@cfÒTºì¹EŒm1>4àöUWi­u4Ý|_13|DlëÝ7]3uÎ{ƒbï~ÂõuÝS3ª›æþmí@¦^‚#­R”‡Õ»{–ÔÒÐ…$¥I9_ð¯o’Ô¬ ©#…Åýˆ®‹w˜Ä6´C†äU¯k±’–òÚ“¡JP VH%#§JÓ;?Ðzf}‹K»>—âÕÙÍAy[ާÂx8ä¬4ÏC¿á’½Ý~LcÜÛ`˜óaôÕ~ªb/Šoºé›æë£rc~gbûß6¸ „a_ÇÕ­tNÍWlÝÇÄÊ̘Ž% K‹S»T’¤«*ÎQIIÚS9mìïIÛ{F†Åº$ø]ÛT¿iär}§eË9š2´¨-% VÄ”åyÉ:™×‚SMZªf&5ZÚÓÿÏoëoÝѮ،Ÿ¶™MZÓvþïó·ÎöÏ×_½Oퟮ¿z´åi=.î˜S²2Ãìöbƨ3ù¹˜$;ŠoŒìÁNÌùŽÀ¯+斌ΑԒ\j:-Y÷(Ë1ålÆnAe礄­èÛ…­IW{@KM| •·FP`µÄMÓ;ÛÅ×߯­²ÁV&µ¦n™íŸ ݆mãs½³õ×ïSÆç{gë¯Þ­öˤtœNÑ4³vë ÊÚÖ¢žüËd†.O4âmˆ´6¥¤­oBÌ—¾N¸J†vã<ÝŒÚ4Õ†Weú¶áB“yel=â©’Ü÷dª+‹.0K¡¥°w¡ñ«ã n+_—ZÓ)ì"š¦›:¦n¾#Z5ÿå­³7]©˜Ý×Öˆ–JqwÅõÆÏï~oÜÖ×½…øÜïlýuûÔñ¹ÞÙúë÷«HÒýŸB²«õú÷mKS‘k¹Ün+2»SñxˆˆòÜyA$îP ­µ8¤îW ¯jM3keVÄOq­(ãÖˆNÇNž'…¥å95ÒŒª:UÆ–Ò•Å#n)’°Ç-µsD_|MÓ­~æô_3½­u×ζ»RÓÚYÓLëL_óÿäÍucÆç{gë¯Þ§ÎöÏ×_½ZΊµX èû=ŽK—™ðun©w”‚à~ãj€•”¬¶”Ò;Ç”×J@ÍXtæƒÓ²'EÓ·ĹDF¹‘øöɳ„-;·O¯áBÐÄ©JJ·#8­ |¥Áì*®*¢f#bwâ6vn׉¾.ë˜×ˆÚ²Ä––‘MÕkÏN¶÷ø˜Ýê¿wñ¹ÞÙúë÷©ãs½³õ×ïV»gÑ6WíPäÍÑÉF§{G].®éЩhã’Ãí¦)á.-hÁ^°íæ¨ýªiÛM«T·ÛT›{ë±Â™q„Ë%º•okjÔ\o¢R½«R”9=qŠÜÀ±Ö †[ÿ1:û;×ïLïlìqµ°œYiƒY-s­þwnâãéV¼nw¶~ºýêxÜïlýuûÕçácþãÿ‡ÿšxXÿ¸ÿáÿ槆7ã·ð‹þK=ùízxÜïlýuûÔñ¹ÞÙúë÷«ÏÂÇýÇÿÿ4ð±ÿqÿÃÿÍ?†7ã·ð%žüö½í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^.Ïퟮ¿zž7;Û?]~õqøl¿ÒXþ>í<6_é,vžïÇ¿‚ú8^)w´ÙŒÃˆrCî%¶ÚÕ¹JQÀ¯©'v{Gß#63WÙã• ì&k›ÓrROû‚GÍU~Î Èk´-8âßiiMÖ1!9ÉøTÿ¥i)ŽåëBµy«NG,ÆZÄ'§Ê!†Þ<|emòŒõÈùê ÚZØZSMÝ×Í×ìßvÌNòC±°µŠ¦ªuWG…ãl¿2¨É¶]˜½¦D„DJ -ì¥õçcjK¨BV:`ààäMHÑ×øíÌÖˆåC%*3WƒónC IÿpH¯~ËnŠz”êûãÿû*ílÖú¬h«"tÆ¢³ÄJa¨<Ûóa6´»Îïª^PPòqŸLu¨¼+ Ã)´›:*øÕ|Ý4߯7U›‘ ë ª˜®ªo¾/»^wnÖ×Axµjë‹á—voi“)¨ {)}yØÚ’â UƒŽ˜889v{ª™i —¬lÌ)IÉBœ˜½¿8܆“þà‘V£Oí&ç&#í<ÂûG´­·[XRIvq$t Ž ZÓ;?Õ–¤ìÂÿv´™j¿ oÀPo I^©Vze\§Ê\c€Õgea\Q}ó|ÄLìQ­7ëÚv"78Ö¬›Éì ²µ¶´³šâ›¢è™ÝÕëÅÚÿõߨ¿fn‡æ qg½Ùá o^ Þ`©ÀËŽÅS¥-8A!+K­¡@•qƒ´àä*×Ú+ŽÍÓZ±ReG›$j(%é,(Þ_Ã+A’rFèiVœ‰Æ8^7ÅTá8DêªÕUݱ;Ñ­Õ¥•8»Åxʼ›©ˆ¦vfvb'fuÙ[ü]=l¶;9 8Ì–Sð§})ÀRV”õ#äÆFsòŒƒ=sí½WÖÓÏÂâSjOÙ}Ö÷v¬¥j9)8#¨ôùZÍûDûßÓ³9ü©uVÊìeü˜Îº-l©«S~«bèÊ¢6fY±N ÿ¢k¦©TÕ}×nLÆìNä5ø:ïMÃRSQÚKHÞ•„€|£¯Jþ¿®tœ½í°þß‹½’qóú¤Ö?JøŒ¸ÆDQQtqOìÛ8ú«ê¿§Caw\é^Kîµ½ÔcjÔÑ*NFΔûyÒ=ç¼ñ|?çxŽïLzìϧJÇ©LøÆ :§ö3cáUס®§Xè”…Äh (þ£9Áò|à}þηÑÄ’XI$¨’Y=J†~'Ê=~zÇé^g¾À£ªg¹µ‚pªëÐØ>ÝôvÅ£;ž…üP|qò|Õ#ríVÓq†ÄIry¦„¡n[#­ðAH©‚ð%)XÂR0+¥|W–xm¥Ú«:&î)ýŸTdæ EúšªëÐØU®tŠqÒÖ\q;QhåIù‰ÙÔtk\é]åi®76„nKD£ÙéÐ}R²gÆ0ÙÔQÕ?³ã60=U]zòõ¦Œ[Hiq¦ÛÎÄ–2Ÿ\ +ÙûJ¡Õº€´¸æ7¨6AV=2võ¬n”Œ¸Æ±ESû“ìÕW^†Æ½y¤ÜZ–´-HãR‹d’Ÿdù}?Ò»tçjVM9v‹v±>`O†_Dd¨³½%*) l§¨Qõ‡Ò¾m2×´¦i®Î‰‰Ö›âuûÏhɬ‰‰¦ª¢cCi±v‘b°Û®0lÒÞ„›•½véŽ6ÂJÞa`¤•6zœ|`8"¼cëÍ'ÐYA9)m²‘ŸŸ¢k¥yFZa´U5SgDLñNçÜú¯'0jãSUULtèlÿt=5ùǾª½Ú}Ðô×çúª÷k¥eϼeÁ£ªf,ÕÀwêëÐÙþèzkó}U{´û¡é¯Î=õUîÖ1JgÞ2àÑÕ?±š¸ý]z?ÝM~q絛vŸt=5ùǾª½ÚÆ)LûÆ\:§ö3Wß«¯Cgû¡é¯Î=õUîÓ¿8÷ÕW»XÅ)Ÿx˃GTþÆjà;õuèlÿt=5ùǾª½Ú}Ðô×çúª÷k¥3ïphêŸØÍ\~®½ Ÿî‡¦¿8÷ÕW»OºšüãßU^íc¦}ã. Sû«€ïÕס³ýÐô×çúª÷i÷CÓ_œ{ê«Ý¬b”ϼeÁ£ªc5púºô6ºšüãßU^í>èzkó}U{µŒR™÷Œ¸4uOìf®¿W^†Ï÷CÓ_œ{ê«Ý§ÝM~q絛v±ŠS>ñ—Ž©ýŒÕÀwêëÐÙþèzkó}U{´û¡é¯Î=õUîÖ1JgÞ2àÑÕ?±š¸ý]z?ÝM~q絛vŸt=5ùǾª½ÚÆ)LûÆ\:§ö3Wß«¯Cgû¡é¯Î=õUîÓ¿8÷ÕW»XÅ)Ÿx˃GTþÆjà;õuèlÿt=5ùǾª½Ú}Ðô×çúª÷k¥3ïphêŸØÍ\~®½ Ÿî‡¦¿8÷ÕW»OºšüãßU^íc¦}ã. Sû«€ïÕס³ýÐô×çúª÷kø{DÓ¹è§>…{•ŒÒ½Œ¼Æ1ÿZ:ªýŒÖÀwêëÐÙ~èšwÚsþ^å>èšwÚsþ^åcT¯s÷ð(ê«ö3[þÝz/ÝNûNËܧÝNûNËܬj”ÏÜcÀ£ª¯ØÍlûuèl¿tM;í9ÿ/rŸtM;í9ÿ/r±ªS?qŽª¿c5°íס²ýÑ4ï´çü½Ê}Ñ4ï´çü½ÊÆ©LýÆ< :ªýŒÖÀ·^†Ë÷DÓ¾ÓŸò÷)÷DÓ¾ÓŸò÷+¥3÷ð(ê«ö3[þÝz/ÝNûNËܧÝNûNËܬj”ÏÜcÀ£ª¯ØÍlûuèl¿tM;í9ÿ/rŸtM;í9ÿ/r±ªS?qŽª¿c5°íס²ýÑ4ï´çü½Ê}Ñ4ï´çü½ÊÆ©LýÆ< :ªýŒÖÀ·^†Ë÷DÓ¾ÓŸò÷)÷DÓ¾ÓŸò÷+¥3÷ð(ê«ö3[þÝzLnÒ¬±äµ",‡ÊÒãN¼¡I9y=r*Y]­èÇV§ÒVº³—•Ü WÊv¡ä¡9ù’ ° Vµ¾Xá–õEUÙÑ|nÝTO^©±c“ø5ŒLQUQ¸ov·fi 5gb‘”ID®8iw<€BTéZ‰NNì ÊÀêkÝÎÖôƒ„­í#§œqy.,*{{”}NÔ>”§'äH|€ À)X§*ð™»ÿ]ÆíÕDëñê¯Ü†HĶ13:ªµøãðý¥µsšÇQÛ´¶†¶Cƒ9é]î4HN8ŽWÚmJ)É QÊR €VU×VsHv„V¥Jì£J—TrµÉor¾SµR”çæH|€Vöþ3:Gÿº_öo×ìi®ƒ-›E†Þ»ÍÝ-ߎÙ8e”€¥è¥‰¹P89JU÷ÿœ´Ãâ)·±³˜¦û¯‰™ÿ”LN¾ªýxÖ÷Õ8¹ÌÕciU7ݱ;Ó|nnN¼oKÕš/´s§¦«í:Õh³ElÆàÝp„Ÿ„V÷ÜZŠRU€MÊÀêi[ âÿÿÙ6§ºÛämvg’RFÚ²¤¨|„gø‚24© (p¼ Î,0[;:hȦèñka–\ÚáÕUS»3|öÃð‡hŸ{úcög?˜U.®¢}ïéÙœþaTº†ÊÏ›Úý¾Xnb«5y¤¥)UÔ™JRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRJRƒ`û —³ì”ÒkÚU´Ë8§üÿ=~ïL°ò–„­NÔà·I%II;A!£ÐnV~Só×á°Çñ˜Ñÿ¬“ý«Õôâ³Y[×e~§uŽ»8¯eŽëKfÎÎ5`e¥4•Úd¯ÍÖPùŠQ+BA$¤úd’¬üæ•wí‹ðSª¿tÉþ™¥[q!µÇ”WküÚLÄ]©¾ýoª¸Ã £ªš5«øîÿO˜Ý¢}ïéÙœþaTº¿k ,÷iI-ñ®-"3Û£IS‰my u-­ éœôPêr2*»ã–ÏïŸiÖ>.çÍ3‡vìògŸ~ìtÆí¸üœõ¨ì¬ù½¯Ûå†Þ(Ú±ÓWšPt©È÷ËcS¥H^ޱ¾Óû8ã8ôÀÜ}£aKáGw©Þ¥uôÀéHËdnñÍ£¬sy_S¨çz`áIÆNÇÓ”«rºõQªêMJ˜w·µf0\ÒÖ‰ ‘=ÇeÁVp¬%àÞS‘&: ƒ×)7{{¶a½-h ! 3ÛvQ|”ã*ž-åX9òc©À0ô©É÷Ëdžïã¬p¸ŸK«àzaæHÎ[V÷Õ„Ÿ”§jºtP¤‹å±ÙÑd#GXØiü‘›zanFἩò¡·ÔlRzúät ƒ¥Nxå³Å;çÚuƒƒ‹¹óLáÝ»<™çß»1»n?'=iùljt© ÑÖ7Úgg˜´`ì)|(îõ;Ô®¾˜( éS/–ÈÝã›GXæò¾§QÎôÁ“Œ6§)!Våuê£^q®öö¬Æ šZÑ"ABÒ'¸ì ø*΄¼Êr1äÇAzä!éSnö÷l zZÑ@Bg¶ì¢ù)ÆU…<[ʰsäÇS€:cÒ}òÙ'»ðèë.'Òêø˜y’3–Õ½õa'å)Ú®( éS’/–ÇgEca¦7òFmé…¹†ò§Ê†ßQ±Iëë‘Òž9lñNùöcààâî|Ó8wnÏ&y÷îÇLnÛÉÏZ:Tä{屩Ҥ/GXßiýœqœz`n>у°¥ð£»ÔïRºú`t¤ å²7xæÑÖ9¼¯©Ôs½0p¤ã §céÊGÈU¹]z¨ÐAÒ¦#]ííYŒ4µ¢D‚…¤OqÙAðUœ+ x7”äcÉŽƒ õÊMÞÞí˜AoKZ#ÈBLöÝ”_%8ʰ§‹yV|˜êpL=*r}òÙ'»ðèë.'Òêø˜y’3–Õ½õa'å)Ú®)"ùlvtYÑÖ6c$fÞ˜[‘¸`o*|¨mõž¾¹( éSž9lñNùöcààâî|Ó8wnÏ&y÷îÇLnÛÉÏZG¾[*BôuöŸÙÇǦãí; _ ;½Nõ+¯¦J:Tä å²7xæÑÖ9¼¯©Ôs½0p¤ã §céÊGÈU¹]z¨×œk½½«1‚æ–´HP´‰î;(> ³…a/òœŒy1Ðd¹zTÄ›½½Û0‚Þ–´G„™í»(¾Jq•aOò¬ù1Ôà˜òÒ’{ž©´Ëñ/ àšË÷ƒ›»mX<¼—·¶ü¸Å•ÚÏw´8[»Z§[Ö[)“má)QNP—qó-'åÃZ5Â寮NÅÉ^‹ ÃmC3^¶D[ˆø8¥å̇üî¤%HRŒ%gzO­®ñ¦“«ÛwP?¦§%²Ìy–Ëi/óoy¥ÄRT®2´dGXêߢZC4¥j’¯Q¨w³jH3%Û¤5ÛÌ›k²¸Y(”è&"RR¥-*âS{µÂÛ`†ÉîÓNöYóvjUÊÐížEÝÎ4®,‚à€v­È®¯ )iò)……$žE‚… 1ÚõTy ŠÜµ0êc¸µ6‡JÅ-!%IÐ’Gɸ|â¯'Y¡vjÜ—-+*ðÌ©m8¿ qR]вÚPµ°œ‡ Y!%!*I*EsjY¥ö}g ¾iåÞ!-Âãm®0ûŒ­ÒÛkZc¥8‚—Š”¥Ÿ^ŠQ4ºõ‰D¹LĈò$<´¶ÓM ©n-G)©$œ*ç¯njU­ÔZ×lstÔ®ÔÔH‡ ÃÚææd¯Μ°7n{«nþl®BÉf.¥eÙ7(ÖæĆÜzK:Ê’¶V‚ÒÒßœ%À®2¤y’T:Aq·Ï¶¼¸Á“ Ó»}¥6£µjBº=…¤üÅ$zƒ\µ£I¼ØÔ¡Fçn³hDXRdµ"e²Ét–Û—,ðn@JЬ:µ€•'ÖËu±ªé©—r¸ht¸ô(¨€é²½ÝL”)¯†m ¹¼%.•¤¥(ZÖBÑ4¥i}ž^t_…ÌûdlMÂDÕ»'¼4eæ”ím¾8o–°®\–¸ NÕ+ÊÇrѳW Èb n½Ö@y2r—JËN6´Äq`ÂyÚIR•)K 敨jÉÚFåí«–•l"ÑØ‹ƒj~:ß¹$4 †´¡àäkáVнêNiÔ3)—œŽÔ”6´©LºTà%*ÚB°}?1T©ÉËc³¢ÈFޱ°Óù#6ôÂÜÃySåCo¨Ø¤õõÈéO¶x§|ûN±ðpqw>iœ;·g“<û÷c¦7mÇäç­*r=òØÔéR£¬o´þÎ8Î=07hÁØRøQÝêw©]}0:RòÙ¼shëÞWÔê9Þ˜8Rq†Ó±ôå#ä*Ü®½Th éS®öö¬Æ šZÑ"ABÒ'¸ì ø*΄¼Êr1äÇAzå&ïovÌ ·¥­ä!&{nÊ/’œeXSż«>Lu8¦•1z»ÛçÅK14µ¢Ò´¬(½ÙJZ†Úy^Zq×=z¾¹‡ “û_¿øï€øÏÅÿ@î‹ïùxÝñ|Þž}*2´·_´Æû"f^§_­‘­±µ º IZ¤¶û]ä:Ùa.eE88‚ bªäÛ,s¥G]¾Çª;8丩¶:dì [*ùpw§Õ=:u!<‰Nã0ëË[…- ¨„!%JVÈ’Iù'ä¯_Ÿá~+Üdø?wï\JáåÛ»~1»o]¹Î:ÖƒÙ^¤ÒÕv‡xਫ਼ÄÔ¹%‰Ž¥µ´ë¶âí >æÔ©e@•c8RÊm«P‹mö]vJäFȽÁ-3ÎŽeØ—½\yÜ9[ˆÁ*À9Y¥IÜ]Ýbµ·ßmlæø"qÈg*ç9ÆžLú§Î½£§—Ò½tMÒ=“YØïRÐêãÀ¸Ç”êZ¬¡·¢ 82E¤Ý«¡nïºZùkHW5½Ôa¦ñÈáÊ~*w'*ô†}j´-» ŽþËsu²t$B¸å borä~1a(èÚ]Ü¢Fò”$!8Z•„§ºÑ/F*ík¼\nºxKrУ9¤Û\m¤ÌçòŽ1l ð))ÈeÄ6¾JK´}+^½\´LHw5iû¦[òfÁ~z´<ò¢’Énb²¨‰B’V  ‚‚€¢†Ð°€;´-¯JÌÕ²Ódh›`‰~2g?:Ù)ö µE a!ÒÑS­¯ï.Ю›Ôê°+K·^t\ŽÏäµ25±»Ë¬JT†Ýh2§d©KSKgŽÊZ }”‚ J”¼Òƒºmžï+ræÚ§FŽêq·^Ž´!hp(¶ HÁ YIùv+†¸kPÕ7í4ô¨­Ü/ _a.ü‰ó‘jXDæÉ=áçÙ}IB$,mÛÁ„΂@¯(7{J5Ž—rówÐómñ¦­Ùϰ)´–FöÝoº 9”¥A!E*Z‰Rz¥+BÓ÷m4η’«á±½¨\çàÂ-Ãe{’ V—c8·0‚ë{ÝeÕ•’z%f»h5]ù×¶2‡»¹„ú“ËÙï ¸–áB€µiJ’œ%a•Òµ›uÓC%»\G]Ò¬ÂUúXš•[ä<û6¥©%-%âÀRÉØàK‡á“½¼)°\ Êå´†e<Ër’†Ö¤¥æ‚‚à);€V¨ÈçƒÊ•1&ïovÌ ·¥­ä!&{nÊ/’œeXSż«>Lu8¦«½¾|T³KZ-+J‹Ñ”¥¨`§•å§sÐg ëëÒ>ÃÆcGþ²Oö¯WÓŠùöþ3?õ’µz¾œPU;büê¯Ý2¦iNØ¿:«÷LŸéšWWöwµ­¾¨ðT²âÑÐùÚ'Þþ˜ý™ÏæK«§hŸ{úcög?˜U.©YYó{_·Ë ¬Qµc¦¯4§´•¶cŽ*øÅò=»l†v\][É-HVå´Â…¸ðKPP=A)FíJGh3tš»õþ ¶w¤êå?&$×¹Îi_PèçÁÅh%Ç:ìa°0Ï´„Öeí3&Ëp»µs—lÇ·>’¹(!¤ –ÜÜ}c`FIÛ‚0B¶»…Öí£´N‹™?³+Ò“§XuÙäµ¶=­ç.Rn3æ;ÊŽ²‡X!m¸´<É%_¡]I°Fìî9¥ŸÔ—µkPÝçæIq- ø»JOƒ¢² zŒJmÈÚrÛ|ïqžb{ï°o#.3ÆT”Õ.¡@¤¨`õÁT»WÝ2Ö’¹X›°Ý÷Ì”‰H}WfÈmmÒÈ)îþ`ùÝÔn)ÈÙœWÂù`DC°1e¹µ.#îIL¥ÜжÔë©e.å À;pÀÚ7å%G%^”è‘äK”ÌHŒ;"CËKm4Ò –âÔp:’IÀ»¤iûüg¢³"ÇseÙ®#Ä}1[é$ÛV0¥• £' üÕås³Ýím´åÎÕ:[¶©ÖØZÛV× €ÉJº(|‡¡«|½v˜E¢u™Ôº¸±aJ~"â3Ï…5µ%F*žÉK(ê] (·h®›iqß¼Ý/ŒY§œ»ôKÔu˹‡Ûaq÷liI %Koá' ãâ ¯Àìÿ\Α"<]!|[±·Ò`¸ž2–Ã…*ÈQAI õVä€ PÊ&½¾«´g"»çlC ]±øî¦[¡×[i;³©Üë] …‚®µj¿ê?h‡`µX“u‰ÄRúW5ÇR–&2†VÛKr;% ÀuCà”¥…eyRD³TYmQïÑívKœDÜí~Óˆ»|*p8µ:®,9¸¥()Hlq‚’ ”WAÇ=÷®‘”ß»k yè¯%Iy[”¸„£Ü€Tµ¨mdüZŒ«¤íYhv÷.ïÙ:¢Â›\Vž”‰ +àLE-jo»ï ~$üRR)t R”mU£äØa‰i»[.Œïʨ|Ǽ2^cpu´½°¥ ¹ÆÒ´à´¶¦fòÍ•í;wnç!Æa®÷7y’‚7åWP?$üÕ`½ö„ó×Ûû-¿¸F³w%µõ¶ï;±R”6·VÛm)Ï*@²S¹{JB¶;^¯´Zï:uØ6)Þd¸®æˆï\P·Ý®/WC)Hoàòñçãù¼ÃhF[4>±¹¶ë4½Þ@n+sDEåL¸­­­#XQÎ6ç!*>‰Qrãȉ)è’Øv<†V¦ÝiÔ­µ¤à¥@õ‚ Xcê( Þ¯/;ç&Ûxc†[oÜR¹ŠøVÞ+ï[J‹­$’[9I#ÔîwG¢Hœã°aw(ÝL—KŠ2¥Œ£Œ¨€‘’p”Œ$-)J UÇE˜zIˆê}<ú@Rb4û¥õ/ )mŒ¶\@’ÙRBŽ<þ»ŽZCTÌj„Æž¹óÝúÛƒ‘ÔÚdŒ!J„¢¬à’@ëI·ˆè‹m»t–åÚüµÊT´©·9’ÚT×)Àe¬ç®ÿhÎKímÓÅí–žïp‘{bû8È“ÌË’šSŠHm J mnyÌ¥JZ±´n%AZŸ§ïöû¤{TûÎ%ÂNÞ¯ÄZwrŠSµeYP `u#ÓiÒ÷IšÆ&”š¹J} %kc‹Œ)!CvRÛ0'­wZµ´êSíÚvrb0‡Ãͽs>ñq²Œ†ƒ|iÎv)¥ei^ô«hœ‘ÚŸ{UéËñÒÓl(He–nÙC‹D‚ú KQ„Œ­À ”ÙIÈ •sSŒi S#N'QÇÓ×7í å=í¨ê[`5ê$*F~1ÀÊTÊU„}I>Í:Rô…Êùb‰#f曹+‘[Gå­´¶Ô¨(ÀV>rz¢jXÚû¦¢¶É¼:Ô×ç°éœ[ä}ä¶ïU)ԞϕM«ªüÙ ¤#±]ÓˆÔ’ͽ×ÒË9ÐÛäïÎÇ6ì;Kd»9=¶¯~ÿfÏ‹Øîví»3Þ¢-¬oß³ãñ¸ÜÇϱXô5'o¾XÑ,ÙnnË–ûrU)4!´ºÒ^KXh°NÜ>w ùQHÁO¥Yo]£X'éiV6tµÍŽhB#o*ð…ìÚˆHJŠ{¸Ýÿ@Á##;œôÊv… ãg»ÛbÄ—qµN‡j9":üu¶‡Ñ€w †“‘ŸQó× ^5î¾FªµºÇ…w2¦¦tÕ2"¥— pdã¡Ó‚êñÈë„rTNê£ÐNh6檽&ÒÅÖÙn}͉eSœZRó‹u ¥´lJ‰QS€ú`¢Hš/Kݾ»h°£í•ÖØCü–v•6¤¡YÆÀ¡á* H)VAë^z.ïÁ«mWÙ0Ýš‹|¤J 6øh­h;’7«péÔdtÎGM¦õhçj›j"Ï2SR›i™èjCkh:–Át´¤¨mya_2v‘´y1¤5L8GO\ß´+”÷¶£©m€Ö7¨<©øÇ)P)V8Y³ÝÞ³=zfÕ9Ëdu†Þ˜ˆë,6³·Ê¥´2zùCç« ½cïÞÜÔV>úë—IWVYŽÈ~FÎD¸ªRÚø&ð”­µ»Ï’ dô¶¿³Ú4㩚nLÇQ T'LÆ¢‡ùA-©ÈÎ8Îéò¡À‚AQI+^à¨M³¹N[oî3ÌO}öMïäeÆxÊ‚ò:¥Ô(• ¸ ŠŒ«5Âù`DC°1e¹µ.#îIL¥ÜжÔë©e.å À;pÀÚ7å%G%^”g^jï¶OŸ¨.wo šÔæ¸Luöy[VRJJ¿ÜtÁÁ=zÐpÜt¶¦¶Ê‰ã§nðäM_FŸ„ãk}yj¨åIõ=zZt½Òf±‰¥&£Á®RŸC EŧZØâÀã HAPÝ”€vãÌ ÀëRvÍK`µ]íÒ-ö{ჼ—aɼ¡Ô­O5ǹ8Ž”§ÐnJд¸ Seäv§ÞÕzrüt´ä e›„vPâÑ ¾‚RÔD¡#+p(%#vRr%ALÓ¶w/r%Gb\hîÇ…"`Kûþ2ÚœZµ'ͱ*#v—ÎXôýþûÍàv;Óƒo7sˆ·¸÷gní ã88Ï® Ké»î™³^çN»ñÞŠìXìø³i[Hy…²éZû¹ 8p”á)ÛÕë`Õ6{]®áorÅ&àÃϸäf%ÉaÖYJ’*9XQ¸ÊÙR‚S‚’” R”mU£äØa‰i»[.Œïʨ|Ǽ2^cpu´½°¥ ¹ÆÒ´à_´í]âžö­|ññÝ|=Þn-Ûy6mÎÝÝ7céVkŸi ¹:Ù"Ùbx{°Òô„-Eø€! yM´Ù}%±Œ/*I$¡hIRèïhVWfÆRôäá ˆ³˜™—0ß)ÊÜ3°Œ’Ô £Ä³ÝåÜÙ¶DµN‘=ä%Æ£5juÄ)I@  î‹×Ò¤íÚXܢ˓oÒ÷yH…+¹É ÄZ–ÓØ$ ¤ Ù|Ý<¹H8ÜœÙuOh–‹íÑçצç1lWâÏh]¥­N3Ai|l‡‰d¯(éÐùª!í]r§Ä»Yä»j‘ܸ™‹9,¾ßtaQØÜê›ZUðkVü!;•‚6´…zÝg»Ü¢Ë—nµN™9%ºÄu¸†‚w,€BF£“CóW _tohLØb˜Ë±4PÍÅË„»ïŠâÃ`„ªSx›ÚRR®„¨¯¦ÚÔmS©¢Ù–6¢»³l([fs\K Îäìn㑎¹?=¦¦Ò÷K”ë¨ï–ÿ€¸ÆiÞìáy„>„…­ !\kIÚ@#¯Já¸Ùîöر%ÜmS¡ÇšŽHŽ¿m¡ô`È$¡…$ägÔ|õo¼k›EÁ»“fËw“a‹fŠ—opEC mIP ©–ÉHÛÔºsç9®ÚÚ<ÉNËn Dû»W‹›¬ÜYÙpŽìC`°2û¤nå#É×Êw Ÿë—§& t…ñVï¡§`¸Ú–Û`‘¸ ã)IRR2Tƒº[çÚ§9éLmc‘‰-)·ÊTö"­W gnsPŹAÓ­DedD’„©†Üy§Û[K°ËM$„-[TZR‚ŽTV”&³z‘m‘)&ÓnvT $%é<î¬ä’¥¯jRO\ ¨HÚ”ô'*PpÒ” R” R” R” R” R” Ø>ÃÆcGþ²Oö¯WÓŠùöþ3?õ’µz¾œPU;büê¯Ý2¦iNØ¿:«÷LŸéšWWöwµ­¾¨ðT²âÑÐùÚ'Þþ˜ý™ÏæK«§hŸ{úcög?˜U.©YYó{_·Ë ¬Qµc¦¯4”¥*º“)JP*é'LÚ®…¶1œÝ³Âv-¸òù@· Šk ''ªötÈ;UŒ„G×S2[KJ[KJÒi. r7%@¥Cç}©Ë޳¿Ï¹Ä¹>ôJˆŽ&ÖŶ;›ØãXm°obB6,);rœ`CËV[`BM¦u­2Z‰u„e¶Ä—RëŒí}ÖJK‰JBòY*ÎÔà(¸Ü|ì6#t‹*t‹œTËm§%K©®”6Ò¼Û‡;v‡$yoWi׉I‘9ƉBm¶Ëe¦‘’v¡¶ÂP’¥2¥(ž¤“ëc¾ÜlÜÉ„¨Ëií¥ÆeDjK*)ÎÕqº•'pÜ Œ€¥p£°µÙäÇÛequž’dÜ_µÅCR\Q‘)µ %¶ü˜Pp8…%Ìñ€¡½h$ÃFÍ•£¤j•ÍŒÄw–$»’*[M-¶Ô¥\ZT’T•æµã±t“6»u½¨·4Ü_žåÊ2‚Vè@<à ŒSÆÙJšXÁÉÙR]"Ùª7qe’…¶^nÞÂd”/;“Þ9pBŠHÝÕ$§âœPkŽÖŒnôòÝ2%\U*PFÀ†›Jž+g$¼ÆÌ‘ÌãË™Íi¦m¸·”ÛŒäȰÝÑi–·ÞCˆ–µþ Idf2¼„¹þ`óyrªÊn’Ë*Ó‘Õ)2¥‚VÊ•á!`§O7~È®«Ö¥¼Þ"¦5ÂKK@XuÂÜfÚ\‡ 8ò§œó+ÎáR¼ë9ó+ Òv#¨nnÁMÎ ¸·éEÉaÝ… ¸°8г„©^v‘ê@2èìòÿöÒÖyQš–ã xV÷ùk[N!(i*qjC­¸‚…|E,e±¾ ôåöã§§96Ö¨É}Æ‹ñž5«]J€Êr’qœ= ^çÚ¥º\ŸqU¢d†â§¬–0㜋ÊKX$¬©Y# ­x>ud<ãi¦|v÷azg4ØPŸy‡C5ÊÂy]C‰u´¸0Ûo§Aä üœš¬ÔÀÔw#*å1Å4ä«„^ê§Š6©†É@)h'±±[@ÚR¤=”¥„ö³Ì‘¥íöŽò|]û|yDÜØ–Ë.Èm%mª;h eIZ–— ƒKÆpJy`é;^¢UžErmq§?9—‘>[O)¡†ßqÀæÖQÕ­ eKYLý[¨&÷u;pãu‡Ó$=–ØyÇÓ¯8ãiJteD8²¥¥åJÊF¬¾=:,ÀüfQa2ÃqµÍ̶€Ú·¤¯rNô€•e ÈšrÎÿiv ‹eζäû,(¢îĵ9Èáo•© µÆ¤¥]]P´•´z°ÄÖwø·öoŒ=3c¡(›lrÔ`¼q4[ãhïʲ„ƒ¹J9Ê”LEÒs×)ÎMˆÈuÌnLhÍÇl`ѶҔ§Óä'$õ&ƒ–”¥”¥§4^šŸª.ŽÄ‚Ì—Qƒ&OvާÞKAIIØÒz¸¢¥% tP*)HR“^±\YLÉm-)m-+Hu¤¸‚AÈÜ••œAô"‚__ØþÖµ½ê–䡨3]ežòœ8¦‚ÏAÈÚ¬€ÐÔÆ‹Ó6‹¤[2n&r¤_®ë´Ä[!´DZC ´”(¼3%>@[ÿ,ù¼ÙLÃR]'Þn7y½Åù·$-–»{+v7-)Ùµgz@VI9É$¬º–ógЍÖù-!eÖË‘›uqÜ ã*ZJ™sÊŸ;e*ò çÊœ¤kl´û¾Ù)¸DºFžT–VÓÍ>¯‰·!ALzîÁ Æ2`êr&ª»ÅÓŽéöE³ÃÞêâWjŒ·|à(º¦Ê÷$8°•nÊBˆIAfÕš6nšµÁ>le*n ,¶Ä‘¸mÊ”‡TÒYu#)šq`îII)9ªÍL^µ%Òï1$÷c¥aÂÔ+{µ€@RÃ(HY¨¬íܬcqÌ=”¥”¥”¥”¥Ó²m #ZßÚmÄN¦eGfs°Ø.ºŽUœHÂVKŠò¡)$å[P¿M-¤`Ið¸WÆnlÜ/·,¬¥ K=ÁÔp-ÖÔ…<Ò[²8ÈÝæòÕliÖ+Ì[½±Æ››|Œ-ÆèBÇ¢¶¬äzƒŽ„0@5ÝV_ wŽèüf¹ŸT„â'»:¬eÈùGøutO™­„lG°œ)J ö×hˆÆ˜’ÚçGs·&D¢‡ÖÚÄ—™Z›@ †wòà¨ú×u摵ßb36%ñ†Òæ}¹3ZvCíW ÜIJTU·z8Ô[¤¯smóK×:ŠKÖ·”å±—mO¡ø+hˆÂ˜R¥¤Ûc˽JVÃå$’A5|¾M¼ð÷¶mpîÛÜí‘âg8Îî'w ÆsޏÆMv¹·Ú-Ò­¨µ3:1‘njT˜Òä¡õ²· –Ø!´%AL–WÐtÞAê 9o´xÆýzft¸ñ%Fˆ˜Ñ$¢:Ô·òÂËŠmÁ„†Û·®ðr6á\*¾ÜW¨ßÔª4›„‡Ü}åIˆÓÍ­Ç2TKKIAê¢q·àŒ`RÇ}¸Ù¹“ Q–ÓÛKŒÊˆÔ–TSªãu*Ná¹@+JáG Õv¯Õ7k?xðé¯DæÙ³“e¶äã8Î2qVn™´?f‚ÓÆq¹Üm.ÌÈCÈ 0ˆýçà”ÑARÊ»ª¼áÄãyNÏ5ewiÎ7pKî5%w‡$¿!„:ú–U¸:°V‚I;ŠHÝùY®¨Ú–óÌm-IhGض’³µ>ÛkÎöÐñO"Våå PIÞ¼ƒ½YzœÕØmÚv\ÉG‰ZûËé}Ô¯¦Cì«i NxB€9#8Éõ¨Ùž~ XKD`Ô]üjn3hq[ŽN÷§?Óy;GARZƒU]ï°cB¸‹g^Œ֨єØÊ•´)¦ÒBw-jÛœIÆzÐ-«KšÞÛh›zŒõ©é¬3&áJi´´µ$-`¼„”íõRp'¨êg$iË;7+Œ‰Ëœkm¶Ö™Êav%*fé-±–¥6×HS½p…õii$vÖ¼vâ/¾4Ò£G—èDx4Év–ÙJ“¤íÂVàrsÓöÙ|ñO翃»÷~äÏuâÝ»»ìáÛ¿Ï·f7ùþ7Z 4M w„µ=¾Õˆ";×HОY™¶Ëïá¿"BÁPI*(BB‰oÊäíåA‰q‹w‰"ó~zÍ¥>„®Ü´çÁk/d$Ž/òÕÔnò×£êËã3¥L/Æ}R¶rµ*/±ä[Úˈ-§bIJ6¤lI)NH¤ [¨!w…5päu÷Õ$½!–ßy·ÕÏ6ã‰Rštá$¸‚•”œå)ÀyÛ-qÝÓ‹Ô¥ºUÇ‹-‘ÖCªRX#ü°Ó/úî-úØ·J°h¨Ò¯ U»P®< ¢.Ì…©r }È*îÄ` IÁÇÿ†}°Gƒt‘Ùq·% ;z•¥ÀOа¤¸œ Fî¾W”k¹:²ø ±¿m3Æ7.*qÔ6ACN¸Q¹Ö“µm¤aÂS€œÖÚnÁjº]œ¶7s6ûìZ§1"RóùSÛ\mĶw%‡Jv§.n!=]¢é“Õ1cÉfùÞ.Å“BйnÅeh[D¡k©RÂINæ•çWPšÍóV_/wFnW7ã>û/ª@H„ÊSªPRÜ[I@BÔ¢¹JI* HV@¼äêÓuhÔ×6`Ïš©H”ûnÅBX´Hq´¤…cÍé»$ž¤š]Ak‘d¿Ül²ÖÒä@”ìWTÑ%m¬¤”’ÆGL\5ë.D‰rž—-÷dHyjq×]YRÜZŽJ”ORI9$ו”¥”¥Áöþ3?õ’µz¾œW̰Çñ˜Ñÿ¬“ý«Õôâ‚©Ûà§U~é“ý3JvÅø)Õ_ºdÿLÒº¿³½­mõG‚¥”އÌnÑ>÷ôÇìÎ0ª]];DûßÓ³9ü©uJÊÏ›Úý¾XMb«5y¤¥u@·Ï¸wŽáL¾ì¤?ÀÒ—ÄÒq¹Å`yR22£Ðf½-Ö{½Ê,¹vëTé‘á#’[¬G[ˆa'rÈ$a*98ô?5WRnR” R” R” WT{|ù0eOKÑ!ìï/¶Ò”Ûν@a;AŸSéOŸá~+Üdø?wï\JáåÛ»~1»o]¹Î:ÐrÒ” Rº¤[çÆƒ|ˆ2Y‰3v}Æ”–ߨp½Š# ÚzzZå R½cG‘)ÂÜfya p¥´„$©JÀùRI? ü•å@¥)@¥uO·Ï·÷~ÿLNòÂd1ÎÒ‘ÊÒ³µÄäy’pp¡Ðâ¼¢G‘.S1"0ì‰--´ÓH*[‹QÀJ@êI' *TŸÚýÿÇ|Àî~/út_xø»ÿ讕ܾôôëéH~ÿpºHµ@±ÜåÜ#nçŠÄE­æ¶¨%[S…GBqAJRJRJW¬Hò%Êf$F‘!奶šiKqj8 HI$àAåJWU®ß>ë9¸¸2gKw</D‡³¼¾ÛJSlo8Fõ„î=}O¥-)]V»|û¬æàZàÉ-Üñ±¥8âð 8JA'þÀÐrÒ•Õ"ß>4³äA’ÌI›û³î4¤¶þÃ…ìQVÓÐãÐúÐrÒ” R” R” Rº¤[çÆƒ|ˆ2Y‰3v}Æ”–ߨp½Š# ÚzzZå R•ë.<ˆ’ž‰-‡cÈejmÖAJÛZN TPA ÐyR” R” R” R” R” R” Ø>ÃÆcGþ²Oö¯WÓŠùöþ3?õ’µz¾œPU;büê¯Ý2¦iNØ¿:«÷LŸéšWWöwµ­¾¨ðT²âÑÐùÚ'Þþ˜ý™ÏæK«§hŸ{úcög?˜U.©YYó{_·Ë ¬Qµc¦¯4­]š›[7Y’î—è6¤xtÈ­‰ >²âߊó)#‰µà%KIVqÐôÝéS6ëc¶ø{½ÆGÚÖ¡vè¬2ññvÂcῌ{²º;Æ<èëñ¶Õl6#t‹*t‹œTËm§%K©®”6Ò¼Û‡;v‡$xnÐ%Ú®’ísÚá— õ°û{‚¶8…¨d=AÅWRnZÔ5dí#r‹vŽÕËJ¶hˆìEÁµ?oÜ’C Ú Pðò5ð«h^õ&…Oßî¹X;œ»|mÜò˜ˆµ²ÖÔ…+rÀÂp’ ÉèhÖŸ¿»ö¬w5ÄœøôÄYnC¤mX”JT6ŒœƒóPiw­IÙäiÖ;•ªÛl‘-Ñ¥®£¡×Ñ…qµDm·S´nq甕$¬åK5]ww²=`·Úm ƒ)äJzL¹lÛŽ¥!±°,4…ñò‰+Bq€ÛŒƒ…'b+7;=ÞÖÛN\íS¡!Õ¸Ûj‘m…­µmq ¨ ”«¢‡Èzµt-Ý÷K_#maÉ æ·ºŒ4Þ99OÅNäå^ƒpÏ­®…“mp–©îÁ!qJ`IŸ¼ÆŽ÷" Sì^àZ y…-'“oÓw=.5 æuê~•å(ŠÔuF¶-¸ÏÞ[h\7ƒ” ¨J”J”…%t/µûÿýŽçÿEâô‹ÿ¥üÿ§ù_ýýkÔémL%IˆtíÜHж[×rs{+t€ÒV1”•’AøÙ͹¸ú=©ÚÝ‹v®´C·\PbÚ땆»Üy*< 6¤å[“ò‚b-h´}Í.‘ÔÖÆ.fƘÜ5³$¹†”‚‚¤²Q¹Eäù±‚wâ ØÓ÷÷üG‚ÇswÂóâ",÷LnÏ.Áãj³»üÆéûûU=ûͨˆa© }q–ÒÓ¤†œ*#VA W¢ˆ8ͪñqÓ£³XÐ"É´ ‘CAÆâ pœ…+{ŽÅCŸ•JL‡ï*PWÁÔ4û¶Öoöç¯QÝ“lnSJ˜ËG q°V”õJrQþ⻵Õ:q–ß¾é빇6<üu%²VéNìcvÜå9È ‚: fM÷M‹ºÛ¾•œò×sf_q´<†ÑÞc4Ûv.:ÙmÆQ¸²”«jA Z”¼ù[fh–n–e;vÓR£5§¥@¸ Vg‡¥:^d«®öHy%.”4¢­Š; 㥵5¶TH—;w‡"jøâ4ü'[ëÈPG*HÀϨùëÒÓ¥î“5ŒM)5 r”úJ.-:ÖÇRB †ì¤·`NZ •¸[_½ßüzñ¥X‹&+ñÂÕdò:ç­²ìpÜbY Ymj 6£Ð¢©=3;HijX-—K–•wÕÒB­O©ómVįƒ$¨¡Â•ÿšŽFÂTØä ¡iÛ;—¹£±.4wc‘0%ýÿ mN- Ú“æØ•»ËŒç¬z~ÿ}æð;ÎéÁ·›¹Ä[Ü{³·vÐqœg׃KÑ­ÌéiFcXå@µB¸"L©¶w]Kׇ‹4òÙ-í)\d%—”U“ż¡G!¥w&× Ø½)m72“ Y!o,¡JQ@Æwõòò·í ¹E£À4ãVíMlº?n„¨oµ™(VU&KÛÁu”—P=sœôÇZæÓO[¬z¹ÖݺƕÈRá‰ÑÛw„h,…¡.mJœ¼™ÂU´+¦xn:~dUYQÆ®+¼ÅDˆÄKŠY*umq”‚\ mIÂA¦ Íz/Ij3}vÇËs™ra„<üV ¼^h)(QÜÙ@PÆô¤œc>„‚ 7ŠØû—ÚÏÆÙö½á~'Â÷uåñ.û»|Ûvy3Çÿ&ß=.—[î5ÒÈ/q %^Å6S/îP—Í¡¶ÔàÞ¥…#r”ÛU„š­M^ï–­Pí“—!Hmå„DuÂÓ+ÚCÊKiRøð´«!'!CÈÏ¥Hj›í­ë¥—O\îQ},8äXêw•c œÔ”ç“óÖ×H÷½g|½DC¨>ã"SIt°‡R€PŒàõÁ5e³ÜtìnÍdÆzM¡»£¨t%„’¢Iw4U’:nnKa ‚ At*œ¶é·.µÊu¶a‡d¢Ýȳ!l´•)Åä$¶Œ%+jÖ•œ„Èܶ‘-WH‰¼Ë/ 0Ï{‘ÍÃjxÈ!y8H ú`æµNÔS¥,ºÞM²ín¶)ˆÚ…ˆ6¨Ær< ËSí¸¥¥²µ:ÊBÖ”€­Šm Цi9‘¬ÎNTè+ÄVfÉ€‚ç:­–˜,jLR.±}åA mQÊÚKŠC­mpŸ2Ê\N@ÊéAë-¤3)æ[Ô”6µ%/4àIܰ}F@?8åJP)JPY»/zÝ^Ù®—[¬kdKtÖ&8ãíº½á·P¢„†Ð³¸€qN¤t©}!+OZ¢ß¬ræééÆZ⩹ÒÑ=1ejq°XJÉZ™;T„´Iê–É­i;Ô77`¦çÜ[Šô¢ä°î†\XhYÈBT¯N»Hõ Eé;úï®Ù­ÖÙ7i(aR-ì-íì-([n€¸%IqÌ€ ”·náâ’ü+¼ø:û¯yÛÍŸìß·¦í¸Î:g8­RžÏ¦èˆ®·­4ÅÉ6²ÃŠM¥öÜK¥2ŠJÜj?™I_‡åy' <2B×Ë—Û¬÷{”YríÖ©Ó#ÂG$·XŽ·Â0NåHÂTrqè~j“•£ïh¶F¹ÀŠíÚÖápuø1Ýq[Þêx” …*eÀOÅò’ h'/:;5,›@¹4n, · ÈR·¸ìT8ÙùT¤Èpnò¥µ|2ÓÃâ‘;ÏvàçG'yäáÛ¸gŸn=vy±œuÅyD"\¦bDaÙZ[i¦T·£€”Ô’N';Kjh1e˧nñcÃZ[”ëМBZ‚JR²FHZÜŸœP\õ ãH½ªíN4ý±è1#*fÚÐa—ÔÙ 8áDHêu)sb”Ú™X IÁsz›±o¶¸Âúü«¾Œ“(ØS"aÚe‰o‰‰|´#¡ĆñæHm_¸rb…?Oßí÷Hö©ö;œK„¼_ˆ´<îå§jʲ @ÀêF+Êõg»Ù%&%êÕ:Û!h%©qÖÊÊ (qFÐÐiv¹ÝžÀÔ7W2Ñ%r¢Ãq¹IФÅmÐÙÐzÁ%omZBY 莭«ËA^´m¯Tµ-/Xí±¢êKw½Ç“'’VÚ£¦3¡¢êTÙKªÊ¸·îl/xÜ”çÚvÎåîD¨ìKØð¤L †[S‹Bv¤ù¶%DnÀòã9À1”]žfw»¦ãv±ÇS6I–wOƒ)IÿØòòÊR˜ sîF¥K¨;¢-sK\Fu5±û„i²f9 Éaöâ  )L„nIee^l` ¥Y¨8~ÿpµÈºÀ±ÜåÛãnç”ÄE­–¶¤)[–„NO@s^OYîìÙ™½=jœÝ²BËlÌ\u†XÝåJÈÚO•]ü“óPhZæé¢%Ŷ«L D5·qiQËì•®$|+Êûi„”ºÁîÞä•y0 ÷-J¯v¯p´ÍÕ2Ÿ]Vnwž„›dGãBÖNÇ ¥jV8%HNì#j|¢ùgrÕÕ!RãIjçLeLoòGR¹)ó%m,dtÈ$×­óO̶ê쌸ÕÎCÈŽ¸ê„—¶‡Ø•%+$‡0Rzb‚K²ù6hº†BïÎÚvé-\¢¸ón8¦Èh$)ÄáBŠÐR ”« ÉÁœÑ72‹ÍñíD»B¦¸¶“b ¶Ô2Ò7¥a-w7’’¿‚Pø« VT’JWG½YîöII‰zµN¶ÈZ‰j\u²²‚H @dŸô5ÃA¥é«žƒöÂ✱Ãiɯ(1U5ä²3°!2a¸—3åqÖ£»‘@m(Œµ\4Óz9 >»bq J&Cr\Ù]ày§øÎÆ“˜Û“ÊŒñ9äV۬÷{”YríÖ©Ó#ÂG$·XŽ·Â0NåHÂTrqè~j“ÓÚ>÷{°\ïÑbº›eµ /I1Ýqa|c ÁÚ *Vž›”ÉÈIÉ]®/gÖdÅÔ–‰78î ‚¨¯¬ždEJPCŒñ(¤²½À’’= ³Šµ[µ&‹³]¿Á™lZѪ#ÞbÂnÌ9™„¥:Œ‡Þ9R•6vd6‚„©·¬ã=ÔzCTéÆ[~û§®væØóñÔ–ÉZ7¥;±Ûs”ç ‚9ŸÓ÷ö<;žÇskÅ1áûâ,w¼íÇGÂgrq·?|â‚Ã:Ts©íÒS©t«²•ÈoO„[ÙFÓ´-®ì ŽT?É8ø3¸õÙÝ-ý)´;ü»uÎÆ¸“yž³È]¹ÕÂ¥H u…²s†Cˆ6âB–‚1É¥Üì÷{[m9sµN„‡VãmªDu¶¶ÕµÄ‚ 2R®Š!èk×OÙܼ='ü\hQ¢1Ï*Tå¶[Þ–Á! RÎVℤœ¨€ }ñ.ݦëÜ$iVç®ãÅ\Ë2ÜÚCs_@àRxVâ·¥¥Œ%!E-!a¨úÑ«3Z¶ê;!©s)j‚¶Ã€Tr„ü( ÊA 9 õ#©å¾ÚäYîkƒ!m8Bu·$¡ÖœB\mÄ䤨¡œ@õ±éûý÷›Àìw;§ÞnçoqîÎÝÛAÆpqŸ\ÊR” R” R” R” R”ØcøÌhÿÖIþÕêúq_1þÃÆcGþ²Oö¯WÓŠ §l_‚Uû¦OôÍ)Ûà§U~é“ý3JêþÎöµ·Õ –QüZ:1»DûßÓ³9ü©utíïL~Ìçó ¥Õ++>oköùa5Š6¬tÕæ”æž¼@‰k›h»Û¤Í·Ë}™$E–˜ï%Ö’êQçSn »^s#nIÚA ðê ¤‹Ýþãz–†‘"|§e:– qeD$N2zdš“Ó–ûG€\oצgKTh‰J#­Ky,,¸¦ÜH`»zï#nêí^ªnÖ>~ñáÓ^‰Í³g'Ê7mÉÆqœdâ«©4›:ŠìQb̃sžƒT+ŠX@iÅ:²AiEySîah A Àê£Ò殀ü"Ï$Ë™ %¶àê'%-¹9ghi²[tˆÍµ)iÎó³¨ á‡¤æI³793 ¢Cñ^›Ëœò#³ÉÈêHAlð½ÑKJÂNS»¥ýó2®l¹~´[¢Ãš·±#c‘ä–B]GÁnÂ{ÃE@€®§jUƒA9qí.;÷›¥ñ‹4ãs—~‰z޹w0ûl.>í)!¤©mü#€¤á<`|BURZ-Ób*Ïl»· –ÔˆÓ.¨|)Yápµµ”Ü('ÌBú¥'ÏYiVl:¥ÛMClº¾Ü×a¨ 9…H^ÁÊ^JPœœä…©#*Æ òBè.q ®M —Ðóªq‹£ËM ¸ë‹àZÊBP¬cr¶£Ò‚Ãm×ÖXK4èºvçV›\«k-çxyNí{yeE.¤>é$`íRR„†IjKE‰Zµ[.îG»E0‘Ý®¨aÖc—Râ’¥ð¨,¨6”µ ¤¬mó «^2õŠÀåî "^ÖØ‰ri·VÓˆSŠk)AJ\Ï"€”ù†I ;ëÎfžq¾ÎgLM¶.ßc“/,³»rrvªBc•áAYÃc=zZ _#MZÚ"ÕÞ»´ÕN†û"(y—T–ˆTˆïcM‘³a•yvúÛõÍ¡ˆ¶8’,·wãÚ­mÜBð„¡k”rn:ýc³4äÕÁaé’¦^y9\e$%<’µ …‚ Xô³è;ÝÛIIÔxº¶ÐÛ«! ¥•8”[!!JØã‰Y“½ƒšù}¶Ýmí­Û3¾0"Lj©FfXKl6†Ð¤2p¡´Tµ¤åd$‘š~é"É·^¢!¥È)©M%ÐJ ÛXP ƒŒŽ¸"»®6Ø è‹5æ:d¦\™³"I:•6®$°´)$ô¬”äc8 QmݧeÀL”x•¯¼¾—ÝJðêd>ʶ”á'„(’3ŒŸZ ;f¥°Z®öéû=ðÁ‹ÞK°äÞPêV§šãÜœGJSè7%hZ\ P)È2ò;@Óïj½9~:Zr … ²ÍÂ;(qh_A)j"P‘•¸‘»)9ªŽ“±Csv npmŸ¯J.Kì(iÅÆ…œ„%Jôë´R—Gg—ÿ¶–´ëʌԷ[À‚·¿ËZÚq CIS‹RmÄ„+â)c-ôZnû¦l×¹Ó…†îüw¢»;>,ÚVÒalºV¾îBÎ%8JvàguzØ5Mž×k¸[ܱI¸0óî9‰rXu–R¤€ ŽV@n2¶T ”थ$s}§Éï×8æílm«síÅ\—ù˜mrVPÆm*B€©Ä¡ -ÊHÁ1šªÎæžÔsìoË-ø©‡¿‘=ô¤œ+)Î1q‘‚B2¦tŽîŒnÊò"ÜU**‡[J^ $ç ²ÆÌ•ÌçË…²×Ý1x½J[ E\x±’Ùd:¥(‚?Ë 2ÿ¡Îâߨ݉Èú:;:B÷r¸ËhÜb[¢Ïf32€[(yæB9[R>-§Â›YÙ€‚ š]E}Ó78VH‘¬7xȵ G%˳nc—yIé;\*x€¾ <§Ö¤Õ¬´ÏÛ#wt雸ЫZÚ7–ÎäwDÄJ»·B $`åD€ªQiÔ[äY#¸Aœ.qCÍÊnBÂÉëYËÍ´Z·ƒ»#¡Vì'ºó nV»ìK3Óã/°ì—‘fGîì6•-n¬>¤„¡Ãä '@pN]{F°\5Ý£U¹¥®hvØû’PÂoÚ·U1rÓ“Ýú$-×RG©AI*­B¾XfåkrËsrÉ1ö$¡”ÜЙ-ºÊJr÷J’yÈãâuòÞtS×üKM¦ýh¹™1U$=Hsh RJ \\å̧;RÚŽÒñPõèÝÒ÷mq¶[_³0Ô©"bktw€H(ÈÀuµÔéÜW”ªËuJyæãµ-JK-6 ÈJw¬A’OÎMXlš–%¯NM·µm’dËalÚÎ̼Ô¥µÇ€Ý¦\5º¨·+sr›V•…-—AÀÀÍ:Sëä(Ï\€ô¥(¥(¥(¥(,=ŸßáéBnÓ-®Ü@Šûe6€ ­©¢¥q++W”§ã9¤Ééxì.‚õojìÍÅl:´ñFËke+Ca y‡ZCa)!)l`„”¤ª—J /Lv Õtzé:Á&ç-w·nÈqéQ•ÕjB¶¸«VQÕÆCEY›·†~¸²?¥—§Ó“š†Ý»ºDpÜZçBûË’ Ýq1Ò§[Ü´ŽBÝØßµh¡RWÛhˆ›pbZ¬-G+»³užcIS+’ãn<ç•Ä€¦ÉT—À^T¤ 0rÙS”*Ph×>Ь²ÓgKrt!m•!óÝ¥Ãg™4–ÖÚÜ$¶Aã@;­É+B j:ºíóyTØ6¦­­%÷ôÇìÎ0ª]R²³æö¿o–X£jÇM^iIØï·72a*2Ú{iq™Q’ÊŠsµ\n¥IÜ7(c )@(ç†\‰å=.[îÈòÔ㮺²¥¸µ•(ž¤’rI«dÖ[•âëtn‰Û¬qhœ‡Š`w€Ó†+Êciãpº„í# $`¤„¸ò"Jz$¶!•©·Zu+mi8)P=A`ƒUÔšN6¥¼Æ³KRZö-¤¬ÆmO¶Úó½´™}ý¥/à× ²–ÔêvŒŒ-`ïl‚ T 9}¸ééÎMµª2_q…°¢üF¤'cjÆ×R 2œ¤œgBA—¹ö©n—§ÜUh™!¤8„©ë$%Œ8ç"ò’Ö +*VHÈ+^YêíOLAӠLJ tRòµ÷¹KRÕ‚ÃnŎ⯘¤¥]BU”, ]«NëaÞfN½2íÑ™hH\d¦0l- ÛÌ:ÒB¹ @Ú•mIJr“¨.’/wûêZD‰ò”êZ -Å•I8Éé’k†”Ðn’"[.6ä¡§cÏB´¸ ãZ—3€àHÝ×ÊãƒòI³¬ïíYž´‡ ¹ø¢«vÛÇÖÈÛµå6\!;Q·ÍåØ¸ÚœW©AaºëÝѸ-ÌE¡H€´®2[³Dl# R¶a Í•-D å$¨’ yH՗ǧE˜ŒÂ¢ïâj,&XcÎ6¹¹–ÐVô€•îIÞ¬¤Pt °³¬olÜœÊ- ®DQæ‘fˆq â\™ q“½);ŠwyG^•Ò{@Ô¦T™*U¡k•˜oY!-–ˆ-£iknÔüŸù ÅV”škV_4îÏ ~0â|Hg¼BfO£ß*Æ£µ9RpNÄç;F ÕWuA~‚ØërµFZ™¥%•Ée;”¥¶RTJ@$šƒ¥…gjÌõ¤=ÈïÅÝ[¶Øî>¶Fݨ/)²á Ú¾o.ÄmÆÔâ2ñt‘tr2žCM",V¢²Ó@„!H“‚¥nqXõZ֯ʮP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPlaã1£ÿY'ûW«éÅ|Çû úÉ?Ú½_N(*±~ uWî™?Ó4§l_‚Uû¦OôÍ+«û;ÚÖßTx*YGñhè|ÆíïL~Ìçó ¥ÕÓ´O½ý1û3ŸÌ*—T¬¬ù½¯Ûå„Ö(Ú±ÓWšJR•]I•'¨ï·C9¹·EFSí°†XˆÔtñ m@ÚÒR„ƒŒàè” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” Ø>ÃÆcGþ²Oö¯WÓŠùöþ3?õ’µz¾œPU;büê¯Ý2¦iNØ¿:«÷LŸéšWWöwµ­¾¨ðT²âÑÐùÚ'Þþ˜ý™ÏæK«§hŸ{úcög?˜U.©YYó{_·Ë ¬Qµc¦¯4”¥*º“)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP*ãôœÍQöôÐY]žÜåÅæ_.ºËc+(Ú‚œ(Šs¸c=q^«Ô1ì0¯)1g*lè¨b,˜ÓC*Ðóo%ψ¢¢Ë~…=ºä‚îÕê;&±‘¤ã1ã·X¬%é-ZXyþ  Î[ÑH98Î@ƒ±éûý÷›Àìw;§ÞnçoqîÎÝÛAÆpqŸ\¹µÚ-©©²RÞ™uVé1`´¶$H%ÀäFK ¬)è«lÚŽáÇ»wP¤‚Rxckˆ_úÛ—;<›«·)¯ËJ%¿l »ë”˜Û’¥tÞ¶ÉP ÆÂ”¨~ÿpµÈºÀ±ÜåÛãnç”ÄE­–¶¤)[–„NO@s^OYîìÙ™½=jœÝ²BËlÌ\u†XÝåJÈÚO•]ü“óTämQnoE öwfÊØ´7"K¬- îYPãøvÀ$¨%/„••%AJB¦×öwtt« ´Üžy6¶`®W|`¨)¢Ò’½æ71NæSðjt¤$íNДm ƒú~þÄO~Çsj"jBŸ\E¥´´é!§ ˆÆÕBUè¢3Iú~ÿoºGµO±Üâ\$íàŠüE¡çw(¥;PFU•R1Z ë][¢ÚãÜír{Åî^¡¨äEﻉ(Kªpq©–¸÷-ÄŒ%nå-à¯Ê’¨‹ž¼*çgz=¦t8VõÈZã0ô¹KÈJþT4 …¬8‡ÓåÀä*«=ÞÉ)1/V©ÖÙ @q-K޶VPI@(Œ‚3þ†¸jcW]¡Þo*›ÔÕµ¢„§`¬ÊPi ¶ÉämdnR¡è,Ú[GÉÔV·&C»[|MjPßæ>ûÉQe )l o(XJH>bA0ìÙîïYž½3jœå²:ÃoLDu–YÛåRÀÚ™= ü¡óÕ‡Gk6ôΜ5«žá&lyH’ë¨-¶Þ8÷¡D8ç¶ãkI))RpwwD×ñci…ÙZ³:Ùf,¨Pd¥qëqÞS§kŽ.*œQøe‚[[@¤ôJI*!^ÔzCTéÆ[~û§®væØóñÔ–ÉZ7¥;±Ûs”ç ‚9®š~ÿjeÇ®–;œš|Gqrb-´¡Ò€°Ù* (![}pAô®ëåöÛu·¶·lÎøÀ‹"¥™a-°ÚBÈH!†РRÖ“•JvIßu…¶~§Óטö»»H³¢#^wîf:[JÒÐÒÉ;¢¤ç̲ ò„½-©¢Jz$½;w!˜ª˜ëNÂq+n:N Êd6ÁQéþµåö¿ñßð;Ÿ‹þÝÞ>.ÿòñ»âù½=:úV‚Ïj6&UÖ4½Î+ð;éŽì+”XjA”ÂYZ°Ä$ ÉJAJ±}IoÖvx©²§Á.e6ëZà>amËÜúŸÊ›v*ÐRµ…%X!¢R2 ¨Ý-óíSœtƒ& ¶±ÈÄ–”ÛˆÈe*Œ‚û\µÝ›ãy•:, 2ò÷!†ñ„ÿ¯@“êv¥)É;R„á#†V«Ž‹0ô’5Ôúyô8€¤Äi÷Kê^RÛl ¸%²¤…yýv+Zœ›x€þˆ¶Ø·In\9¯Ë\¥KJ›s™-¥I q‚œZÁÞzïö€H&i-Gé Ðå–æn³/7¸¼™,|E tl«)Ü1òä(¬÷vn ÛžµNnl…–ÙŽ¸ë8°â›Ú”‘’w¥HÀü¤‘ê*ßYi˜—m+p‰¦né:qi-!ÛËjæB_v@ "0ÁåtdÉN0 Ü;¥ö—kÙÝ·i™,ªÝ½¥ W¤¥Ö‚Ô'´Ç ÝÆÂ* N⬡I;@Qþ×ïþ;à>sñÐ;¢ûÇÅßþ^7|_7§§_J}¯ßÿìw?úßÿ¤_ýWæ=?ÍÿèøßéV[¦¸…>äàvÏ$Zµ‹k±üf\CbH’ Jf2GÂ%$å¥ ú䂞gµŒIJžÅÂÇÞmïw#?{(RL6ÃÕ„åÄ–ÔyeGªTߥ šX·f]áZ^î 6·ÐëÝÑxh³ÑÝý2€“J°2• å*Åz¯ýie½Î¸??MIq‡n’n±ñ,%È ò¥âÆŠšF‚Ò€*É!B@©=+gsPê86%ƈü÷ÒÃ.ÉßÇÈ®‰bTFU„çÉÀÉ•ݧî’,—ûuê"\ˆš”Ò] ­µ… 8Èë‚(&ѳU§òÙ65á€ü–ÖÔ$©ÆÛciqõîi!-¸Ò·œ:œ€B‚c>×ïþ;à>sñÐ;¢ûÇÅßþ^7|_7§§_Jµiýsh±M…áÖ[»0¡"j˜ÛxBe"D¦PÂÜ%€J’”„æ*#ËKF½·[XµÇgOºëQí µÍÈaþt=è- r:‚'¢Òç—aC}V>Ÿ¿ÉzS1ìw7†ú#ÉCq¥0ê×± ¬åR—å =Iè:׫š[S6ÚÜsNÝІÐû‹R¡8Pʶ¼¢qÐ6¢䞇e…¯¡ÇÕ³õ ížrB"0Ã}ý¶,²ZÜ‚e $¥”¥µ%(â;VZ¡ÝgÕ~Å¢-ND~L›„-BýÖ¬Mq)Žv´TœÇØþÒÉÎŶpçLnVÀ¡3g»½fzôͪs–Èë ½1ÖXmgo•Kh>dô'ò‡Ï]Q4¶¦—lfçNÝä@yim©-BqM8µ/Œ%+• ñºzÕ‚Õ¯‘G"Æõ«2X…& iŒ©RZxºT§#¸ï«Î±ÆÁIÀåF6먠\mkßæÅÝÈQ ºû¦+­0–€¦8·+-“—1¼n‡SéE¦%wmAemYZЂû%(t €­Šø«#ªIAÏQ^SôýþßtjŸc¹Ä¸IÛÁø‹CÎîQJv Œ«* ¤b§'ëŠÕ1õ]¾ÇÃ{ñDÝd»"Yu•>\)m´¥+$”©KV@XŠ»®šþ,‰¶åB³;dLièÛâ6‰L†](îñY s`è¥:„ôÀ!AZÔÚ~fŸTMq¥.l^ñÆ”¸•²C®4¶œJÒ’—¶–’0GN„Ô¾ˆÐM_h•qµÜm÷I±!¼Ãêu.$Éu-4¾ˆ))Ü£Ÿ6@IééŸ[öºâ¬îi—µ ’E¶Üm½áW’ë®ÇåSˆFä6Ù%Dc¨Ú”Þ¾Pµ–ë-Ê5ä_.+ŒÖ‘pMßcŠi¶œd´w6µÍ<ò *Æ <¤$¥AÍ7Cj4j;­ŽÕn“~Ôÿ·mQ^y´9Ô|€2Tœ)8ÈëQ–½?º²ÛÖ»ÎsN¾c¶¸ÑâVèAYlƒ•­¾¸úU¾^¿³É–!7Y¯MDYS}†èÀÝ‘;•œ©¥´²¸)ué¤u5š’yÛŒÇY¸Å´N³EKˆ2|:¤—á(XÈR‰/#´‚R7†sJRƒ`û úÉ?Ú½_N+æ?ØcøÌhÿÖIþÕêúqATí‹ðSª¿tÉþ™¥;büê¯Ý2¦i]_ÙÞÖ¶ú£ÁRÊ?‹GCæ7hŸ{úcög?˜U.®¢}ïéÙœþaTº¥egÍí~ß,&±FÕŽš¼ÒR”ªêL¥)A£B°h©×Í›v¡eh/)wfVYBä½ Äa’ÚW“ê2œ îgEè¦a‡bê&l«‚-ËjæË}å¦ã5&Ôê<¢C( øÁGâäìÚ¥2­²’«CoZ×¾ Ú²BGdmÚÐèâ” QÝÀÌö´ÔŽªÊLæ,rœ—lCQmqÐêŠR”à’ F0(,6­=¤]Õ:COÍ…|nìELþ;“CÙ+G‘ðñ).íÉ? ”•…náÒ´UâësfU¿P¶„ÅzT&[¸³”¡ˆ¯<èqÂǘ©M ' Q9Ú7CÛµeò£:†;ñË˱ס2êYÛ·g‚–ölHNÀ6€p:Rª»À¾È½DÆ¥ÈaL8ª7’ –¸øÓ¹9„‚B•ŸŒ¬…¿Bém‡¨n.3|TdÒ¶Õâqã¼`ðÈ{;Ò¹J#*TŠÿ!(R«4«˜Öwý5»Z‚Ò;Ò&õ¶;ëKÈ!aN6¥œ«n—rˆÆãšõÄ;pfg$Z¡ nïoáXÇÀ¥ÂæzŒ¹r zOÒ×8]ßšU}áô°Ž ä7°¥g[;ÓªÕ„”Šƒ¥ä-sbtXK•c.Ê߯¦ïÖÚvŒî%Ò–ÿÓyA“OµkŸŠxoz±óðsîñÈ|;wmÇ/.ÍÙüÛ±×ëPt œ¥®oΕ ¬aØ»9åòVᑱÅ:çúì'ièpiK\æ÷ŽV4wwÔÂùïÙÊ“Œ”ïtoO^‹NR~Bj”´íÂE˜Ý›‘hLp…¬¡Ë¼T?„ç?§™èp6äôÀ9“§nìÂìä‹B£”!a Ýâ­ü+ø¸\ÏQ‘·#®@Á¨zPNOÓr-]Ýû¬Û`ˆãémÅ[îq&¼€rJƒM¼IÀÔ¤g‘‘Ma¦'éyɉ=èÎ)_JCƒ'ÎË/5ñ†9êw'¢"H‘S2â>ìy ­.4ëK)[kIÈRHê# Šò R” R” R” Tæ‹ÓSõEÑØY’ê#0dÉîÑÔûÉh));OWT¤¤ƒ*E) R`ëÖ#ë‹)™-¥¥-¥¥i´—H9’ R¡ó‚>„P^/ºNѦï:¯Æ"Ýß…g»·n¢3î!Þu¶òœSKl` Ü@€0ª®«µx©»XùûLJMz'6Íœœk(Ý·'Æq“Šî^³¿¹y¸]žz ò.+ËCÖØî0ëƒ8p²¦Ë|Uç Ýç__2³.D‰rž—-÷dHyjq×]YRÜZŽJ”ORI9$ÐyR” R” R” ºh½3hºE³&âg*Eúî»LE°òDE¤1ð«IB‹Ã2Sä¿òϛ͔ÒêbË©o6xªo’ÒV]l¹·WÂ.2¥¤©—<©ó¶R¯"|©Ä=”¥”¥”¥®—-3hbÍ9¦Lás·Z!Ýž·X};·Á% €¤÷¤ùËŠÏòþZ]LIÔ·™6aivKF>Ä4¥ˆÍ¥÷F66·‚yÚv£RŠFÄ` ‰ÀCÒ” R” R” R” R” R” R” R” R” R” R” Ø>ÃÆcGþ²Oö¯WÓŠùöþ3?õ’µz¾œPU;büê¯Ý2¦iNØ¿:«÷LŸéšWWöwµ­¾¨ðT²âÑÐùÚ'Þþ˜ý™ÏæK«§hŸ{úcög?˜U.©YYó{_·Ë ¬Qµc¦¯4”¥*º“)JP)JP[û·K¹ö—bb=§ÅY3YT!%´ÆS‰CŠq*J€HJþ1RAîÒ–Kì }î<=2ìUTDm)”ûqVÛêu}ÝÔ+pçÛ‘¹ …S-vù÷YÍÀµÁ“:[¹ãb3JqÅàp”‚N'ý¯K-žï{”¨–[T딄 ¸¦¢G[Ë¢ ÆHÿQAªYìöƒu‹ª ÂÀ­W.-Þ@މh‹iJÙ 8^PQŽÞÅI!ä©í'qØ qÚî·Yî÷(²åÛ­S¦G„ŽIn±n!„`Ë ‘„¨äãÐüÕÃAf\M44s²`Jïw':&8c©„’Å–ÆRî„î.î)RÏBy,øºE߸_/î})žÎÓ[ë¹IÄ•nPé„ ûB éA9".‘Lè¨|¾9[ûË®YÚCày6 I!y=Tœ£w¥;®‘ñN?¾xîk›—wÅãï8Û·®íùÏM¿-AÒ‚r<]"©Ò‘"ù|n"vwg[³´·ÈóïA’0z )YNßJ@‹¤WÞ;ýòøÆ×Ô–8,í;½®›T¬ÉNÕ¹HÜ´j”£é•Y‹²n÷vî{Dvím­‚±£”¾ƒÓ'¦OEcªL}2›0v5ÞîåÏb Žå­´0q¸r‡Ê°:àñõÀèœô‡¥äøºE߸_/î})žÎÓ[ë¹IÄ•nPé„ ûB¹¯ŒØáð;•ÎnwswËz#lôÛ·kÎnÏ\çÀõÏHÊP)JP)JP)JP*ÕÙÄeÉ‘x0 5p¼3nßkŒ¸©”\{¼2•íaAIt†TñÁJ°VH"«J hqãÅÕ.´Ã G_uйL¶€€Ô¥GmR°tl‡‹€ AH Æz¤àiûýÂé"ÕÇs—p»ž+·šÚ •n@N@9 ÅFP)JP)JP)JPi}˜Û›•k´)»Liѽ¸Î¤yÈHº[ÂcíZÜRIŠœ*QåIA;Tw|ÛšWuºÏw¹E—.Ýj2<$rKuˆëq #îY„Œ%G'‡æ®)J)J)J)J)J)J)JLh–-²µŽ5éM&ØíÆ:&]ã@d¸½ÊÈÚ6ç'#¹¨zP]5üIÌØ-ßìíZoîJ”‡c‹r ,ÅJ,¬²„ `­R^Ü«i;ºî½YîöII‰zµN¶ÈZ‰j\u²²‚H @dŸô5Ã@¥)@¥)@¥)@¥)@¥)@¥)A°}†?ŒÆýdŸí^¯§óì1üf4ë$ÿjõ}8 ªvÅø)Õ_ºdÿLÒ±~ uWî™?Ó4®¯ìïk[}Qà©eÅ£¡ó´O½ý1û3ŸÌ*—WNÑ>÷ôÇìÎ0ª]R²³æö¿o–X£jÇM^i)JUu&R” R” ³v^õº½³].·XÖÈ–é¬LqÇÛu{Ãn¡E ¡gqã HéRZYë ½›½ŽuÖÇ=‡_ŒûNÌnh€ém‚~ O.Ô‚.ç$6j«e·¢ã)M=rƒme´¹"ZÔ‘„©k$0„¨ú’Bˆ˜ûO’Ìë›+µ²ÛÚûqÞ–ÿ2›åp)Hokm©Ä¨¥· J’‚‚•ìW–‚ÕmÕ)7øw©7‡Y­W*úS5…w‰Ìº¸ê @e°÷øud(¶Œ­88Îܾ­P´%ÝéNA•& ¾ˆ¹kõ­K—1²¶P¦Ò¤ ËRwŒ(€¢*´eêhçl¬Û¼.IØVô0•&hIFRñ^]#“ s‹shà ùÇ,ûå²OwáÑÖ8\O¥Õð=0ó$g-«{êÂOÊSµ]:(W 6‹“¶g¯(†è·2°Òå,ml¹åø4¨à)Ì(+br­¹V0 4’/–ÇgEca¦7òFmé…¹†ò§Ê†ßQ±Iëë‘Òž9lñNùöcààâî|Ó8wnÏ&y÷îÇLnÛÉÏZƒ¥ä{屩Ҥ/GXßiýœqœz`n>у°¥ð£»ÔïRºú`t¤ å²7xæÑÖ9¼¯©Ôs½0p¤ã §céÊGÈU¹]z¨Ô(&#]ííYŒ4µ¢D‚…¤OqÙAðUœ+ x7”äcÉŽƒ õÊMÞÞí˜AoKZ#ÈBLöÝ”_%8ʰ§‹yV|˜êpLCÒ‚r}òÙ'»ðèë.'Òêø˜y’3–Õ½õa'å)Ú®+šùr…qáîš~ÙgãÝ»¹¹!\¹Æ7s:ç¦1SœôÄe(©'b:†æìÜàÛ‹q^”\–ØPÒ ‹ 9J•é×i¤åª,îX/²-K-ØûBÜ¿h%!E$-)RTœíRTR¤¨‘AJ³im'QZÜ™íleñ5¨ C˜<ûï%E”$¥²¼¡`) ùŠAú[´Y™¤—¨†§ÓÌ!´*#¯ºJðúÙÃeňΤ¨gÉé½9 ­*Í©´|›+*u›µ²î–ØbD›tvžBË‹¶ƒµAÄy“’¤¥[T¤ƒY U‡@]-¶›ø‘rŒê‚ÑÄĦ¥p. …iÿ•q;Õ)ÝŒ ©;·' JH¯WU®»œæáBk•÷2@* J@JR”H JR ”¥$€  ÐuuËL^ïºÎ ôhq.·¶.±fÊD…²°”ÈÞŸ#<‰QTBKx*Õ„©tÍmt{ÖwËÔD:ˆóî2%4—@ qÅ(HÎ\]ÊÑòcÝ/PîWke¹‹4ÓLÇù–És(Hmµ,î :A)êA ;´ v«¤»\ö¸eÃ}l>Þà­Ž!E*ƒ‚PqAËJRJRJR‚û ¯6h‘të× ›P×§¯Î]œeÆœRå¶¡†ÙØ•'“ü2‡Â'Î7ÆÛB«œÒs/qXy©Ðb®d£ ÜËåÍód‚ZFÄ))? ÐË… øAæè­µê)J)J)J)J)J)J)JLh›¤{&³±Þ¥¡ÕÇq)Ô´YCn%D$p:dЇ®«L w[¤K\¹¥Ì} 0Þàî-A)$’GRqA9©^·EÒÖ­? ëêìi²æ9"+n¥Dt% „+pàQ>\aIÁ' Vjbýb6¸±gG¹ÁºÀ’·nT@êQÊØA[e.¡ È6s·iÞ0I JRJRJRJRJRJRƒ`û úÉ?Ú½_N+æ?ØcøÌhÿÖIþÕêúqATí‹ðSª¿tÉþ™¥;büê¯Ý2¦i]_ÙÞÖ¶ú£ÁRÊ?‹GCæ7hŸ{úcög?˜U.®¢}ïéÙœþaTº¥egÍí~ß,&±FÕŽš¼ÒR”ªêL¥)@¥)A9¢ï,wGgN·I–¾˜ÎF–˜ïEt©$<…)·à ܤ¨)%*JHê…|°0ÍÊÖå–æå’cìIC)¹¡2[u”8”åî•$ó;‘ÆÄëå;¼»:²ÇÔ¾%®\iÒc­:ë0T…¡¶Vá e* sò¤1ÂrœîÉÓp,Ÿl.jFäÎð[£v¥±o”–7º®|¸[kòŽî¡·`'x9pC¦'h®ž/s´÷‹„{Û÷Ø&<ž[”ê›RƒˆRV\ks-á)RÃqÈ)£Ö—jÒÚ6áebúË7ÃlMÒ{rw\ã¦I†ÃIu i€ÑRÝÚê7mÜ„ñ¨¨¶…oo9–¨ê”ò¢4ëQÊÔZC®­(Ï@¥¢©gæ”ruÊe˜Zî*jàÓHCq]’ïÅJqµ-»ñÃ`Ñ%±½D'v='ë]pîýÿTß%÷gÓ!Ž{ƒ«âu9Úâr¯*†N:Œ×œ;pfg$Z¡ nïoáXÇÀ¥ÂæzŒ¹r zOÒ×8]ßšU}áô°Ž ä7°¥g[;ÓªÕ„”Šc«¤Î‹>F©¾=.þìû—TãÆ±EYNáÐãÔzÓíÇWx§Šý´ß»¶ã——fìþFíØëŒu GÖ:º4éSãê›ã2ælï/·pu.?°aÔ•m}¥ k]oïÃTß"w—Õ!þ ƒ¨åuXÜâ°¯2ŽTzœR>–¹¿:T$J±‡bìäS—Èhm[†FÇèKŸë°§¡Á¤ -s›Þ8eXÑÝßS ç¾Cg*N2S½Ñ½=z-9Iù  óªu4[1²ÆÔWvm… lÃnk‰`¡yÜ€íÁÜr1×'ç¤S©¥Y…–N¢»½lCb“\SÆÔì'nÑŽ˜5#iÛ„‹1»7"Иá YC—x¨ Î~N3ÐàmÉé€r)'NÜ#Ù…ÙÉ…G(B»Å[øV1ð)p¹ž£#nG\ƒAé?Xêë‡wïú¦ù/»>™ sÜ_©ÎוyT2p¡Ôf¹¯š‚ÿ}áñËåÎéÁ»‡¾K[Ü{±»nâqœ ã׺gé¹®îýÖm°Dqô¶â­÷8“^@9%A¦Þ$àêR3€HȦ°Óô¼äÄžôg¯‹Æ¥!Á€“çeÀ—šøÃˆNáÕ;“ƒAëÙþ¡¥õ »½sëLWØdÚ"¸ÒÝmMÊ‚”­D` +iÏL›¾·»Ot¼é¹÷{¹­.ÉC…¼èOÅ©°€¢¥—€”ï! •U¥ÓLkµÙ,×D* ³ï ‹—>L„¬5¿ä-—áåtò¶ê¤¥I îŒMêÐÞ’»Ø˜µNJæÜY—õÏBƒ(h8”!I çkÎe@§'i`…W©AoÕ:ºÁ—Û²ÙäÛU.XÜ“92T¶#¡¤¶„á´ä°Ú”pI(J¹*¨R” ˜Ò—éZ~à·ÙCNÇ€ÄÆa—CÌò!ey·P’Rp@8>•Sš/MOÕGbAfK¨ŒÁ“'»GSï% ¤¤ìi=\QR’: ¨¤)I Ëî¶µßoº†MÊÃ$[oSYž¨Ñf´ËÍ>Ú\Hø@ÁJ’y'-î'i*$(®«¨.’/wûêZD‰ò”êZ -Å•I8Éé’jß}Òv7yÕ~1îü+=Ý»txÈ}Ÿqó­·”âšX#cà nä …Uu]«ÀµMÚÇÏÞ<:kÑ9¶läãYFí¹8Î3ŒœPFR” R” R” µiMYÍÞ™6ÇeÈ´\Us¶-¹A¤"BƒYæIB‹å†¼©-Ÿæê jµtÑzfÑt‹fMÄÎT‹õÝv˜‹aä6ˆ‹HcáV’…†d§È åŸ7›)¥Ð)JP)JP)JP)JP)JP)JP)JP+»OÝ$Y/öëÔD4¹%5)¤º A[k @q‘×W ('5 â»\+E¢Ý&¾#ïIT´ÈyNº–’¿:[lmÚËxrâIÈ” R” R” R” R” R” R” Ø>ÃÆcGþ²Oö¯WÓŠùöþ3?õ’µz¾œPU;büê¯Ý2¦iNØ¿:«÷LŸéšWWöwµ­¾¨ðT²âÑÐùÚ'Þþ˜ý™ÏæK«§hŸ{úcög?˜U.©YYó{_·Ë ¬Qµc¦¯4”¥*º“)JP)JPIÙï·L”(JŒ¹°”ˆÓªS`î)i%`”r”ŸT¤‰7õ΢~r¦>å±Õ-†˜SK´DS K@¥¢Y-ñîBIJW·rRJA éUšPXQ­5"[·§¿4¥Ûî&çõÄeO‰EAJuN”ïp’䃵9jq-õÊ”ô—ÒVêÔµšKhœ©H Hù€@+Ê” Ušhm¾Ë­*DxÁÙ¹Á׸Ì Û6'“‚G+‡h8%Y# b³@¥)@¥+KìÆÜÜ«]¡MÚcNˆýíÆu#ÎBCýÒÞjÖâ’LTáR*J Ú£»àÆÐÍ)J×µ—d½ÃKK¾[lú–'v„ܵ35»pˆ«u*RZF1ÞÖ3/s{9Üxƒ&‰"DILˈû±ä2´¸Ó­,¥m­'!I#¨ Œ‚+ʯ½£hø:jÁo’Ì ¼y¥ÜV¶pŒ‘ÝÜŒÙ8ϯ%¨îB•ÚXoí¡·Z?=®ÜúÑ„2ß#˜ZÈBJr¥(à2Mf”¥”¥”¥½b>¸²™’ÚZRÚZVëIqƒ‘¹**8 ƒèEyR‚½gróp»<ôä\V–‡­±Üa×páeM–ù:«Î»Î¾¾ef\‰å=.[îÈòÔ㮺²¥¸µ•(ž¤’rI¯*P)JP)JP)JPLYu-æÏQ­òZB Ë­—#6êã¸@ÆT´•2ç•>vÊUäAÏ•8‡¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(6°Çñ˜Ñÿ¬“ý«Õôâ¾cý†?ŒÆýdŸí^¯§NØ¿:«÷LŸéšS¶/ÁNªýÓ'úf•Õýíkoª<,£ø´t>cv‰÷¿¦?fsù…RêéÚ'Þþ˜ý™ÏæKªVV|Þ×íòÂkmXé«Í%)J®¤ÊR” R”v=?¾óxŽçtàÛÍÜâ-î=ÙÛ»h8Î3ëƒ^¬émLóo8λ¸ˆò„7”ˆNÜ‚¤§…DŽnRFÓ×*js³ VHŒj$Þ¦ÚíÈŠÄ{’%–ž_yeÒIŒ’°?(óúØ’ƒ|³J×&ó=•Vä!±2+FT˜ñCq¶6ZËht ¥Äï,…%¢µ¥aZgCkWœy¶t~¡qqÖy(¶¼Kk)J¶¨ô;T“ƒò(–«Õ¨v]}²1i½9¨õ÷ŧ‘"b%»":ÒÄ”ˆSHPSÉ\„­!JêP£”‹}WˆOð¿ ïÒ|?Ÿ¼w^UpòíÛɳ8Ý·¦ìg+–¦$ÝíîÙ„ôµ¢<€„$ÏmÙEòSŒ« x·•`çÉŽ§tǤûå²OwáÑÖ8\O¥Õð=0ó$g-«{êÂOÊSµ]:(PAÒ§$_-ŽÎ‹!:ÆÃLoäŒÛÓ r7 åO• ¾£b“××#¥é:Lénã‘ù.©Ç€Ê”I8ö¥Ü¾ÛÞ• Q,PtêØ”ÛÆ}­r—!°“ê€ì‚œŒ0Rr‘ækËX^ ^ç%ø(Ö´§ã)½¡Çz$yÃim‘Œq´ßCæÜ¬¨„)J)J)J)Vt¶ÚoâEÊ3ª Gš•À¸.§üBUÄïT§v0‚¤îÜœ-) 8`iûýÂé"ÕÇs—p»ž+·šÚ •n@N@9 ÅFV—«®Zb÷}Öp`_£C‰u½±u‹6R$-•„¦FôùäJФ‚[À PÞ¬%K¦kk¤{Þ³¾^¢!ÔGŸq‘)¤ºXCŽ)@(FpzàšzR” R” R”Öë=Þå\»uªtÈð‘É-Ö#­Ä0Œ¹d0•œzš¸jû ¯6h‘të× ›P×§¯Î]œeÆœRå¶¡†ÙØ•'“ü2‡Â'Î7ÆÛB R” R” R” WsÖ{»6foOZ§7l²Û3a‡7yR²6“åW@$üÕÃWÛ­æÌåšé9«›NH¹Ømö”À 8eÈýÓ{‹%!¾3ÝW«R¼èÊG›hP©JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPlaã1£ÿY'ûW«éÅ|Çû úÉ?Ú½_N(*±~ uWî™?Ó4§l_‚Uû¦OôÍ+«û;ÚÖßTx*YGñhè|ÆíïL~Ìçó ¥ÕÓ´O½ý1û3ŸÌ*—T¬¬ù½¯Ûå„Ö(Ú±ÓWšJR•]I”¥(¥(;¬¶ô\e)§®Pm¬¶‚·$KZ‚2ÂP•-d’•R@HQiòYsbåv¶[c[_n;ÒßæS|®) ímµ8•¶á)RPPR½Šò×.‹¼@±Ý:Ý&Zø c9Zc½Ò¤ò¦Ü‚B€;r’ ¤”©)#ªòÀÃ7+[–[›–I±% ¦æ„ÉmÖPâS—¸ T“ÌîG?¯”îHZîô§ Ê“ß?Ä\µÆŠú֥˘ÙH[(SiRÆÆå©;Æ@QZ¼DíuÓÅîvžñp{~ûÇ“ÂËrSjPq JËne¼%*B±¸n94zæm'lÏ^Q Ñnea¥ÊXÚÙsËðiQÀS˜PVÄå[r¬`8jbN£¹L³ ]ÅM\in+²Q½ø©N6¥·~8l $¶7¨„ýc«®ß¿ê›ä¾ìúd1Ïpu|N§;\NUåPɇQš:Täc«¤Î‹>F©¾=.þìû—TãÆ±EYNáÐãÔzÓíÇWx§Šý´ßƒÒ5Ž®·÷Žáªo‘;ËêÿÁÔrº¬nqXW™G*=N( éSµN¦‹f6XÚŠîͰ¡m˜mÍq,/;“°¸;ŽF:äüô“ªu4«0²ÉÔWw­lCrkŠ`!Ú„íÀÚ01Óæ ‡¥NOÖ:ºáÝûþ©¾KîϦC÷WÄêsµÄå^U œ(u®kæ ¿ßx|rùsºpnáï’Ö÷ìnÛ¸œg8õÀ Œ¥)@¥)@¥)@®«\ w9Í„×+îd€T”€ ”¥(”¤)J $I\µ1¥/Ò´ýÁo²†!‰Œ8Ã.‡™äBÊ0ón ¡$(¤à€p}(;•£äǺ^¡Ü®ÖËsi¦™ó-’þçPÛjYÜt‚RÔ‚@0whíWIv¹ípˆúØ}½Á[BŠT2  â­×Ýmk¾ßu ›•†H¶Þ¦³=Q¢Íi—š}´¸‘ð‚•$ó:N[ÜNÒTHQ]WP]$^ï÷Ô´4‰å;)Ô´@[‹*! ’q“Ó$ÐpÒ” R” R” °éÍ'2÷‡š*æJ0­Ì¾\ß6@%¤lB’“ð­ ¸PŸ„nŠÛ^«V”Õ‘ìÑmé“lv\‹EÅW;bÛ”B$(5žd”(¸ÞXkÊ’Ùøþn ¦«@¥)@¥)@¥)@¥)@¥)@¥)@¥)@®«L w[¤K\¹¥Ì} 0Þàî-A)$’GRq\µÝ§î’,—ûuê"\ˆš”Ò] ­µ… 8Èë‚(:¯Ö#k‹t{œ¬ +q¦åD¥­„¶Rê¼€ãg;vã !êsPÞ KµÂ´Z-Òa[â>ô%KL‡”ë©i+ó¥¶ÆÝ¬·· î$œ€ è¥(¥(¥(¥(¥(¥(6°Çñ˜Ñÿ¬“ý«Õôâ¾cý†?ŒÆýdŸí^¯§NØ¿:«÷LŸéšS¶/ÁNªýÓ'úf•Õýíkoª<,£ø´t>cv‰÷¿¦?fsù…RêéÚ'Þþ˜ý™ÏæKªVV|Þ×íòÂkmXé«Í%)J®¤ÊR” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R”ØcøÌhÿÖIþÕêú›„·™J•!àTN×T?ÿµóóì1üf4ë$ÿjõ~ý„ËÎEmM´âÆÐ2”“òT†ÝTÏûiá•M1+ý¡N–4°iRqÌðÚãŠPëž§× 4®~ÑAN‡Ö)P ‹;€ƒòuE+ªdTDY[]¿ Æ<™šìïÞ~Ö€å»J"é&Lh†3ÜŽÆŽ—œOQŒ!K@=p>0ÀÉëŒïuÒ>)Çã—ÏàÝÏàísrîø¼}çvõÝ¿9é·å©ŽÑ>÷ôÇìÎ0ª]sܬù½¯Ûå…ƒmXé«Í)ÈñtЧJD‹åñ¸‰ÙÝnÎÒÜs#ϽHÁè0¥du;}).‘_xï÷Ëã_RXà³´îöºmR³%;Tzå#pѨ:Uu&˜LªÌ]“w»·sز#·kmlŒí¥ð¬™<}2z+Rcé”Ùƒ±®÷w.{Lw-m¡€³Ã”>U×®Dç¤=(oÖMÚþÚ-q&Ÿ··d·®”æW §T¢Í‡§IR•œS€*¡]Sîî߿Γ/»0˜ìsº¥ñ4œím9>TŒœ$t ÐgÛ,û#ãÚ 5ÏoV¨KÅ~eã TÊP ¦‚p!96Š£ØÙ°=Íã—+œ,máîvôIßë»vç›ÛŽ˜Æs“鎼1$H‰)™qv<†V—u¥”­µ¤ä)$uEyPY´üX[5ššOyj5­+ˆóì%.'ü|Tíʶ(¡J 8 #$¾–ëæ›cBK´IÓ0_¼:½ÍNSo§î€Yè¿Ép«su¦dHe·›e÷[D„ÞJ@qIVÕê7%'åH?%yPXgÇŽžÎ,²ÒÃIåÞàÚÝÔ„³¥$ú Ô@ù7œÓPÇh°[-< Œ”&ã5Ò€V„8€c²^œG””‘»œ%C- AªD…En"ŸuQÛZœCEgbV ¥è H'åÚ>aIr$K”ô¹o»"CËSŽºêÊ–âÔrT¢z’IÉ&‚Õ­c·'^@m«_'|…kqpí¬¡…<ã°ã©im(AJTµ)XÂʽ¥V®Ìwk¤¸ýÒL.'ÖŽí$åæp¢6,íNT=•=Aè=)t¸OºÎr}Òt™ÒÝÇ#ò]SŽ/•(’pìrÐhÝŒiÄ\rº^!;àJB-ª”mÊ”óî¶’–ÂZqIx6VP´µÎ£…í\Çeº èzÆÞÅ÷OÜæ\½ˆSâ"r™€Ð ò[RTÓÉqiK›S‰.`Ò‚ßo·ÚÜÒ:Ž «eÎîÒÂ%>·žko"e¶Ál¶Y7„ÈVG& L¼Í û5þñi·Ý܀Ţ%ÊØò¤¶àY<Sg ‚ðhºàRÑ´6[Âú¨U3Çn"Åà­ª3QG QmçFíû\u)8Ø;T¢2”ôò§”ËÚ'Kد:Tßm·v M¸²ÌåÜf. ã=TÓ±R!K-:êPIp¡ÃGÖÖUÙåG'O]ì(yn²Rä…z¯ojKg Q‚R¼(à„שAjì¿MÇÕZ†E®LK»èMºL€»kaÇ[m•¥E²>€ÈÊ–‘¸zÍ¢ ê;Íó6MC-µm0¨eŸû.«x!Õ³Åç-¯§OÈ¥¤„‡3šPj—®m9|¹_m·IÑ%Íjbc²ËIKF4—Û[J 0¤©JKH!B1³†Ç ­sû5sQw©ÏJdHZã0û­Å[ExeÄ·hÊ‚¢¥¾ÞÔº•WœÒS“âéw~á|¾?¹ô¥þ{;Mlk®å'U¹C¦vƒí ƒ¥䈺E3¢¢=òøäEoï.¹gi7äØ$…äô9Rp:Þ”îºGÅ8ürùáü¹ü®n]ß¼ãnÞ»·ç=6üµJ«¢ 79ÄZäÉ“cÙ1ÒËŠè3”%k®GÆ9=39¢Ãi³jù çY²ÊŸañ•ÌŠÚ”ÀíVLJQœ‚Z¬×T „ûxî¤Äï,*;ü©­+›V™')=(/š3DÚ.Vn³¡j±Ón›p•.ÞP#°¶ñLGP ÓŠ !ÎBOGPž?0r«÷PÚû4Óï÷xÈ}7K‹æØB[in"Ò ^êðTIX0*3N_n:zs“mjŒ—Üal(¿© ãXÚ±µÔ¨ §)'Á#Ðy¤\'Ƀ‰Ò^‰vaÇT¦ØÞr½‰' ÜzœzŸZ›ü.åáÿúEÎÛÏ ·ÿƯwxÝŸ†kàц•(óz1ù;»:†¹Ú¾$ví.ÝVPòÓ¦Òêò–V â[Q x££ÑÍ›ƪõ(5 ÖqÕIˆý¡ÞXÖ!‹|o¹MtÈ)R\gáƒOa޵)Rx©BHÙå£4´ö¡.Ö,—;ÛPíoND6ŠTìgÄ^d²ðâRV¤:C)1¹ËG4¥–å‚×xÓ’dDÑ×Å_QtžÜî9Í2Ý´|eRQÝÂi%N¼C-:r o4¥(4k悵ÂìÕ½ITé±ä-°û‘œ[…LäG 6PVRUÞ•4S±*VÔ5~‰‡glv6šÕJr]Å–Œëí•°U—;¡ÚÖ{ÂË…I;pP6‡ÞsJ R^ní¨Wý3|E¶u“u¶;ªLW\”-èXd-,¥.º!¬’ ¢T•Ðõu­v›Ê£®Õ:Є­§¾—$²=>AI$¤$íRN!F”v6lsxåÊç x{½wúîݹæöã¦1œäúc¯LºE}ã¿ß/Œm}Ic‚ÎÓ»ÚéµJÌ”íQë”À{F éA1>™U˜»&ïwnç±dGnÖÚØ+Ú9KáX=2xúdôV:¤ÇÓ)³c]îî\ö ˜îZÛCg‡(|«®\‰ÏHzP+KEŠ××ðtüˆRZhéxîÆ˜Í‹é¶"IQl$w…){Æ ’¥¼f•9kÕº‚Ý9¹­\;ËíB6öŒæ[––£RZB^J‚S´©8tR‡¢ˆ!j²Y!Ùû\Ñð͹֑p•L´ÝÚnC±C’Êšx)´‚T€¶’êô 4Ë.ûâúEÎåÁ ÇÿÁ/owÛ†wà×–“Ÿ0òú0ùy­w ö©ÍϵΓ[Yã~3ªmÄdp¤FA#ý‰®ZjÈñãv+éz·AD'e\b´á´&ZË-˜¥CY·Ëª;œNä¡iÂJ_J PöbË}Ÿ§Q®ñÇcBbå!)K2Z›+Kg»–‘†Üß¼<驤’¤·Íxìå¸ðn²bB¾<Ã…ˆ¤2!RmÏRÛèe!%{”„9¸6ésjpA5šRƒP»è 4}kd±)3­MCÎ8©RœSŽ” © !µÃeä©;°ÓJ^¤)Yí;MÅÒ÷ö!D‡â¥òÔ¶BÚ%kNЧYeN°+pm n)ë´¨ÕiAxÐVk-ãK\Ò½5|¾ÞØ›´ÍªfÇ»²îõø\ò¥il)D«lž»ýtÖ†þŒ½ßn6mCp]žâˆîSÍ®;­–ÜS‡”6´ 7±+.‚âHZÔî ª(5 !ÙÔÅ™‹wTùdÉŠ¶™um/‡“ %(ahQYh ¼Ò‚•ѵ‚ï…7°æ®ÌéKšß5æäÝT„-–Ûilq¨2Ki*[Ž ©i^ⱄ'=¥—¨lÅк-¨Ú&øtâ‘&HJ ¥¼§^@e· r´©Ä! BŠJOÕ•«ËRh…Çí ¾eªïm·L·4ôVŸe-½!ÄACªŠ…†•<\Ã;‚ ·¨%J$+9¥‡]Z£Ú.#³aÔ65¹8ô[Î ›ù7!A¶÷6RÔ¤y·Ž¸ÍpØÙ°=Íã—+œ,máîvôIßë»vç›ÛŽ˜Æs“鎱” œH¯¼wûåñ¯©,pYÚw{]6©Y’ª=r‘¸hלhúeVb웽ݻžÅ‘»[k`¬ghå/…`ôÉãé“ÑXëJ ‰1ôÊlÁØ×{»—=ˆ&;–¶ÐÀYÆáÊ*ÀëƒÇ×¢sÑz¦ZŠ•Yn÷y’ ÀR%ÚÛŽ€Œ¤¾á'8éêzôÁ‡¥Áöþ3?õ’µz¿z¬2aG!µ?) ¼tþ‹ŸûŠüöþ3?õ’µz¿|Æÿ§oÿ°íRX¿þßãý´0ø¾˜…W\€;>Õá) öNßÊOɵ?Ê)^½¢}âëÜîîŠWTÈɾÎÛ¦<œuUgÕóÿ´O½ý1û3ŸÌ*—_ û0µÛn׫,k­º$öSc´·%”¸ û# (œ3þ¦¡~Éû-šÏö½á6˜þnóÉÝc¡­øâÆvœdý&¹Wãª)Ê‹L_©›æ)›÷?ø‰ÿO¬Y”6ta´b½DêªÕMû›5OúbÔ¥+]s)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPkßa²Š>É=$´¡N®Q N2¯ðtéšýø”8ÚCa©* ÃåŒãÿé¯À¿aã1£ÿY'ûW«÷í[dMéÛ,§Úˆ%ÿˆGF QJ•Ðí““>»T!€ÅS}ÜM\'Stj¡ãÚWÙî­wcãu¡ïæÂ@NâIq âã'¯Í“J…ºj·ÎË5mÊÚ˜ê´ÉhÑl/ Ü3êNúàà‚ºžDÄÅ´L߯ ¶=»WGCófKm¦õe“u¸Ä€Ê¬r—$¼–ÒT_dà3€N?ÐÔ/Ù?z³^>×¼&íáÃÞy;¬„;³Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ý z•°ò9í«é§#žÚ¾šf8îúŒáäût1êVÃÈ綯¦œŽ{júi˜<ã»ê3‡“íÐÇ©[#žÚ¾šr9í«é¦`óŽï¨ÎO·C¥l<Ž{júiÈ綯¦™ƒÎ;¾£8y>Ýóö('ì•Ò*9ÀrQ8ÿòWí}Ub±jqà_kxÅq*BÓriµŒ섨 ÿïЀGâ®G=µ}4äsÛWÓ[8>DÍ÷[ßõõ1ZãÈ´Ù³íÐý™«˜aœj¶¡¹´›3¨iˆ²çRôA;RO®  R¿•¬Œ«é¥Y1N8¦šéšµz«§b뮋·å†áùTUu×—ÿÙsipp-3.6.1/docs/beep_1sec_50x160b.g7220000664000175000017500000001750013730472040016325 0ustar walterwalterÞ›7”$     àfª2¾SÏÌK‹MÑØûò­jéi«o·]ÖÑÏÏÐÔÛz´îìk®oóyßÙS’R’•ÛùuñîïðnõwÞ\ÔÓT“Ù›üöôn±n²töþÛÚÔQ”™Úüút±n°nö7ýßÛÖS•SšZßzöóp®òòwüüÜS˜Ð’[™Ùøxò°®³ñuwÝ^ØRÖÕ“ÞÛü÷·ò°m³vø{ÚÛÖÓØÙÜÞ·÷µëùmóùÙTý“ßßùº®·nu.wÛ¿UÓYÓ[›Þýöô°ìô/ÝqþXWšÔÓ]ÕÜûý²ñ³qr7ûßûÒ\QV™œ•öþtîööq´ù>Wÿ“}ýÔ~ûýõïûñ0üøzUÛÏ÷‘šöý°ô·qøzÞÝ×ÛWš=”ùrœojÞôþ~×ûSœÚ–y¼ó×kø±r–ôtT×ùR|šXÝo”ïòüî»Úó[Ø]ØSúVœøµ¾ôñóùp—1™ßYÒ~XW·¾îüòóü´ðÔYzSÛš×Ý4Ùvö´ïúôržyÓ3Ζ_Õ?Ô^±±ÿwñ²ñÙzÚ<ÖÞÒ×z–pÜÿìïüïþö¹_”ÜTTYÓwŸ¸¿psö²ö|sÒ~þÑÖÜSº3‘uòôêݸ°vØ_TYߨ‘ÝÜt²Üú«ºô0ÕwÛÛÜS\Ö]~¾3›¬íüo—mÞ]Õ_×ÝW™øžtör¿ïó¾sÑuþÏ_YÒ{–ÿ¸nÚï÷·òûÖ÷×ÿÕTÙ~›í¿ž®ñtò_˜v›}RÑ^^Þ¾Ýúëûv³øòuÔÔ½ÛUSØs–ô²õrõöö»[™{ÒÕÓq“t¹õønöøpœ|þÔ^ÕÒ8Upž·±ðpðùu¾ÝÔ]ÖÓ~Ïöüzœï»±î|ùö•ÛuPQÚ_œs’t°¯õøôò|ß~ÎýÝÓ›\Ôòµßñ°±õ6Õtü×ÙÔU\Úߺ3˜ïr·p¿tüÚÖ}ÔÐWžwÖ5ùm·µðöu–õÙÑZXÑÝ™þ»ñþrî¶ñûÖ÷>O{ÕÓûÚšðõÞ®¶óø2Ò0ÜÙÒ×ÞÞXÙ{¼êõù³óõq•×uZÔÑZ3•üòõðôøôµÖyÖW~Ž4™{ût»òqµuÙöÛÝ[Ž9•]\Ÿ/ å†rùòÛ~Ññs\Õßöô{TÚôtûØØüòÿÝÛúûû]ÞÿºýüþÞ{ÿ¾¾žùý}þÿŸ¹ü}ÿÿþÿÿÿ½ýýý}¿ýý¿ýü|¼üüü½ü{Üý»Þüøý¾¿½üüü{œûÞ¹ûßù{ß½ùú¾ýßz÷žûÝú¹ßyùÝø·šöÞw}º¿ßÿ¶û~ßwÛô¼øþúùÛýõöþüþzšô»û¹ø^üóù»»üz»úÝŸïúü|ùšôó–õvùÜÜñœ¶ø_þô÷üüùÙt¾÷÷˜¶ø]ööû»¼ø{÷×ò¹öØôøúºöØtžô¾úóÕtõžõöûÙwŸøòø›xr›÷ô›÷ux›ötû»üyÛßñs›úõ›t¾yöü÷׸öÞ÷tÛ÷žùµùvß·Ú÷¾yþzµ{ü¼ú{úÚöºx´ûwÕyŸóúûô×xôõžÝó·û{ÚúµvñÛø¾øÞvñÛ»öÜ»õwûþ¼üÛÜòóú¼ú×ûôþ÷¶ú|–ºôßµtßúõœû÷wü»Úùµw÷üûþŸ÷Ùôüu´ù›ù¾yóù÷ßù»Ùx°ßsœõœ{ô{øùÚòòwþy–»övœvû|·ØvŸÝótõþù–üõuþ›òv™ùßt¾÷þüõØûõÞ÷ŸwµÛ÷¸øùxø÷Û÷¾ø´ùõÛÛóüu¾ø´Ùù³ÞxóÞ»÷ß»v߸õßøôŸxß¾õrŸüôÜz¹òøöÙzõ¶¸zôš{õþÙt¸öÞø²xöÝô˜ûü\ïtó–ŸöÝ÷óû|ûû»¼ù÷›wþx´÷õ—|÷öø÷žþÝÙòûtžy¾ßðŸtÝø³Ý{µß{÷üÙ÷¼÷ßyò›¼øtõùø{ºøÚ¸ñuõ™üw›ûôrß™øtõ¸ö[ûwòÞ¸ùwÛù³õû{ÜßµþõµxüüÛü²þøõÜü¸÷õšùþÙõòó¸_ø˜ø¾ó»xÝyžùñó~Þz¾~¯Ùp“.oÓ   `àænÜQÍMT›\}ûv³n­-¯u}\×ÔÓÔ•T×÷t²ðîðsw<ÞÚ×Õ”ÓV[ÿ|ö²ñð.³xúþÚVUÔ“ÖYœøò±o²qóú|Ý”ÒÓX•Yßøô±orñqµx¿XUTÒÔU—Ÿtó´mðîö|zXÓW”ÛÛüù¶òòo²sûyž[XÑÓÙÕ—úŸõrò±nôóý}]Ö”R×ܘŸôsöïóór»þÞ[–ÑÕØ—öÞv²pðmø4xþWÚÕÓ—]‘{þú²ññ°nÞ1ÝÜØ“ÚÕT[Ÿ¼¶öö,ñòö}ùßÙUÒWšZ–÷žóòôoösÿøüØÓ^Ö•ý÷~´jÞkþúu~ÓùҙؓõÞÞ´q»núùpœ\Ý’øÑ[ùßú¶ö÷ñ1Ü3ØÚºÖTY]—p›òýõn½õzTuÝP’v“sÒøðó˜ôôo›x—rÓ\ÖÚüº˜muùóþµðÚY~×Ó]–s˜¼ïZîõù¶½Ý{ÐWxV–y×òö›ê»øñv—sÔØÛÞV×øšnšoµ´óÿžvÐWÝÙÒxŸ´íÜ|êpºý˜{ÓÚTÕû—ù»rë÷ùðø^Ýþ×\R]˜²õ|í°ïÝtœx]Ð×\Sÿ•þxµðòù®ó™qÜÔ_“_ØT”uûzö¶°írÙûw’uÒT×üÚþþž*»óùµíÞXÐú›ÔÍ9ß0ÜÚììðøœw÷Ñ~‘Y]ÔÚôõŸîùðíü¹yÙZÿÑS›>Õtžõïýïõ³ýz™úÒYÓ]•:Ó±µ¿îõsús×rÚÖØÔV˜Þ]ïšlëþöòúž{xWZÖ–zßõñŸóñm¿8šø}ÔÑVÝ|™Ù»î½nwûñöÙÛwŽÚXS~Y˜ô±¿òtlµö’t}ÖØÏÞ^Ü×öžêôt²r½o—ž|TTÖÙø{õ²ótnÝöú•~Õ_ØÔÛò/ßlî¹òÞwÜÓ9YR•]Ú1Þýðï¸ïXtµ}’Ò_ÞÓ}úú¿ò÷ô¬¶Ý8Ûþ\ÏÙÔÚr•÷pï÷µ´ñùÚ]ÓzÖS˜~—ñw—ë·õôr™t]WßÛÖÿÜp½òòõòwÜÚ4•XŒ–1ε6˜`ïÅw·rZW_pò×[ýôt^×úùöÜÚþùøýÝÞùzþÝþþ|ºßýü»žü|ûÞ»Þüû~Þ¼»þ~Ÿüz¿ÿ_ûÿÿÿÿ¾ÿ}ýýý½¼ÿ}üÿý¼üÿ|ýÿ»Ÿû~Þú»þÞºý|ûßýúºÿzßøøÞùºÿ|üù¸ú|ž÷ûÿÝþ÷ûþýÛööœxº¿øÝ÷ûúœùöÚsŸßó»¹ÛuúùùøÛóŸö÷~¹™õþv÷ÞøõßöôÜø¾ß³õÝùv¼úôœöÞxóßôù¹õvšzüö¼ûõ{ùùœÛò±øzÚúóžÜô²ùyúºù[ø³ö\ü¶w™ûôx÷ú»üøÙuûx±œÙóÞuŸôü{µùÜûóXø´üÛ³þ{·xÛù³ÜÛò{xµÛõµøÜ{ó™úôö_tŸú´vÜûõø¹÷[x¼w´÷ÜþôØùóßö÷÷˜~¾x¼û°Ûwßy³øö¹Ú|õ~û³{øùuÛø²øõÚyþy´õ¾üöûüúù÷šüñÚø¼õò×´Ÿxôõóü™ß¶yø¾uó×ø¾x¶uþ~·üþþøùvÚö¼÷µy~òØø¾vôÚÞs¼ùÞ÷±×y´òZú¹ûöÚõvø¾ùߘõsöœwõŸøÚõü7²ú÷Õûž:õžñ|úúxÛû³vÜûû×ùøõ²Þ{ø{¼»ÝüsÜøŸõß{±ô{ø™üßy±søû»Ýüùvv¸¹úzÙú¯\üµvßùÝúõõžüpÙü´öø¹öxÙù²ÞÚ´p÷øÙø¸ø÷óÜþ¹x™ø¸öñÜxxœ˜t¶tüyõÞù›ÜñòsÛþ¸Ø÷´sÞ{¸üÜ™ò|öõü¸Ö÷Ÿ5¸örÚüöÚú»wôÝxø¹¸wvÙûõù¸÷wþüü¹Øxò÷÷›ü÷—vºtþû¶Öûõ_ø²÷üz˜_÷¶Ÿt~{Üû±ûôÙyœ{ñÝôŸyõÛø»t¾xó{–þóûöÜ{¸Ù÷³öÚ{÷Ö¼ôõ¸vûߨû´þßóßûµÜüõܸóÛøŸvµõ÷ºØÞ÷wõõõ˜ŸùvŸöñÝzžz¶Þw¾yøù÷Ûxµõßw¶÷Ú|·ûöÜwœùõÝ÷´õ\Üùô¾Ýó´øyùû™ùòœuüø´ÜvžöösœùûyšöóóŸ{ùÖx»óºw÷ßþWú´óôøüÖÞ´ûs²öØÝ›z³óûz»ÞÛùò~ö¶š|÷ÝÝöô¶ßzøÛø²¾v÷zÙßµ·ô_wüùœø¼zòvúù˜úÞ÷÷y±ÝúŸÚõqûy´šÚøôuôö·Úü¶zõžyøöÞú²ÝúôÞúòŸùöø¶øw»üù™wõûy³{øüÙ¹ôu÷öÚü›÷û\ò´þÖ´Ý7º÷óûúüXzŸô»v»|÷{™yôõÜøôø›yñÛõ¼øòZvžú³ßø÷wúü¸Ùø¾uòö›{Üü°þv{›yüx¹øùüòÝXù³÷õ÷÷Øß´tŸöùþóšütþض²wÛþ³ÞXö²÷xûû˜ûôßxsŸü´ùöÚzŸw³ôßþöùÞÜöóºÝvžûó¶ôÝz›ûöÞôºöþz¾þµx·xÙüóùôœõÛy¾uùø²ùüùÚ|ó¸·\û¹y÷õÚz³Ûûò÷¾{ù¾yÜùòÝšóò÷Üüùݵß÷³ùx›ûõ·öx¸|¼xÜ÷óÛû³Ú|ôô»õÚû÷x›ö¼ûóÚy÷ù¹ø¸ù]ܳóÝûõÕù»öñÚuš»÷Þœtþy´÷÷•zÝ6¾õôúûþÝùÛt»t»ùó×zôŸûñÞúßuŸ÷¾ùóZ|öõ›»òûþ;Ú¾÷õþÛõø÷x¾~ô÷÷¸Ø{ßùîßµŸ;¹wþyþ¾òWùòvÛ¹ó{—yøûóõúßÙûµõsùþ–Þõ¾tïžß{úšxøö´úuØüžÞz²ñyø¾ß|œ÷qÞúô›úóvúšûöÚvºu¾ûõ×ùµuøùö˜þuÜõŸu¼úôÜ[öò¸ùõš|ßw²øøÚú¸xöµúy™û÷xwóßøœ÷÷wšxßù´öt¸ù{ÙØµòÞxóúþ»ÝÚµó6ßœô\ܲqu»yØþ÷yÞö¼ûõøxø›÷þy²Ûw÷šøôö÷Úù¶Ùx±Þz¾÷÷Ù¸÷zÚöôú¹ø÷Öz¾x²Ýô|´\w³Ý~øø»ø~üóØ÷wŸxð÷öÙúš|´ôôÞy÷ü¸ÛwŸw»x±Ü|÷Ûßðþù´ô|û›üó[÷»u›÷»|óÜ{ù·»uÜyÝx¼ø°÷xüùúÛÙòð÷·ù~•ÝržõøuœþóÚúõuÛܵ²Ýx¼|´Ü÷ßw¾{ôù¸ù\úò\ùµµÛ|óûøÜ·÷xœøuöœøÞy¼yïܺÝ~´Þ]öï›ùþ|õu¾þ¾ÕxðñßyµùÛ»ñu´ø{üþöœúõxøöœüþ×´pþœõs˜Üßõòuõœ×øŸôøu¸Y¶÷öÞû´Úxù÷ñÚ¶{ôtõ¹ÜÙøóßó¾zöù»øÛø¾v¸y¹ßòÜyþûºØöñÝy¾ú´užþtÝØõ±õß{ôù¸öõ™üsÛy²ö·Úúõ\ùó¾üö¹øXy»Üôð¾û]~´÷ôÛøµwÜ÷òû¹˜{õwõøÝÛ´qòßz·þüù¹ù\ø²ßÝw²Ûuõ·Úzœx»uñø÷Ú×ùüó²tšÝùZô¸ûö÷Ûûžy´s¾üÞ^õ¼Þt»wü|¼Ø÷ôóÞ[Ûõ·÷ñyÙú»ûóøöúÜÙ¶óßö¼Üùø´ß÷´÷÷ûx™üôŸöó|ÚºŸxòüõ¸øÜYöõsŸ¸õÚx´ßvû¸õ[¸õßwþö¶ùÜØ÷uô´žyøù¸uþûô›ù÷uõ÷Ÿ×÷Ÿuôòž~·Úz¸t÷úõ˜zžxòþv÷šüúöÝ\²þÛöötš÷ø÷œúôúôÚöœw³öŸßtÞßõ±üøÝ|¶÷õö{œ{¸Ùñþwõ÷·üûûÚ|óþ¹´\üøõõ÷õšûõùµÛõöÜù³Þûx´õóøûþ×ü´~·óÞþœÚ÷òúxŸx»òtŸ|þ~´øòØõÝ9´ôù×õ¹{óÞxøŸ˜tïöxÚúºÛÞrñ÷ùŸÙü¸uüõôÚ¹¸uÜvºÚøõõvÞöœú¾øòxöþü»™xóxµÛvº÷Úv»~÷ø÷»Úyþx³ö~÷þûÛwµø÷÷ùû×ö¾x±Ýø¸ùxÚò¾y´ÜÙøqÙõ³øþ¾xÕ{³÷ø·Û^öÛö²þûºzÕ|³ÝùóÞßöžú÷ôÜ~¶ß¸Ûó¸ô]{¶þü»ô[ùòÛôŸö·x÷Ø{úø¸·÷ÚXôþø´óš~øx›úðÜ÷¾yøÙõŸw´öøÙ|ù÷Ÿþô³Û7ßüôݵŸwþWõùu»Üöø÷›wö÷Ûú¶øÚv»|¶ö¸~Yüõù³wyx˜xüø²tßþ÷þ|ùµÙùúÚõð³÷ÛÝøÚ¶ðûúµØ_µöµÞw¶~úÛø·öÞ]õŸùòßùõÝù·óšxž{´ü]÷°Ûzßw¾y²ßüõ]Ùøò¾÷óöÙ^Þ˜²yô´sÝ^¹Üv»ôôuùøšúü{ðÞöŸûôü»ò^ü·öÝÚó¾zô´÷z›ÙsúòûùÞ:óŸÜ÷üøõtžúöuŸøüØö¼x³Ýx÷ô{óÜøµtßùµuþûôù·÷Üþó[»ô¾ûòœyÝûò߸ôÝøþx¾ü°Ûwuþw¹þòÚùžuüy¹þñ›wþúõužü´öÝ|³÷vú÷˜~¼úñvòÙÖ¹óôövÛX¸µõßw´Úyõ¶ÝZóŸu»úö{ùúÜ»óÝußú¶÷õœxÜùóü›÷ôø÷Þù¶Ýù±ÛxŸw´óßþöܼó_ûö¶¸y÷Ûßò¾ößx¶vžøü~òßøµ÷Üûô÷x¼~òùô]™öró¶öØÞú5¼õøüõÜûóÜû´ôÝüóÚø·óÞúù¼y¹{²w~tÙù³òßÚyõþö»ûøwyôô¾ûôxÞÚžöí¾xùüø¸öõœzôùôšøòßvóô›˜÷óþvß|¸÷÷üüûVµóôµúû•{Þø°ôs×úœûs»÷xÛŸôÞõú7žþõûøúöšútw¸øúû™ûs\õŸø¶xþXóžùôw·Û[÷µøsŸþxÚ÷µõ\úõ™úrÝöŸy¾ûó]úöܾóÜwßûµßû³ß<¶öxø×wòôsݼ¼~zÜösžþôÝüõóžüöyøÙù´õôßø¸ûøÝu¼y´¶õØyÝz²þö³Û]õõÝ›ðþø»\µÝxóõµÛøÝ9´rüûµùÙ|û|²Ý÷õ÷ùÛûôÛ¸ñ»ürÝ߸õ·vôÛû¸xÜ÷°Ü{³û{ÚõŸy¹÷qÛú¶÷ûß[÷µôüõúüÚÛ¶õ»þsž×vµøßr·üZùºûßt´wzöÙøòútöúxºùÙù¶vþøòØúœö»wõwšûøøuòú÷û¼Ýy›øût±õÜ_´Ùy°þøõw™þôÜx¹Ýt»õõuØ÷÷wõŸ÷ûû˜wü{²Ý÷·Ú{uŸûòüþ·xø×ù²þxóŸßûõôÜþù²Ú÷¾ûóZø·ž|öõ¹yÜü·ùsœ÷[yµõüüòÙùy´÷ñ÷w–ßùw¶ôóü~•þùz²þu»ÞûÙw¶öøz÷—þõÝzóôصÜx¾r¾zŸ|ßüø\±ùÛ÷uö÷›úÝ|óß·´uÚûóû¶Úuž|µtxûþ÷uÜþ³Ûüõu·úwŸùÚøñ|ö¶Þ׸ûwñü¸øÚûÞuòµ¸úÛüÞw²öøºÜ_ö÷·úöÞX¸ôø¶övØþµÙø¼uóØõžúøu¾ûôûû¼÷|ùÛõ¾ûòÛ÷·x™ùð™öþ5¸ûvü›Úsüøòu»•zøwŸwñÚùßu¾ÝûöòXöžûµùõ{v™ûõºö^ÜöõÜžôr™øûz÷Ü÷·üúšvœvßô¼y»üõxÜúô¹öÝyöù·öܘötøõ¶ø×ùóùw·Ûü²]ûóÛ{Ÿõ¾x³öØ{ßù¾õñúxÙyµÜø²÷û;ûþ¼ù¸ö\|òÜú¼Ûòüxôv¸—x²uÛ÷süù÷ÚZóñžùõ»þ{úw˜ûõº÷Z{·úÜû´Úwôu›û÷¼ûyyùœøó¹õYxœx±õô{Ýü×õ±ôÜwž^ôô²Ü]žwóßúþ÷žÚqõõžùø˜Ür´õvüŸüÙúñ^sŸ¹ùùÜxµöÜx·ùÛ÷µxÝ÷¾]ðÛõ¾{ôø·{ØûþÙ²÷\·õÚ|õõŸÛõ´ßvóÝšöþùòÝùø›÷»÷ôÞx¸øööüÚõ¾wóœ÷ÚzŸv±õöùØ_¾úñ´õyØ|žø»w°]xœyüy¯_zž{öö¾yw¹ÙxüûñtŸ×øóvõߺøü÷w÷šþ÷ùùõßû´Üù³ÜywŸûùúºXôòõù¸ú|˜xº÷ñ÷yþ¼þü÷ÝØöðöþzÙòò÷ôœþüúݲòÝßõtÙµ¹ùóÜ÷õx¼÷›ø¾y²]÷óÚüõ÷Ýù³þ÷Û{¸ø´þ]¸ôÜx³÷ùø›_òsû÷¹yÙüµuþ÷µy˜{þy³ôßüøzÚø¯ÝߟwuŸõñÚøwßu¹ùôûõüx™ù¾y±÷uü|þúºöÛ÷úy²øÛ™ôrÝò»~·Õy¶õßxòÙú¶÷ûzÛüµ›tøÜ´ßuœxòxûõùÝÛsõõŸüö˜xôµôÞûøWv»´óøÚ^žx¸óñøúßþþ÷¾ÜõõwœÛö·ùôÝù¹xܸð\µüÛùõµtŸ÷Ý{µußùó™ùõuÝÛóŸœò~wùßþ±Ýtü|µÛ÷¾yôtŸøþþñÝuø¾z²vöúüüÙúðÞõ´ôÙxžöüõ¹úüVö³ó÷·Ù\œùñ{öµü»Ø{Ÿuù÷±Û~õÛüð¶÷Üüøüø÷´›ù~y´õ·øØûÝw»w»þó×üôw¸÷ß¾øxyñܺµxxÙøòø¶÷x×{³÷›xñšz|ö¶zùûüû[¹ñÝùôµyÙùôøõŸøŸyóßùò›üßu¼Üó²Ú_¼{´õóû|Õß³þ÷²^^ºúûöü|õ›ûôõÜùòÚ»µöx›wõšúûw÷ú¼û×{¼u¹Ýõõw™øÜy´òþ|·ß|™øñßwóøøÛûüu°Üø¸ùØ7¹yòܼ÷wÛØôtŸÜðß{¼xŸûð÷tÜþ¹üøôõuÙ¼¾U÷°ó~Ùûwššóoþüwןvøõðw›Þùû÷õöøšþøøþø´yœú¾~³÷ÛXöõøµôÚûö{øžøö»÷ßÛw¶÷÷µÜ^¶Úx´òÞx¾ûÜy²øßwõ¼ùöÛ8¾yò·÷ûX|œx¹õñ|yü»üüõÚyñø¸øôØy¾wµöÜ|ÛùºööØòvûºøÜú¾÷õw›þò–÷ü8ó»ôØÙööw¶öûÞüùÜù±Ûö¾x¾Öðßx¯Ýyœ÷ºzÝyõû›øøÙspŸú¾øûùuùܹµwÜÜ´õ{¶÷x×÷ü{²Ü÷øû¹Øuõ÷›õŸûþx°ùu׸ôÚöñ||¸¸ØüñÜ÷õö™|õ¼öÚuœz³÷öØûºzÞöóyšøŸüôuõú¸þû˜{òÜy²Ûþ¶øxyŸüõÜø³uøÙùµÙt¹sß{µÚüôþùòÝü¹øøø¾úòÚù¶øöüµþ¹Ø6žv¼wï—x\ùôõòºû_þüù\¶±xyüøÖ÷»õûx°×yôœøuôùû¹ùüvÙv¾ø´øÞ|ñ˜öôt–w»yóØöº{ûþ÷–öõ6™wßß³ÜxöwûœúÙürÞö¼ùøØt¼ùòÜùœyµ÷÷ùùÕ{õôö¾þ»xÙw¹÷óø—|ûÙôî¾ÝøûÝÚö°ø÷üü–þòö¸ùúþœù›õûuµxšüõûÜ÷ñÚû´Ýü´ßwõõw¹üû¹×w÷x²Üù»ûxÜ÷òÛ¹³vÖ{ºxž÷ó_ûûúüù›ùutŸ÷¼üõÜyõܶž4žxðõ÷˜_wùõß÷´ø\Û÷ò_¶µxØ~þØóñó™ß\Øõ¯úù¹|×ú»vú÷¶_›þuÞõòݾø^œõzõ´ùÞ×ô_rºõ¾ßß{²öþûõØû·ôÞy¼ùüû¹þóÙøõø´Ûõ·øßûóÚ·óÜûžûóß¶óžü_Ú÷²öÛ¶Ý:ŸõöùóøÝ]¶þÜtº÷þ{¾ÝòÝøt÷¶÷|øxûðÝ÷¾9¾]ðvþúò÷ß—yq¼xóöø~ù»ù\øóø¸ùøúwÝ|²šúõu¶zøüºùùô[w²œyõ›vÞt¼wùÖôó÷÷Úû¹ûôßt¾ûôœùu¾Ùu´õÝxµùÞÚr¼÷òy¾ßÙüñ~òù»ü]Úñ»÷ðÛx›øò¼ôö|þ|¼¹ÚøsõwŸÝò]õþy·ûø™ùsy·Ýøùùœuúz²øùúÛÙôð¾ôûÞöšøóõô›ù¹û÷óÛùx¾ø¯x»þüYÙñ´vŸ÷ù\x÷üøñ]ûõ›û»xðÝu{¶÷øyù¹Øy¹Ûïyù·x¾zù÷Yù´ôÝørû¼ùöØüñÚõ»x´ø¶×øòœsü»õyûüyøœùñÚsßù·ùõ×w¼ùóÜû¸\›ññv¸ü~Õü²þwµû\þøœøð]ùôü´Ýyôþú´ÚöuÞu¼úõz·üúüøøv˜ø´s›|ôùõÖt¾ö¼ùòÞyØv±Üøµøü8Ýùµõþ™uuùwØõ¾uüvÝþô÷›ùñZö´ôÛÜþõòµøû–üõôþøðY¼÷›ø´ô÷sipp-3.6.1/docs/3pcc-B.xml0000664000175000017500000000727613730472040014551 0ustar walterwalter Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.6.1/docs/3pcc-A.xml0000664000175000017500000000731413730472040014541 0ustar walterwalter Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.6.1/docs/.gitignore0000664000175000017500000000004313730472040014771 0ustar walterwalter# sphinx build folder _build *.pyc sipp-3.6.1/docs/regexp.xml0000664000175000017500000001473513730472040015032 0ustar walterwalter ;tag=[pid]SIPpTag02[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> ;tag=[pid]SIPpTag02[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK retrievedIp: [$1] retrievedContact:[$6] retrievedSdpOrigin:[$3] retrievedSdpOrigin-username:[$4] retrievedSdpOrigin-session-id:[$5] retrievedSdpOrigin-version:[$8] Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag02[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.6.1/docs/int_scenarios.rst0000664000175000017500000002150313730472040016377 0ustar walterwalterIntegrated scenarios ==================== Integrated scenarios? Yes, there are scenarios that are embedded in SIPp executable. While you can create your own custom SIP scenarios (see how to create your own XML scenarios), a few basic (yet useful) scenarios are available in SIPp executable. UAC ``` Scenario file: :download:`uac.xml ` :: SIPp UAC Remote |(1) INVITE | |------------------>| |(2) 100 (optional) | |<------------------| |(3) 180 (optional) | |<------------------| |(4) 200 | |<------------------| |(5) ACK | |------------------>| | | |(6) PAUSE | | | |(7) BYE | |------------------>| |(8) 200 | |<------------------| UAC with media `````````````` Scenario file: :download:`uac_pcap.xml ` :: SIPp UAC Remote |(1) INVITE | |------------------>| |(2) 100 (optional) | |<------------------| |(3) 180 (optional) | |<------------------| |(4) 200 | |<------------------| |(5) ACK | |------------------>| | | |(6) RTP send (8s) | |==================>| | | |(7) RFC2833 DIGIT 1| |==================>| | | |(8) BYE | |------------------>| |(9) 200 | |<------------------| UAS ``` Scenario file: :download:`uas.xml ` :: Remote SIPp UAS |(1) INVITE | |------------------>| |(2) 180 | |<------------------| |(3) 200 | |<------------------| |(4) ACK | |------------------>| | | |(5) PAUSE | | | |(6) BYE | |------------------>| |(7) 200 | |<------------------| regexp `````` Scenario file: :download:`regexp.xml ` This scenario, which behaves as an UAC is explained in greater details in this section. :: SIPp regexp Remote |(1) INVITE | |------------------>| |(2) 100 (optional) | |<------------------| |(3) 180 (optional) | |<------------------| |(4) 200 | |<------------------| |(5) ACK | |------------------>| | | |(6) PAUSE | | | |(7) BYE | |------------------>| |(8) 200 | |<------------------| branch `````` Scenario files: :download:`branchc.xml ` and :download:`branchs.xml ` Those scenarios, which work against each other (branchc for client side and branchs for server side) are explained in greater details in this section. :: REGISTER ----------> 200 <---------- 200 <---------- INVITE ----------> 100 <---------- 180 <---------- 403 <---------- 200 <---------- ACK ----------> [ 5000 ms] BYE ----------> 200 <---------- UAC Out-of-call Messages ```````````````````````` Scenario file: :download:`ooc_default.xml ` When a SIPp UAC receives an out-of-call request, it instantiates an out-of-call scenario. By default this scenario simply replies with a 200 OK response. This scenario can be overridden by passing the -oocsf or -oocsn command line options. :: SIPp UAC Remote |(1) .* | |<------------------| |(2) 200 | |------------------>| 3PCC ```` 3PCC stands for 3rd Party Call Control. 3PCC is described in :RFC:`3725`. While this feature was first developed to allow 3PCC like scenarios, it can also be used for every case where you would need one SIPp to talk to several remotes. In order to keep SIPp simple (remember, it's a test tool!), one SIPp instance can only talk to one remote. Which is an issue in 3PCC call flows, like call flow I (SIPp being a controller):: A Controller B |(1) INVITE no SDP | | |<------------------| | |(2) 200 offer1 | | |------------------>| | | |(3) INVITE offer1 | | |------------------>| | |(4) 200 OK answer1 | | |<------------------| | |(5) ACK | | |------------------>| |(6) ACK answer1 | | |<------------------| | |(7) RTP | | |.......................................| Scenario file: :download:`3pcc-A.xml <3pcc-A.xml>` Scenario file: :download:`3pcc-B.xml <3pcc-B.xml>` Scenario file: :download:`3pcc-C-A.xml <3pcc-C-A.xml>` Scenario file: :download:`3pcc-C-B.xml <3pcc-C-B.xml>` The 3PCC feature in SIPp allows to have two SIPp instances launched and synchronised together. If we take the example of call flow I, one SIPp instance will take care of the dialog with remote A (this instance is called 3PCC-C-A for 3PCC-Controller-A-Side) and another SIPp instance will take care of the dialog with remote B (this instance is called 3PCC-C-B for 3PCC-Controller-B-Side). The 3PCC call flow I will, in reality, look like this (Controller has been divided in two SIPp instances):: A Controller A Controller B B |(1) INVITE no SDP | | | |<------------------| | | |(2) 200 offer1 | | | |------------------>| | | | sendCmd (offer1) | | | |----------------->| | | | recvCmd | | | |(3) INVITE offer1 | | | |------------------>| | | |(4) 200 OK answer1 | | | |<------------------| | | sendCmd | | | (answer1) | | | |<-----------------| | | recvCmd |(5) ACK | | | |------------------>| |(6) ACK answer1 | | | |<------------------| | | |(7) RTP | | | |..........................................................| As you can see, we need to pass information between both sides of the controller. SDP "offer1" is provided by A in message (2) and needs to be sent to B side in message (3). This mechanism is implemented in the scenarios through the command. This:: Will send a "command" to the twin SIPp instance. Note that including the Call-ID is mandatory in order to correlate the commands to actual calls. In the same manner, this:: Will receive a "command" from the twin SIPp instance. Using the regular expression mechanism, the content is retrieved and stored in a call variable ($2 in this case), ready to be reinjected:: ;tag=[call_number] To: sut [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test [$2] ]]> In other words, sendCmd and recvCmd can be seen as synchronization points between two SIPp instances, with the ability to pass parameters between each other. Another scenario that has been reported to be do-able with the 3PCC feature is the following: + A calls B. B answers. B and A converse + B calls C. C answers. C and B converse + B "REFER"s A to C and asks to replace A-B call with B-C call. + A accepts. A and C talk. B drops out of the calls. sipp-3.6.1/docs/sipp-05.jpg0000664000175000017500000011347313730472040014714 0ustar walterwalterÿØÿàJFIFHHÿÛC  !"$"$ÿÛCÿÀmK"ÿÄ ÿÄO!"1A”Ñ#25QUs378BRTaqu‘“²³´$dÒ4±%&rCÁ6DEF„ÃÿÄÿÄ= !1S‘4AQaq¡²5Rr±Á3"2ðBÑ#Cb’ñÿÚ ?ªë¾Ñnzî÷"[?á!ìÁ„àÚÛmÁ Sy).aG*É=H-™(i!J‹ ¾:ãl¡ @$«’BzdåCõš­ÛnÉŽ%E¶.($d”äãøUÆÎÕ€Ânr[¹wFÃI„Ó;·ÃÉ”—BKˆ RŸÊ Ü”W°e¼¡a›· œÍQ„SM4ã3ŒÄvaç:ôëp7{•¦Q½ÍUÏöÄÿtÌàדuCD@fÜûrÚuÖ]a¶A ©)PÊsƒ•ŠÄ»¿_ú8~Êߺ²][·H¹ZdB‘n…b¦”‡<Ë>u­ejXIê®>ž§7ýÙ½–ðÎŒ‡9ûˆŸ«s[RyjˆNæŠ^Ü®§ÎÞǤdì\²¤ÙÝ8ûíO÷LF4á8Dp±ÃÂ'v†ÆN³®óÅ]*Š£Ÿòǯ ~8os¯ÿgÙ[÷SÅÿÙÃöVýÕm»iK$,Ù·ê[•Êf“¨„˜A·g™jÃn0R [JBåèH;¢­öþÆ-˰[çÜ$À¸;x»´vžx×áýôíQm°…mIN2Fôê)RkbÓ.\lèŠëœ"qêׇ_n^8c†0ÇNG¼UTѦ0ëír?ÿgÙ[÷SÅÿÙÃöVýÕÔÆšÒ×þÌ4½é›d›Txún÷{} ºÓ’dˆÒ£a·âFâ[[‰IÛä*OE©ëwdzv»‰=ÊâCZû3Åö#>l3ZZPëj@)I;’½Ê†Î€kNr\©¦®aTp´L}3‡V:ÿlü…x™Œ4Äá§ßúpïÿgÙ[÷SÅÿÙÃöVýÕ~:M«O‚Ò®èœßgíjç%9)µ¶Vz©€Èi'%X_'L)Åa»hø Y¯(Tb³i•>#SŠœ2æ9™.—v |+0å „l!¼:éK©­š2åʨÇõÕÛáá§¹Š¬x¦pÃׯ³ýÑÞ£ø¿û8~Êߺž/þβ·î®ÍfìãG±®ìaK½ÝîN¢Ö옭:‰ôÚ‘!rB^Ž[$<òóÔä’1ö-¡ôÃ’´ ùQ[ÔPï-8ÔÇdLkŠ4Þìë¦:â)‚W„§!\ƒ%;ö´+ZÓ8îtÑUQLÎެ>­tGøÌk×£ ”ä;y˜‰˜Ó=¾}\wÅÿÙÃöVýÔñöpý•¿u\´®ƒvìÚáªf÷›r×j¸^-(3Rúݵ½+@a)'ã6•ò î †ÊA5sÒðÖˆŠ„ã¶b¨W³PHn)œû‰un.18@Ž”´H[ŠN}$¤ <©t´ªi‰Õ8N^3éÝ18àÕ¯&ÛQ3ôÆ~Ÿ‹ÿ³‡ì­û©âÿìáû+~êèÚ[Nif4lhwËÅ©„:¯ÂÅ×.Žå [BZ€!+u!•=æ@JÁ#¥OÙ;)·È“O^‘*ÚU«¶ºÌiQd¸¥ÊJûÉŒ—â”*BV2ÝkJÛ8.vSW 4G^&# f;pìß®6yÞ¸§ sÕŽ˜×¯sx¿û8~Êߺž/þβ·î®‰eÐÚNá£áë·zf/u½ pœÒœJá:ÚvóðµittâÊH=Uèªö¸Ñ~t··aq”̽;úóE<ê†Û¡AakBR”©ÎP… YÛ»åk¥â׊§Dé1×ã:'»½¯m’í¬løÊ£GWo¬+ž/þβ·î§‹ÿ³‡ì­û«²èXºjó«4^œ¿ií>»Ì™‹¸ª%¶6Ö¡[¦’­Ä¸ë޶‡°âœQJ•»hVÓ%§ì–#[¯®X,ë6ˤUµ2¥ÎœY”¤²G ÐÚFR‘Œ’œI‹·Îk ¦šì§,4Õ4ÆÙ¢tîŽe--c…Mq‡Ÿf?ì8G‹ÿ³‡ì­û©âÿìáû+~êêš{CØ‘Ú"-NL‡þ‹L7nŒà˜îYyÔ8½ÝJ•‡ÂQŹG+Aû±iñó9.Z5ŒÜ¬Q[`¸ã jUÁ-<„¦KI *@Æð“€¯*&¶kÎ ¬Y×iM3<WÕ8yaŽœ|±a§"ÛMtÑTáŽ=sÕÿþ¹‹ÿ³‡ì­û©âÿìáû+~ê躉¸Ú_Hêâí¶Ò{íîvŸÒñ¶Ç[¡¹å—ûœM€„¬8  )>Œnv?`±5÷)J´ßßs±ÄCë€êÙCrn iôIi ©HÞpåP$ÕÕe»*nõÞ8Ûøá» c<1ÁHÈõÍ­6\-3–þpÑúrïÿgÙ[÷W÷Æèpý•¿uukŒ;LËN­ÓÝ>,_í÷¹âéÜb¾©­±=æÒÐæin–‘´<—PR½Ã Ú+Ž˜ËüÓü+w'_i¿p°£ƒ‡odêŸ6­òåý'±Ç³¶5Ç“sÆèpý•¿u<`þ‡Ù[÷VŸv_æ«øS»/óUü*OŠîicOksÆèpý•¿u<`þ‡Ù[÷VŸv_æ«øS»/óUü)ÅwÓÚÜñƒú?eoÝO?¡ÃöVýÕ§Ý—ùªþîËüÕ q]Æ4ö·<`þ‡Ù[÷SÆèpý•¿ui÷eþj¿…;²ÿ5_œWq=­Ï?¡ÃöVýÔñƒú?eoÝZ}Ùš¯áNì¿ÍWð§ÜcOksÆèpý•¿u<`þ‡Ù[÷VŸv_æ«øS»/óUü)ÅwÓÚÜñƒú?eoÝO?¡ÃöVýÕ§Ý—ùªþîËüÕ q]Æ4ö·<`þ‡Ù[÷SÆèpý•¿ui÷eþj¿…;²ÿ5_œWq=­Ï?¡ÃöVýÔñƒú?eoÝZ}Ùš¯áNì¿ÍWð§ÜcOksÆèpý•¿u<`þ‡Ù[÷VŸv_æ«øS»/óUü)ÅwÓÚÜñƒú?eoÝ_Ÿ?¢DöVÿãZ½Ùš¯á_Îî¿Í4â{Œiímø¹ý/²·ÿx¹ý/²·ÿÔà_æÓ›N'¸ÆžÖß‹ŸÑ"û+ñ§‹ŸÑ"û+ñ­Nþm8ù´â{Œiímø¹ý/²·ÿx¹ý/²·ÿÔà_æÓ›N'¸ÆžÖß‹ŸÑ"û+ñ§‹ŸÑ"û+ñ­Nþm8ù´â{Œiímø¹ý/²·ÿx¹ý/²·ÿÔà_æÓ›N'¸ÆžÖß‹ŸÑ"û+ñ§‹ŸÑ"û+ñ­Nþm8ù´â{Œiímø¹ý/²·ÿx¹ý/²·ÿÔà_æÓ›N'¸ÆžÖß‹ŸÑ"û+ñ§‹ŸÑ"û+ñ­Nþm8ù´â{Œiímø¹ý/²·ÿx¹ý/²·ÿÔà_æÓ›N'¸ÆžÖëwGq-·2Ö¢”¦#d’}yjؽ!«H/ÛlÕëC÷ {KOêRTà)?¨€EDö\ʇišXãÑy‰ýäWPrÕªïz­G؇-F[¦Ay±äopYÚU»¦Õz½U”ouÝ­i¢81†š»g"4ÇbJås³·¢ª§óýž×/½A½Z •l·8‰ -´äCSjXüÍ$+¨ò“ž£§Z–^‘Õ sÚôüuJ$\-í-?©IS€¤þ¢+Ù›‰‘qˆ¤à©:¾ÂN=_õ~êèïwÅh› ‹&…·ßÐü5-ÇÞ´®IÞuw'õ!===kB÷”ï65p)¦œbp™¦&c¯F®Ùŵa“®ö‘5MS†ÿºPA¾ÙÄuK¶[\D…–Úr'u”…,cɹ¢¤…uRsÔt©W4>¸ë5‰‚GT?:ÞÒÓú”•8 Oê WhíGGiw(ÑZm¦£v‹iBm!)m!ÙÀ@0£èÕ40³Ýô–Uö%±ºòÑi¿³¿¨ 'ÊÉÏ\ u|ö_ÎÛÞN›*,¨§¢g‰Ãþ=“£ü»gC‘3jé}¢ÒÒÒª°¦b4Lc§…ÛöùFž§Ê×öõnŽ~5Ém7oqEI:×-•lVÜ)!èê;U´õNAÁô`×eÑßuÖš‡¦aEÖVËô«ëH)–ül\!Gj†çÉNÒ®€nÝ€Q;NKÎéRÔ¨ŒD”Þ¢„ŽÈ¶WÇ<) ÀÁ=¯ž.l9ÐGPGý«5κ3Ÿ$Ù_/4DUŒÆ¦c¯NÝ­‹ÝÂŒ‰”m.–5LÓ§¾"|:õ®p.V¨6è­^ZC°ßNerx9Û辤t$duýžš³\;GÒ.K‘l–Tûj.v[q×3¹%N4Ú€°•s’‘œú+”v‘ó>žû'¿©5I¨üëÊ‘ÊUÙWDÏ"4U1£ uG‹KºLXMTU‡ g=x~Îߪ¬Ñ­ñ£=y!ÆšJêÞNç1ê}>šÝV¿Ž-oÚXÕ2£Û¤‡âGº-–^ HJ‚Ð…€¬¤rN•óý*“ŸµQuXS1SŒêñkÓ›6TWæҨží‡{‘®aH²¢Æî¤|Ùи\Ô"`/x½‡ÍæêŸMl=Ú(yPKÚ¾sˆ·¸‡a4»»…¨ËBJP¦ÐWµ)$ÆzWÏ”«yéV8ÿMF¼|û|Wózœ0ãªÕ†¾®ÏÐ{BïÁÁ;Z]eòÆr"ûÅõç2Ë…%ÆüÎ*¶#pô£9¬‡´ufaÖw.ðdª_(½;»™Lð—3Éò¸¼€úBz ùî•dgŽ„]hܾrλz÷»×Ãh<25,Û–ô[/ í††ô}Q%‡ ¶ëPË75#»%Ó—8¶¬q•zÊpO®·´jmé‹Ük¼ Û2^-ùÉjlպ˒žim-÷SÈ Ž­Cq;¿^+çU–¹å6ÔÍ5ݨœtOžµöy,¦&‹j£÷Ñ–.Õ¥Ù߸ÜZÕfmê}v•\çÏq÷ØiD(©¥B ¬©88ç£W­ ­ë“îjY.Hº3Á>Bî‹SÒšÚÆã…{”©i8ÀÆ+‚Ò¬£;¢Î©ª›µÌaä¾¼‰5ÄEVÕkÇ__û«³©ô<®Ó—!—æk{¬¥°øÈ~öë‰mÑœ-)S„%C'žª×‹ÚQnRlæ®S”Éœ‹»‚K©HH SÛ÷R1œtÀ)Y#<æ#»Q‡‡n½ìs"g¶«f­Îë VÙ Ûß·À¼"iÕôG›ÅÊʾSj)P*J±Ô‡×šÚ·ëè¶×í¯R9nuÄq©ØWÃ…9nä(d™õW¥dœùµ˜˜› pß q›Vq1×uN­³&,(ž0…E€Úڈ¦îi„­AKØ‚­©*P2HëšÚ·ëè¶×í¯R9nuÄq©ØWÃ…9nä(d™õW¥9ókÀš8Špž®¢sjÎkŠæÒ¬c¯­Ý‘«í-Ø>·|Svr°âà"àSÕ’¶Â¶¬å 9P=@¯áÕ¶?¬b9>úáTªÑŸvÖxÍÆ>*W›6V˜pí*œ;]×ámëŸÎO¾Ÿ lXÄþr}õ©Y> ^¶TԻýrî¿ lXÄþr}ôø[cúÆ'ó“ï®J|@½l©ß'5.ÿ\»¯ÂÛÖ1?œŸ}>Øþ±‰üäûë…RŸ/[*wÉÍK¿×.ëð¶ÇõŒOç'ßO…¶?¬b9>úáT§Ä ÖÊòsRïõ˺ü-±ýcùÉ÷ÓámëŸÎO¾¸U)ñõ²§|œÔ»ýrî¿ lXÄþr}ôø[cúÆ'ó“ï®J|@½l©ß'5.ÿ\»¯ÂÛÖ1?œŸ}>Øþ±‰üäûë…RŸ/[*wÉÍK¿×.ëð¶ÇõŒOç'ßO…¶?¬b9>úáT§Ä ÖÊòsRïõ˺ü-±ýcùÉ÷ÓámëŸÎO¾¸U)ñõ²§|œÔ»ýrî¿ lXÄþr}ôø[cúÆ'ó“ï®J|@½l©ß'5.ÿ\»¯ÂÛÖ1?œŸ}>Ù>°‹üä{ë…ÒŸ/[*wÉÍK¿×.éðºÉõ„_ç#ßO…ÖO¬"ÿ9úát§Ä ÖÊòsVíõKº|.²}aùÈ÷Óáu“ë¿ÎG¾¸])ñõ²§|œÕ»}RîŸ ¬ŸXEþr=ôø]dúÂ/ó‘ï®J|@½l©ß'5nßT»§Âë'Öœ}>Y>°‹üä{ë…ÒŸ/[*wÉÍ[·Õ.éðºÉõ„_ç#ßO…ÖO¬"ÿ9úát§Ä ÖÊòsVíõKº|.²}aùÈ÷Óáu“ë¿ÎG¾¸])ñõ²§|œÕ»}RîŸ ¬ŸXEþr=ôø]dúÂ/ó‘ï®J|@½l©ß'5nßT»§Âë'Öœ}>Y>°‹üä{ë…ÒŸ/[*wÉÍ[·Õ.éðºÉõ„_ç#ßO…ÖO¬"ÿ9úát§Ä ÖÊòsVíõK¾ÚµÝ²Ùt‰r‰rˆ™Cí(º‚Р qŸ¤ •:ã³En=ÒðÆó’Ü}C-¤ŸH@Tu('èJ?¬úkæÚV½¶zÚÛN5XÆ=ÕU‰†ÅŽoYØÄÅÎÝù‡ÒQ{JÓ6d²2¾æ=›‚Ü›pjK«yÜ]R†Òëè“¸äœ ®;5QQ.ìïê[cQGKi'ÒJ úRŽ=gÓ_7R±NxU?úcÿµxÏŒãŒùêdŒ‡Lcÿ’wGã O¦lÎ ·(VŽÏ­Óœ»ƒW´©HŸ)÷£¥e½¡¶Ñå@S‡9êI$‹™·\•¸ÿá/i oê¦ØuIm'èHT5('èJ?¬úkŠýÅ¿ŒÖÿß/üG«íMs­mº}¸Èz:æÍ};û» p4:­Óô£×ÒG¨­yÊW\±„^î´ÕÁÕ35c§^œbgW[v缕37[z©áaŽhÕ£ :ß<ê»v§F‘g:¶ÕlåLÙ².Üyd´…„’°ËiB—>Ê$“Ž w9¹¤dŒ£¨¯®Ÿ8?ÿ¼×[‘ml¿ þžÆÊ,袭õÆ3®fzÚš­m¯soo\×]Q¦g»CõÚGÌú{ì^þ¤Õ&®Ý¤|ϧ¾ÉïêMRkŒÎßœ[y{a¿’º->5{¤¥)\â@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ~ã}ÒÚIjΩdàgÿѿ꯷Új‰tzã¾Ì‹ƒ­%—_Y ZSèUû?YÂsòF>ûŒÿM'ûf†ýzUÕYì/3a3„c‹¥—Öãz†ÕnÙ¦®‹hM¹¶×káj+í¯ÍÑjQ' #®ò¤z…|sùÁÿ´5éÏk?‚­UûžWö•^c\þp‘ö‡þõßæÍ¼Û]m*ÃîÂ&ógÅÛDw~ß®Ò>gÓßd÷õ&©5uíæ}=öOPªUs™Ûó‹o/l6òWE§Æ¯t”¥+œH¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(;ÜgøÊi?Û3ü7ëÒ¨þªóWî3üe4Ÿí™þõéTU[:ÕAv³ø*Õ_¹åiUæ5Ïç hï^œö³ø*Õ_¹åiUæ5Ïç hkÐsG¡Ú}ÑøCßžÐ× æC´û£ð‡¾ÿgÓßd÷õ&©5uíæ}=öOPªUAçoÎ-¼½°Ï’º->5{¤¥)\â@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>ã?ÆSIþÙŸá¿^•GõWš¿qŸã)¤ÿlÏð߯J£úªÙÖª µŸÁVªýÏ+ûJ¯1®8HûCÿzô絟ÁVªýÏ+ûJ¯1®8HûC^ƒš=ÓîÂûüñáûmëHÌÌ‹¦£H¸F·4¶ŸÝ&J\ShÁ¨m W\c¢OR3“U϶x§søccààåïœ38wnÇ87îÇ\íÛÊÏJší#æ}=öORj“PyÛó‹o/l3䮋O^éNG±Û*:õ†˜ÙÇ%Æfän;X*}zS×Ñ‘Ö,vÉ=ã›XØáq>¦‘ÎÌÃÌ‘Œ8Œ+ > ­ªéÕ" é\âA1ÑovÌg9ªmä-BJ/’œá9K%¼«|u#®mö¬Âsz¦Ñ"AB`6Ô ø*ÆS•2ÊrsçÇC‚zf”“ìvÈÝ߇XØæò¾–—ÀÌÁ“œ¸­ì')°ÊëÑ&’,vƧEŽcc}§÷òIm™¸ûFFð¦Žï@Ø•uôàu¨:PNx³Å;ŸÃ/|á™Ã»v8ñÁ¿v:çnÜ~VzR=ŽØìéQ׬ll4ÆÎ9.30·#pÉØÁPÛè;Òž¾ŒŽµJ È;džñͬlp¸ŸSHçfaæHÆNÆ„ŸPVÕtê‘XãZ-îÙŒç5M¢<€…¨@q©EòSœ')d·•`cÏŽ£$uÄ=(&$Ú-íY„æõM¢D‚„(Àm©AðUŒ§*d7”äçÏŽ‡ôÎIö;dnïìlsy_KKàf`áIÎ\Vö”XNåuè“Pt œ‘c¶5:,tkí?¿’KlÌ ÇÚ27…0wzÄ«¯§­<ÙâÏჃ—¾pÌáÝ»xàß»s·n?+=*”‘ìvÇgJ޽cca¦6qÉq™…¹†NÀ– †ßAÞ”õôdu¤ ²OxæÖ68\O©¤s³0ó$c'c ÂO¨+jºuH¨:PLF´[ݳÎj›Dy P€ãR‹ä§8NRÉo*ÀÇŸFHë„›E½«0œÞ©´HP… µ(> ±”åL†òœœùñÐàž™‡¥äû²7wáÖ69¼¯¥¥ð30p¤ç.+{ ÊG¬'rºôI¤‹±©Ñc£XØßiýü’[f`n>Ñ‘¼)€£»Ð6%]}8j”žlñNçðÆÇÁÁËß8fpîÝŽVü#F×cˆ«¦\ÕIbói`-p߆B[冔wò¤¥Ð§ JTÚ†3èV c j ý¾é"ëùs‰p“»žSևܠ¥nX9VT9=HÍe²êMdЍ–]Ew¶ÇZËŠj$×AY’pÏê÷;`gMYe[g_%]çÚÕ9Q|9 gÈóÈtïK»’”%•Ÿ¬„î%!E(ѽZí±ôe–ío’ëë•*KKÑx–‡n2Š­+lrùUµ ê¬ƒå ŒfñwfÌõ•›¬æí’z$,0âÆß2Ò|©êG䢶¤êM*Ì,²uÝë`BÜšâ˜F6§a;p6Œ tÀú(-Q»<€ö£jËð‚J]böÍŠä燤¥™N—R‚ÏÆük[ÙX*W€ÚBI%"³¨l‘ Zá]-×O‰!÷¢©Ã³ñ쥥9°Jš!ämR‚|ÙB1×5N¦C–÷¨®é]µ¸ \* BJZ9òŽƒŠã¨/÷("Æùs™>©‡å­ÆÃª**si$n%k%^’T~“A½¡cÚÞ¸KrìÃN3)q ’‡ÌFÖ\BwH1þ47… ?úŠh)UKÞ´Ý•»Î|…I²Ù"wYLQâW_M'jM:,¸“µÞTy‹K’šzk‹CëHHJ– ˆ@çSô \ffNn<‹„ksKÎé2Râ›F=ChZºãz‘œ š’ð;gŠw?†6>^ùÃ3‡vìqãƒ~ìuÎݸü¬ô¨:PNG±Û*:õ†˜ÙÇ%Æfän;X*}zS×Ñ‘Ö,vÉ=ã›XØáq>¦‘ÎÌÃÌ‘Œ8Œ+ > ­ªéÕ" éA1ÑovÌg9ªmä-BJ/’œá9K%¼«|u#®mö¬Âsz¦Ñ"AB`6Ô ø*ÆS•2ÊrsçÇC‚zf”«E¾T½TÚ.ËRÂK1”•¤`Ç•”'1Ðç¨ééÄ=)Aм*Çÿâàï‚Fðo„>ܹžÛÅÍû&ýØóçv7z¶ùj¡c¶Â¸ów½Al³ñíÛß®\ç;xZsÑœãÒ1ž¸ÙwWjUj9š‰‹Ä˜7Y¹ï2`NgY mJA=:ž§'­FÝ.Ÿt&t·qÈü—Tã‹ÀeJ$œì‚ñÙæ‹µÞº½&ãlž¦ØžÌ6“sj/Æ5n"J’ñC…­Û6€:-N%²•×¢E‚ïg)ʄПï¤J ^òÓ¬É*l§vÌÊH;wu=HÀ–[ÅÞÉ)Rì·YÖÙ AmNĶVPH%$¤ƒŒ€qú…vœ›–$¸Ò`9)2Ö€Â7©Ô¡HI.cy-XNvÄã$š r0b®<™.KVþòÓ‘Ò†ÛÁòlXY+Èêr”àô½5½¢mqïzÎÇe–·Q}ÆF©¾=.þìû—TãÆ±EYNáÐãÒ=4zIÚï—©1 êРBï3f:ËL'<©k ó<„©%N6BœSD‚|¡@%[ÖÎϢɹÞþ¥‚-ÖÕÇlJbDEr-ô)Ä'.Hmœ¥(XXK«Â†¼eB«ð‚ÿã¾=ã—?ý?½¯¼|Ÿ|Îï“åôú:z++:§S3yzô΢»·s€ÛÓ5Àûˆ|ªX;ˆò§¡?’>Š ,m’ÜFªisçÝäYc·'+ImM8—BÀ1ÔA.`,dmmc$PªqÇW#‹f©¾'…õÈkFÇW¿{‰ótR¹ʇS½Yôšˆ—"D¹OK–û²$<µ8뮬©n-G%J'©$œ’h$äÚ-íY„æõM¢D‚„(Àm©AðUŒ§*d7”äçÏŽ‡ôÊõh·ÀŠ—¢j›EÙjXIf#R’´Œ¸ò²„ã¦:õ=8‡¥`ûŒÿM'ûf†ýzUÕ^jýÆŒ¦“ý³?Ã~½*ê«gZ¨.ÖZ«÷<¯í*¼Æ¹üá#íýëÓžÖZ«÷<¯í*¼Æ¹üá#íýëÐsG¡Ú}ÑøCßžžû'¿©5I¨<íùÅ·—¶òWE§Æ¯tº7cÝoUÊóxžÓpB-kŠÍÁ†äHCÎ·Ê N<ÞY-omK'È\JÒ2ÙR2ÛmºRÙ l^¢é©Ïئº«ƒìÝ_а¦Tc¦9mì+.•)(½Ì©I!.Šæ”®q évëf†Ùü›‰n3w5±)çZD¦Ûr+áK-4×4Ä©Mm ñ/(…,•ôDf«‡e…¡lR¡ZlftØHŸfóÎó/\)":^*B”ÒP\+ @* JZP ÑéA×µ6“ÐLA„ï‰Û"µé<ç Ý}Ça¬©/<œHuO)8A,1Ñj%¬y[Ä»’j>›v÷h´ZÍÂã92]·Ýûë¢ÃI"AKm­ÇRàPÉW+H#o&­ëÕâï{”™w«¬ë”„ 6—eÈ[ËTIÆI8ýf‚s´èHöbKHeÈ©[ÈfCN´—7¬y8äHÚ6„’ꕸ¨à$¤ ŽÇç½Ý«”5D› µ£D˜Ûrêd4±ÄÚÞh¸®4¼<ªÈ #©PJ¹í(:õ–Ó ®³¯’nBØ—dÝq1ãMa¶ãCp%l˜ËvLd…yÜRØP¶› ¥uë=»NŽÍdÏ•Ð.E–Ü•4-ÂrRµ)6}IJ£¸7y”°Ú¾.µeÓW›ÄUI·Æih -6’ÛKà–ÙJÔóždù Wd惥ëË6š‡erK6«e©OÚàL‰Áv/¾¹O4ÂÜg€¸¥´ÆÅ¼°\NíɵIEoC³h…j{[ÇÓÞ»»lÇpÞHï¶Ò—9dJ<ÿòRRSñ$©jO±µ<Òõx»Þå&]êë:å! ¥ÙròÂ$$q’N?Y­Œ,:}÷,sLKDt*ã&¨-ê(ë%–’’ÓΩO'%Jn)S(p!\‰QÚf×¢íèr¡¨mÛ‚¢±ãÀìéÊ^Z¦–óÆV¡²PO•±ûš<¾²½C-²ãÌ:Ú$ ¸Ê–‚ˆ Rw$ŸHÜ• ZHõPKëÈ–¸Z®lk7HIã(æH*m*XJ›qÔí * ‹ `)E@šµvYªu%›KjV­:‰«b"Eá²¹¬°\”dFRŠP² Ç……¸PÆSŒÂ¹Í(/íÍËì–RÐ,qe·5—ÒEñ‘)”"Pp¹oãr m(´¬…»·lÚËIéiisí«ÓPn Bmä± P´ö]J"—”­õ•eNÍ'»3·å^CYY!æÞq–qÐyHA!´%;”G nRFO­@zè:iÖ ÀÄ0ãN‘)-°órY+~9BÎ÷›ïN¸;PsÂÀXR)J`û!’¸]¨i¹ižÕ½ ÜZ[ò”˜èC!YwrÔ@ÁFàF|ÙÛƒœ­(:ÈkLÞuTeßï ]L{sÑ„©’å1¹žò{ʀ˘BÄŠKMíZˆ,‚,eQõ"í–DåÛî0DUÜ/­Æ`¥øî©ÖÔ´Ê-­´­¼¡wtÞµíRO&­æovlÏYYºÎnÙ!aÇ¡¢BÃ,mó)í'Êž¤~Hú(:^†Ó=ŸÜ%\‘.ãvÇ.îÄ.\öšÔ@S±ð‡°”¬å‚RGvaÈ9ËDÍ[˜å«OJ½7*\Y&ü…­,å´¶ðaen—T³b7) JÅ ·“iœ«—Ô¶Ò 7)1°ú7¥Õ!KH-çx!XV6¤g Š ~¿ðk…¥‹žÕ¥mq{¬M«‰1Ã-׃C̘åÕ”€¾CȶӸ6"ŠÇ$¼Öl¶½M¡¯7XšhÚãféoxñc“)õ+ %÷\Ú)s(+Q¢“C“¦¯1¬Âììf„}ˆuH[Sí¶¼lql…r!µnF¤„èÁ;Óœ“ô– …ÝÒí¿‘×ßL`Ìw›}æßVv²ãm©JiÓ…ÚÂTJT1”«1:Þ„j{p}•º…ƒmfö¥Ä $ã²;ÉJ ÉÂCÀå±”Ã|•ÚÓhOiº“™ûEÑ.D«2$^Ðäi{¤€ü„¼ O*üî¡EIFs»j©—ËÆÍ©©Œ¶žÜz,¶¤²¢œnO#JRw É%9È I# Œ ê¤š´ÝdƵiéó¸Ân3O߸Pë”Þ9’§#´ò’âT?8ºâ¼Ñõ¥¶ŸVÝm–émLRÓCr|:ÎrÚ··å$¤¤œc €A” R” R” R” R” R²Ä"\¦bDaÙZ[i¦T·£€”Ô’N©RwËÆÍ©©Œ¶žÜz,¶¤²¢œnO#JRw É%9È I# Œ R” R” R” R” R” R” R” R” R” R” R” R” R” ìqŸã)¤ÿlÏð߯J£ú«Í_¸Ïñ”Ò¶gøo×¥QýUlëUÚÏà«U~ç•ý¥W˜×?œ$}¡¯N{Yüj¯Üò¿´ªóçó„´5è9£Ðí>èü!ï¿Ï·ë´™ô÷Ù=ýIªM]»HùŸO}“ßÔš¤ÔvüâÛËÛ ù+¢ÓãWºJR•Î$ R” R²ÄLuJe2Ýu¨åi­¦ÂÖ”g©JIHQÐ úG¦ƒ*Ôî“Uê[\뛨§Vð•)˜¡n¸„HLt”4VI[ˆ$Œ'qÉ •í# §Ë»^$µjܸž‹/>ç{aRÜÒœBSñhVü-[U€7ƒ¸]©­¸¶e\DäȰÝ×vˆ†Cˆ–µ>)j+Idf2|à9÷ÃåòáTº¼DìÿmÓÂ.wnïp‘{~ÅGÌË’šSiQqjR mny¼))Z±¸íTz½¬µæ‘¿éip$\u,ë‚á6ËOÍ·´¬ºÒ"„-J2G™™'#%=õìg*ß^íXA¾Ø-ö¸SîòDe…¼…ÆŽ6£h"?yy°zùxƒ)@Ü• wÝ©,‘W.|»ºÛŠv<¶d !a%*ËJPÆÙ'ÕÌÖqÊÞíkÖ›ºY¢¥ûqh•†Õ7!¥à’—JË‘‚ZFÓÐàô ÑµÎzÛ9¹±Ñn·©“¹ œ‚:¶âT•z}`ààŽ U©žÐgÆÕ¯jx±LùQC‚îÓ°¢ p¤¸—’ö6¡jÜXÈ' Ú”Umvù÷YÍÀµÁ“:[¹ãb3JqÅàp”‚N'öR_&ªéá\,nIKÎÿæÑÐË~llæZÃKV U„-]ô¥A!­ª/2u öEÞZv?#náÎóØÚ‘çykYè‘éQú¼hískµiÍ5it¾)ˆ7Gä\£7 §z› ˆ•©àKKSY[d% .’B‹iÝÏ®^¶Îr…Æ[­ãr£InCg Ž6¥%^ŸQ89¨5/mÑz’ãfw…§¡IZÚei–ÎVò1ñ!%[¹ŽàRÖ7¬uJTh å¦:e<˜Žºìpµ–ëa R3Щ ¨$‘éœ}'ÓS’uL‰šHiÙ‘Záehv2ã(Ç Z@F\iéÙ¸ yI)Ë…)ØkÕ1'N\¡Ù…Òâ–­í:„9©+Øü¤«TÛ_,¶AQ;·a$!éJP)JP*Õm›¦[ìúáh“p»¢ç.S2·6¦¶Q! G!|+ ç«g—a^šªÔÃ6ˆîèÉwäÌtH‹qb"ã‡[uiXsvr *;}`äõ'.ZšÐýšs¬‰Æçq´C´½l 0Â#÷oK¡eK*î©òÓŽCæ;<ÒRuÝ¢5ýwëtiÒ¤NÔ±µ ¸Ï¡ ¢:Ù[ËàCK._Pä)F6´îÂ`çé8ñ¬Ò]EÍ×.p­Ñ®r㘡,ò8vlwyRœ圤¶<øQÚ7eVƒžŸƒÑLØÈ¹Þ®Ž[aC¹:ž %åp¯õr@%ÁóîBCGQÜ-n°Ù^.Ž¿®Ê—îïHin%jkâ²”¸ÉåYqµ« Â}Ó“Š­”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥`ûŒÿM'ûf†ýzUÕ^jýÆŒ¦“ý³?Ã~½*ê«gZ¨.ÖZ«÷<¯í*¼Æ¹üá#í zsÚÏà«U~ç•ý¥W˜×?œ$}¡¯A͇i÷Gá}þxðý¿]¤|ϧ¾ÉïêMRjíÚGÌú{ìžþ¤Õ& ó·çÞ^ØgÉ]Ÿ½ÒR”®q R” VÕ®K0ç7"E¾5Ťgti*q-¯ Ž¥µ¡]3žŠ@ÎFEjÖXŽ¡™L¼äv¤¡µ¥JeÒ ‡9)VÒƒè8 ýPYnÕ麖ãzrÃh@º¡i¹CA‘Á,©ÐñR‰t¸“È”+È´ ÁP8ư’䉦ái¶\aJîÿè_æK-wvËLmSn%Ï#jR圃•nV%çY¬Öím¯@¶5&ž\…C„ó®q( ÍGJV¤¨8BPé# rS’FA—Òš"Átí"#•»kžò­†âˆî:f2Û«CjuAJi½êI[¤q¤uQq!U…®îìÊrt¨ÐnüEˤiO¡i\IŽ•¼„¶¤ ’[líZV°a U®…£tý¥^én9ûæ¡vÌóâR•ÝPŽì9#©¥„)Y£¹\ˆ;Œîç´Û¯i²® 6Uém<Ìwâ÷]Íw½è@Dt¤¤©ò28ØÈ?{ëÊÝ«5”ÝCkƒkvh‘ à0ÛoÉ` Úƒ!× iò[) ܵeõGgÑí¶¯VÛÛ³cµ™[dC ,¡hŠ¥ %ÅŒ>.:õøßFÄòFj/n´X"Ü Þº—V„8¶Z`ÇFä ­©Äƒµ.´Ò”²R’…$V§>ê‘tñfõ Í«‘cÉ­HSrovì8âHSž¡•p”BRm®33'7EÂ5¹¥çt™)qM£ž¡´-]qމ=HÎM[¾ZUª|!ˤ˜‘ ¹î»l³¿lªRÚ2Q„¼áÜÚ½d¶¥Â}Ös“Ζî9’êœqx ©D“€ý€U‚Á­^³E±2͆Ðúì—Üc¼ñ‘½Ç”2½®„8Ú >ôœç+ݪ-Ñ­7Ùø“;ë mÚþY;²OV]u #¢Õèëƒ,:sFÛok,‘¨>ñqv×" îJx¶°¼pò7/ÒNÛƒ$B[¨zSÏ7¨ÈqjRYh¨¡°NBS¸•`zI?I5²ÍÞäÕ™ë2&:mÏ,:¸«;›y~1)9 s Þœ+nSœY—§ 7£½3qñI#`[0ÊR˜AESÁxtàž<¥¾-Î#ä!Y¥)@¥)@« -LÔ]%'N9hyV—\–âäó—PKny^ÊËmÚzn ÅWªÃ,{8¹NT&„ø·x"PZ÷–fISe;¶`RAÛ»©êFÍY2M™È*ƒŠÌ)3Ð瑞>6” Ë`'…ž©BTxÆTr­Òs{LÕs,Öˆ2g:ü‹MÄ\cÏ‘%÷ß/’TqMœ 09{¶n¶k3vk¤­7"Ùa·Ý“<:á}ç$wMí¬øÇz^6¡*ò#*>mÓ— ¦åßàÙcGj8oRųIT7^ï ¡Õ¸•&Y{-™ÐǬ¥Üô-ä9õúún‘bÁlƒjn:ÜX…Õ#•À€· Z×’lcvѰ`TNöÖttÉáÚm“Ÿ{‡ ™ÌxøžCÉÚqï¶£œü€=@µ+6éZZÕ¨!Z£Z“6\7#ÅqÕ2C(Ž´¯ãVµn<êÍŒ%8äœé8úºDèFæì)­!“=Ô8Û«vCqÒ­à ¼ÞHJü»Ž2PoEídKêï0ìÈrU ˆ Å™pckL¤!#-ÉJŽR†ÁÉ ñ¤ã;‰¦K}r¥=%Ä´•ºµ-A¦’Ú''jRR>€Ð[íú2Üæ¡•m¨šˆÊ"Ç—jK ¸óO¶‡PH}æšIZw$:¥$-!KMVí¸Ý%ÂÛ%Ýõµ¶K/ ª# o'bºuNNFM¼³á}ÏÃãsóò÷ÍÎsmÛŽ¡i}»sÎw´¥Å:’Ü“!\&Iâò©ƒÂ—w¥¼…ý}7H±`ǶAµ@Œ·n,Bê‘Êà@[…N­kÉ ¶1»hØ0*&­úö¡»]²éijØ!ù 8å©R{¢‹ii@(óG/˜ãaJ›Ûæ ÅBJRJRJRJRJRJRJRJRJRJRJRJRJRƒ°}ÆŒ¦“ý³?Ã~½*ê¯5~ã?ÆSIþÙŸá¿^•GõU³­Tk?‚­UûžWö•^c\þp‘ö†½9ígðUª¿sÊþÒ«ÌkŸÎ>Ð× æC´û£ð‡¾ÿgÓßd÷õ&©5ví#æ}=öORj“PyÛó‹o/l3䮋O^é)JW8)JP+j×pŸjœÜû\é0eµž7ã:¦ÜFA Id?a5«[Ög=yŠ›d¦¢MJù}ɈŠZ|Á\«RR‚1ÐäuÆ:âƒiíS©ž¼³z{Q]ܹÇAm™‹šá}´ÞT¬Ày•ÐÊ?MjƼ]âÞMê5Ös72µ¸f7!i|­yÜ­àîÉÜrs×'é®tqQ;OÖòm·›c7 §x~Ç>5Ù€”•ÎBò$%{YQa/ )I$(§òÀ;VË´a½* ÛeÉl[Róð¯ŒÛ}ÔFÛ-Bk™mi/õZFKÊ(p(„ æŒj û#Á|¹µâ™ñ ’Ö;Þwg—ã3¹YÝŸ”~“Q•×´åÞ.ÛeæÙÐQ*Mõ¶\Lf[¦8Ò#-AO5µ2pÀä) )ÆV7r ½s­eÅz$½a¨dGy mÖ¹<¤8… ¨`‚5£|ÔûïŽ_.wN Ü=òZÞãÝÛwŒàgœ ézËIéiisí«ÓPn Bmä± P´ö]J"—”­õ•eNÍ'»3·åZ÷hÖí;o°[ÓihbR–¡™¢T‚:åÆe<Ò†HÜ¢ÛGiB1½( +jÖ¸ ÎmwHÒdÄäj4„²âºajBÀëƒòNFGLä[¼GDSÞ$ÄŒõÂÙ;V§‚™s~HqbUòÕ¸¾èÚ¤Ž˜ l(õ1Têh1bDƒ¨®ñcÃZœŠÓ3\BZ‚‚”€HZÁ#ܯ¤Ö-Põ¦EöCÖ68-êÛÂß šÇ”yTë¤y³éq_üzãGYô´Ý9¦“t…cD›…Ñøs¥¹}K.G‡ä"Bš.®§0€Û…Äæ\‰å=.[îÈòÔ㮺²¥¸µ•(ž¤’rI¬U–[ ‹)èÎ)¥-¥© -:—H8;T’R¡ôH>j¹zhèçc@‹Ý.þNuÌlÈSéƒ. %¬« ÚZÜ•Žuq¬Ò” R” TÄmS©¢Ù–6¢»³l([fs\K Îäìn㑎¹?MCÕâ×kÝÝ#¥°4«¤iñá»xއ6¶Ì¤<°Êœ 9hcnå¸ PU^¼]Þ³3ezë9Ëdu—†¹ ,6³»Ì”´2ºùG饯ñw¹E‰ãu2<$qÄiù q #m@$„Œ%#ôUªùj»%°¥û¥±þã6céŒ/Üy–D^0–C…c+’€œ¤î*9« ê[°¯úeKx´^¬.ì.[鏯š‚ฆ#4µ"$€„¡;°’°>-¶Ãš^¯{ܤ˽]g\¤!´».BÞX@$„‚¢N2IÇë5–Ǩ/ö.o¾\í|ûy»œµ³É·;wm#8ÉÆ}5a×òç=`´1¼5v¿·*RÝ.(ž±H`2‚ò±€´È!²Äàoìö?hÓ·™Hº‘6†ã”GKr¦\ÄG*„¸¦‚JVC%åõJÀShÈëµas­S)ÉiÖ…2B[[¢äöõ!%E)'vHj z·¤Õzº¥·Léxú®sr۱Ɉ¸Q_m"÷ÖuÆÁy¤Žú•µÀ´¤ó(¥)’¾D¸9­Û‡Å%÷níÁξ>íÉ÷qÆÎO>Üz7ù±ŒõÍÄ'ø_…wé>ÏÞ;¯*¸yvíäÙœnÛÓv3Ž•«R|_ú[›¹[?ëv÷®÷þ¯ägg'Þ½{øþWMÿ“Q” R” R” R”Ï^.ïY™²½uœå²:ËŒÃ\…–YÝæJ Ú™]@ü£ôÒãx»Ü¢Ä‰qºÎ™8â4ü…¸†€6 BF‘@ú*èôç¿ðÆT[ê4¦Ã)·0‹ƒn2Ú‚Ú%„Wðöé*V9˜¼T¿h7;$ȱ›¸\•lUÝ—DkƒR–˜XsÃd|ÞÞÒÞ#»¸ŸŠø•d9¥êñw½ÊL»ÕÖuÊBK²ä-å„HH*$ã$œ~³Z5л`6w­ÚFUžtg›ðµ²c°Û ÷d‰¸„© ¾ê’­¯ òÇR”²àO= R” R” R” R” R” R” R” R” R” R” R” R” R” ìqŸã)¤ÿlÏð߯J£ú«Í_¸Ïñ”Ò¶gøo×¥QýUlëUÚÏà«U~ç•ý¥W˜×?œ$}¡¯N{Yüj¯Üò¿´ªóçó„´5è9£Ðí>èü!ï¿Ï·ë´™ô÷Ù=ýIªM]»HùŸO}“ßÔš¤ÔvüâÛËÛ ù+¢ÓãWºJR•Î$ R” Ë…Ê”ÌfÔÒVêÒ„—]Khœ ÊQ HúI I5ж­h€äæÑt“&4CžGcGKÎ'¡Æ¥ ¸(`dõÆoFÓW™´éFã4›À”¸e‡$¶Ì’RQ½JÎA¯S€2H¬V;Æǫ́IŒ†™ÚzT¶£2’¬íO#ªJwª!9É Q 8·ü!Ò?øÍðϾß6­{SňêgÊŠ”v…S…%ļ—±µVâÆA=Ô )t©=Qy“¨o².òÓ±ùwwžÆÔ„;ËZÏDJÐ00ãGk›]«Ni«Lë¥ñLAº?"å¸M8ÛÐÜØLD­OZZšÊÛ!(Qt’[Nàæ••˜òmça×Ç”„ARS¹Dzå$dúÔ®’Ó2žLG]v8ZƒKu°…©èTTHô€N>“é©É:¦DÍ$4ìÈ­p²´;q”c„­ #.4ŠtìÜ …<¤”å”ì!^¥)@¥)@­äÚg*Àåõ-´¨ ÊLE¬>éuHRÒ yÞV§iÈ"´jÕm›¦[ìúáh“p»¢ç.S2·6¦¶Q! G!|+ ç«g—a^š9V™ÑlÐnî¶×rœ·[ah} %mmÞ•%$©oA€ÈP# æ¶ÓW–âÂ|ÆiKœ¶Ñ*$¶¹k. ·þœ(º† IH IîNd®StËŸ[í®uÜâJzYC–æÒ–ò#¡Hä•a<…lód 'ÓVÿhyZ¦ßª¶w‹ƒW¶îžàÄ.¸¸ü­dÉÜ¢ŸŽu!C!? |±\lÜ*š˜ËiíÁ·¢ËjK*)Æäò4¥'pÜ’Sœ€¤’0¡˜Ê°ê;…£À-Ö+ӥlj*LµI—Ö¥¼†PPKŽ $0íÝw‘·*œìVZ4´‹ ½Ê»ˆS‘·"Ca·%‡—B@!HBÚÆ”¾¼ôÊT*WT¶ë}5nÕs®ðîwÄ&\(­<á¶ßyô6ëŠq‰­87©<Š„-N´•! <Öí+¿]%ÍÝ%}áõ»ºKüÏÊ'+szºõVNN­*O¾ÿéoñ{Ÿýo?‡lÿIò6óg“ï¿“Ž?“ù^ªŒ R” R” R” ˜“¦¯1¬Âììf„}ˆuH[Sí¶¼lql…r!µnF¤„èÁ;Óœ“ô– …ÝÒí¿‘×ßL`Ìw›}æßVv²ãm©JiÓ…ÚÂTJT1”«ȼÙ"h©–¸]ùRç!´ºÓ‘Z@ei[jRûÊ#­ž.Œ) + Ê”ØR¦5>¬Ó—Î$"çÝ%]™5,Äe‡£²7…¶ &k¸páù,ÿÕ]Bùb¸Ù¸U51–ÓÛƒoE–Ô–TSÉäiJNá¹$§9I$aC1•aÖÓmåGvÓpœûM ´ˆÏ[‘¨”6ÐKî’2¥’Tw¥©JUW¨¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(;ÜgøÊi?Û3ü7ëÒ¨þªóWî3üe4Ÿí™þõéTU[:ÕAv³ø*Õ_¹åiUæ5Ïç hkÓžÖZ«÷<¯í*¼Æ¹üá#í zhô;Oº?{ïóLJíúí#æ}=öORj“WnÒ>gÓßd÷õ&©5¿8¶òöÃ>Jè´øÕî’”¥s‰”¥²ÄLuJe2Ýu¨åi­¦ÂÖ”g©JIHQÐ úG¦±VÕ®K0ç7"E¾5Ťgti*q-¯ Ž¥µ¡]3žŠ@ÎFE‚6“Ž®ÕŽ‡“su´xºíbkqBÉXt´•ñ•Ž…XÈÝÐò±ƒCé úŸ¾Hb5ÍØPv ÝS$nsvÄ¥¤”ç;J”¤¤ž»Š­Ÿ‡Kø{ðÏàÍÄ9û×é\=ç——ŸÙÝ»¦ÜìÇäúëF.¦j;“šrÐå²bÚuVÅ®Om*JJƒÁÜ€ã …?ztNÐÞÓÚFߺm¼IgÅ®ŽZìù‚“Êêx°©ñ)<íuG)~‡hÝP«T-wwfS“¥FƒpŸâ.]#J} JâLp¤­ä%µ%’ÛgjÒ´ ƒ ¨­†û¢õ%’*åÏ‚×wBqNÇ–Ì„,$¥YiJÂÛ$ú¹šÎ9[Ý­zÓwK4T¿qî-°Ú£¦àÂä4¼Rã)Yq²0A HÚz•eºö›*á`“e^–ÓÌÇ~/uÜ×{Þ„GJJJŸ# CŒƒ÷¾¹Ü­Ñ³YMÔ6¸6·aF‰ ¶ü—ö¡(2p¶?%²pÁ[Q´ ívù÷YÍÀµÁ“:[¹ãb3JqÅàp”‚N'öR_&ªéá\,nIKÎÿæÑÐË~llæZÃKV U„-]ô¥A0u9ð¿T‹§‹7¨nm\‹MjB›ë{·aÇBœõ ¨“„¤z6éëlç!H\eºÞ7*4–ä6rèãjRUéõƒzƒRöÝ©.6h÷xPZz•­¦V™låo#U»˜î-czÇT¥@f¢.— ÷YÎOºN“:[¸ä~KªqÅà2¥NöV µzÍÄË6Cë²\WqŽóÆF÷PHÊöº@ãh€ûÒsœ¯pUjbNœ¹C³ ¥Å-[Úur+RW±ùIV6©¶¾Yl‚¢ 6v(nÂLd·Pô§žn;QâÔ¤²ÑQC`œ„§q*Àô ’~’ke›½É«3ÖdLtÛžXuqVw6òübRræ½8Vܧ8$Ñ¥)@¥)@©†mÝÑ’ïɘè‘âÄEÆ, …¶êÒ°æìäT vúÁÉê=Xajf¢é):téËCÈ’´ºä·'œº€ê[sÊðFPXnÓÓpV(lVÔi!yfòë’ÐÚ›\=Œ8²”Ó.oqÄ$¯-¥ üó¹¾Iy}ŸîºxE²íÞ.ïlX§ ¸YnSªq)-­*Yq­Ì¹•)(V6§$&"ñ©šºY£@{NZz,V¢³1¥É!ÁÈAx´ Žå+ꥭ_)Y­™ºîîô¦çEß?Ä[ºI”¥˘ÙQCËKŠRÇÔ%;ÎR@H£¨lð"Zá]í3mòßz02¢&;Éu¤´¥ùãƒn×›ÁÝ’w8ôîš¼êæ.Ï©&·[ï-¡Â „ìB”á*)H %JJq•$úún‘bÁlƒjn:ÜX…Õ#•À€· Z×’lcvѰ`TNöÖttÉáÚm“Ÿ{‡ ™ÌxøžCÉÚqï¶£œü€=@†¬}'|zt¨eˆÌ*.ÎWeMe†<ãs{^qaµoH*FÕéIÊA5-…Å”ôgÒ–ÒÔ…Kˆ$ªI)Pú$H5s‹Ú<È—ÕÞaØ-äªA‹2àÆÖ™HBF[’•¥ ƒ’AãIÆwL–úåJzK‰i+ujZƒM%´NNÔ¤¤}  ½Åï ñ-ñ¸9ø6÷–ù·mÝž-ÛöãòöíÏLç¥jÖ×ygÂûŸ‡ÆççåæÛ·xß³nzçnìþV:V­”¥”¥”¥™zj"´s·è·).*>Àùr j"œQFXiò¬¸úC‰R‘±#jPR’U½ªt2,qb¾åÕÖÐä¤FT‰”ÌwÂ&DE¤­Rc§nK)8[D$ïÀ‡^¤sÀ¶1j¶EvC.c ­/He BÒ…'wó6Ù*JµeJ%Kݽ7[Ì—)¹.Ùík¸·r¸…4âÑr‚¢úV²#¹Ca <Šè¡£«,ð-=Åp.2d¢[î)‘Ci=P¥!.8V’œ¨(6Е!Kƒ©Ozjù+½‹,|…-n>ëIqr¢ RËî¸IÎNF3¸ç=1@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>ã?ÆSIþÙŸá¿^•GõWš¿qŸã)¤ÿlÏð߯J£úªÙÖª µŸÁVªýÏ+ûJ¯1®8HûC^œö³ø*Õ_¹åiUæ5Ïç hkÐsG¡Ú}ÑøCßžžû'¿©5I¨<íùÅ·—¶òWE§Æ¯t”¥+œH¥(¥(¥(¥(¥(¥(tÓš6Ûx³Yd@ìy÷‹‹¶¸ñpSŰ•…ㄇ‘¹xÞ’p–Ü"—SuN¦ƒ$H:Šï<5©È­35Ä!…¨()Há$…¬1ÊúM=Y¯zz=9 ê.=Î[ì!j¶K)SËÎß;|y!*s%¯"“°½ÕU^—"D¹OK–û²$<µ8뮬©n-G%J'©$œ’k¿Zi[MŽÖÔ¸ÿuo†Ë\U€R£»ý<Ç•êª@ëéÔ)J)JXbE‚ïg)ʄПï¤J ^òÓ¬É*l§vÌÊH;wu=HÀꘪu4[1²ÆÔWvm… lÃnk‰`¡yÜ€íÁÜr1×'é ’¼7f‡¤£0ý•¨—WÓ±Ö™*IA©×ò®4¶¼ž6ÒÚW·jÔ½¡%ûSúKOÜ5Kú}«pjÛ«âió";Î¥0êä!N9È¥$:8ABPœ­^R6„ÐäêM*Ì,²uÝë`BÜšâ˜F6§a;p6Œ tÀú+Vãx»Ü¢Ä‰qºÎ™8â4ü…¸†€6 BF‘@ú(&5+6éZZÕ¨!Z£Z“6\7#ÅqÕ2C(Ž´¯ãVµn<êÍŒ%8äœé8úºDèFæì)­!“=Ô8Û«vCqÒ­à ¼ÞHJü»Ž2Têñw½ÊL»ÕÖuÊBK²ä-å„HH*$ã$œ~³Ylz‚ÿbæð;åÎ×Ï·›¹Ë[<›s·vÒ3ŒœgÑ“Aa·èËsš†U¶v¢j#(‹\e©,6ãÍ>ÚA!÷ši$!iÜê”p´…-5[´^ãt— l”wwÖÖÙ,p¼6¨Œ-¼ŠéÕ9895.s­S)ÉiÖ…2B[[¢äöõ!%E)'vHj z·¤Õzƒkd äï2|CŸowOߕɿ;·tÛ³ë»ÕZµµâü/»ôŸçïו\<»vòlÎ7mé»ÇJÕ R” R” R” °ÜâÁŸX®-Bi™®\gE}ä-d¾†Ñh* QH#™cÊ‘Œäõ76ôƾx†½ˆÓµ K[ÙRS-ÈîîåSüà´_@áÉc-¥Nœ‚ÝPäêM*Ì,²uÝë`BÜšâ˜F6§a;p6Œ tÀú+Võx»Þå&]êë:å! ¥ÙròÂ$$q’N?Y ˜Ô¬Û¥ikV …jjvLÙpÜÇTÉ ¢:Ò¿ZÕ¸ó¨60”à’k5½z¼]ïr“.õur„Òì¹ ya’ ‰8É'¬Ö”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥`ûŒÿM'ûf†ýzUÕ^jýÆŒ¦“ý³?Ã~½*ê«gZ¨.ÖZ«÷<¯í*¼Æ¹üá#í zsÚÏà«U~ç•ý¥W˜×?œd}¡ÿ½zhô;Oº?{ïóLJíúí#æ}=öORj“WnÒ>gÓßd÷õ&©5¿8¶òöÃ>Jè´øÕî—Fì`[­ê¹^oÚnèE­qY¸0܉yÖùAiÇ›Ë%­í©dù ‰ZF[*F[m·J[!­‹Ô]59û×Up}›£«ñVÊŒtÇ-½…eÀR¥%·¹•)$%Ñ\Ò•Î$.ÝlÐÓ»?“q-Ænæ¶%<ëH”ÛnE|)e¦šæ˜•)­¡¢~%奀²¾ˆŒÕpì°´-ŠT+MŒÎ› sìÞyÞeàë…$GKÅHRšJ …aHA)KJ=(:ö¦Òz ˆ0ñ;dV¢Ý#Gœä£¸ì5•%瓉©å' ¥†:-Dµ+x—bÒMGÓnÞí‹Y¸\g&K¶û¿}câc´Xi$H)mµ¸êBÂÜ *åimäÕ½z¼]ïr“.õur„Òì¹ ya’ ‰8É'¬ÐNvÉþÂ,Ii ¹+y ÈiÖ’æõ'‰FÐ’BÝR·”µ %ÎfÁwbÁxjÓrTUµ ÜQf*Pøyå­jŽJ7e[AÁØH¥Òƒ¬èkF™Ô’®W_w´I‘p»ºÜ‰êšÛ J J•!¶œ~9lâ”[;qÄ„9ÛpGe7›s×kA»±1˜ž9œ4Ô¦ßZåÉ$–€Ú2àÚS¸`Õj˦¯7ˆª“oŒÒÐZl9%¶—!À-²•¨)ç<Éò6¯:<ÉÌ=âùj»%°¥û¥±þã6céŒ/Üy–D^0–C…c+’€œ¤î*9ª¤ËMÒ" HîÒÃè,=ÎãspÚ®B@Fâ@œŒV­(:ί¶Y'ö°õçQêX3¡]®÷ÚXžÔœ2ÚA‰½hxlmÅ)-íRšÚ”:™)Ö}Eùæ­¶‰(hj\v—|ee‰-²–ñ[je*+AZœ%8Ak{uÉ«+Ñä2Û.<í¢B Œ©h 8€¥'rIôÉPÈõ¤UF{NiÈ×yì¶‹÷Þb­DøBËl2Û­(Ëâ‘˰:ÓÁ(m.)ôîRj“Õ±t¼cOÚtî·»j½Üb½"dù =0ãC/'sûŸW‘ÒP„¹‚‘†Á^Ëì·‹½’R¥Ùn³­²‚Ú‰!l¬ JIIãõ Ñ ë.C·_´ö˜·]$Úš4Ô”÷ù…„®"ÚrC‘X .„ «‘„(9»È¬$6ZZ‡&¥ef<‡›yÆXuÄG@qå!†ÐT”îQ¹I>µë ìZöe®ì«]¿Qê¿B|ÛƒÝý© ‚ÊX KîËBÝ)Nî' ^P…:œ­8•§tSÚ¦ö¸2Q*Ýr/7êËM¶¦c—Zq+LÙÄ:¤ -©öÛ^68¶B¹Ú·# RBNô`éÎIúKPBîévßÈëï¦0f;;óo«;Yq¶Ô¥4é€ma*%*ÊU€¼Z¬ºZE݆nöÍ5÷!O2"CÔIK „´“Æä-÷[Åh)R–@Ql1¨ë‹|#Ù.–ô[#x”"ô˜p§¦BbºXÚ\[ˆË|K!ÂNå¬ m)LeòÅq³pªjc-§·Þ‹-©,¨§“ÈÒ”ÃrINr’H†c(¥(¥(¥(¥(¥(¥e‰D¹LĈò$<´¶ÓM ©n-G)©$œ(1R¤ï–+›…SSm=¸6ôYmIeE8ÜžF”¤î’Js’F3@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>ã?ÆSIþÙŸá¿^•GõWš¿qŸã)¤ÿlÏð߯J£úªÙÖª µŸÁVªýÏ+ûJ¯1®8ÈûCÿzô絟ÁVªýÏ+ûJ¯1®8HûC^ƒš=ÓîÂûüñáû~»HùŸO}“ßÔš¤ÕÛ´™ô÷Ù=ýIªMAçoÎ-¼½°Ï’º->5{¤¥)\â@¥)@¥+,DÇT¦S-×ZŽVêÚl-iFz”¤”…=‘Ÿ¤zh1R­Né8ñu^¥µÎ¹ºˆuo R™ŠëˆD„ÇICEi•¸‚AXÂw’9^Ò0 ª|»µâKV¨ýˉè°Róîw¶!Í)Ä%?…oÂÕµXx;€eÑzšÑk‹fUÄNL‹ Ýwhˆa”8‰kPcâ–¢´–Fc'Ο|>_.K«ÄNÏöÝ<"çvî÷ ·ìPDxÜ̹)¥6•¥ ¶Öç›Â’•«ŽÑ€G ëÚË^iþ–—EÇRθ.l´üÛ{JË­"(BÔ£!Dy™’r2Sß^Ær­õîѵ„í‚ßk…>ï$FXQKÈ\hãj6‚#÷—›¯—ˆ2” É PPÙ}Ñz’ÉrçÁk»¡ ¸§cËfBR¬´¥ am’}\Íg­îÖ½i»¥š*_¸÷‰XmQÓpar^ )q”¬¸Ù …¤m=J \ç­³››–ëyÚ™1›ÙÈ#«n%IW§ÖêZ™í|mZö§‹ÔÏ•1(.í; § K‰y/cj6­ÅŒ‚z ©EV×oŸuœÜ \3¥»ž6#4§^' H$à`5%ðbj®žÕÂÆä”±Ìïþm ·æÆÎe¬4µ`¥XBÕÑ_JTÚ¢ó'Pßd]å§cò6îï=© w–µž‰• `` ÆŽ×6»VœÓV™×K☃t~EÊ3pšq·¡¹°˜‰Zž´µ5•¶BP¢é$(¶Üúéëlç!H\eºÞ7*4–ä6rèãjRUéõƒzƒRöÝ©.6h÷xPZz•­¦V™låo#U»˜î-czÇT¥@f‚Zc¦SɈë®Ç Pin¶µ#= ’ ‚I ÇÒ}59'TÈ™¤†™®V‡c.2Œp•¤eÆ‘ñN›p§”’œ¸R†½Ståʘ].)jÞÓ¨C‘Z’½ÊJ±µMµòËdé³±@+vB”¥”¥­VÙºe¾Ï®‰7 º.rå3,!»sja+e”r°®pJ¶ypF骭L3hŽîŒ—~LÇDˆ·".1`l(u·V•‡7g ² S·ÖOPrå©­Ù§:ÈœnwD;KÑÖÊ "?vøÔºT²®êŸ!m8ä>c³Í%']Ú#_×~·F*DíKPËŒúÊ#­•¼¾8²à%õB”c`;Nì&£>Ñ%j¾³1×W6T¨¯0¶CKd2¬¥AGxR^O¤'×ÓV }ŸîºxE²íÞ.ïlX§ ¸YnSªq)-­*Yq­Ì¹•)(V6§$$"5ÂÑàë •éÒãÄ•&Z¤ËŒˆëRÞC(( ¥Çvî»ÈÀÛ•Nv?«-ZEÐ^å]Ä)ÈŽÛ‘!°‡[’„HC‹K¡N ¤!mc Ê_^ze*¯j<–¸W{EÆLÛ|·ÞŒ ¨‰Žò]i-)~D¸àÛµæðwdÀ€N=;¦¯:…¹‹³ÆjI†„­ÖûËhp…(!;¥8JŠR‰R’œeI/–Ýo¦­Ú®uÞÎø„Ë…§œ6ÂÛï>†Â]qN15§õ'‘C…©ÂV’¤!gšÝ¥w뤹»¤¯¼>·wI™ã¹Dån`oW^ªÀÉÉÀ©(úNøôéP˘T]œ®ÊšË yÆæö¼âÃjÞTª;Ò “”‚j"[ ‹)èÎ)¥-¥© -:—H8;T’R¡ôH>h7»ïþ–ðï¹ÿÖóøvÏôŸ#o6y>ûù8ãù?•ê¨ÊÚî/x_‰oÁÏÁ·¼·Í»nìñnß·—·nzg=+VJRJRJRRzRëàZ¦Ó|àï5™|;örq¬/nìgÎ*Ã3AÈgE9¨ÙzsÈb+2Þ{¸Hqm£©!G‘ä©Ô¥h(NÒ‡FNÏ6¬ÝrÆÚiëµÚâí¿Ã“å\gÚÛŠQ tå?‘Œ(…%­¨î·Xl¯N—$©2Õ&\dGZ–òA@m.80À;·uÞFܪ½Vm§Q¦¥G‚ỉªA[ÈjT4œ³½[Ül¼)HlôH9½@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>ã?ÆSIþÙŸá¿^•GõWš¿qŸã)¤ÿlÏð߯J£úªÙÖª µŸÁVªýÏ+ûJ¯1®8HûC^œö³ø*Õ_¹åiUæ5Ïç hkÐsG¡Ú}ÑøCßžžû'¿©5I¨<íùÅ·—¶òWE§Æ¯t”¥+œH¥(µk’Ì9ÍÈ‘oqiÝJœKkÈ#©mhWL碇P3‘‘Zµ–#¨fS/9©(miR™t¨!ÀJU´…`ú?A[†µzn¥¸Þœ°Ú.¨ZnPÐdpK*t…¥q&8RVòÚ’‚Im³µiZÁ„€T Vº£KOÛõK}Ûjå«åéñ"C΢°Òã¡.7Ƥ¤ºyÔIZVœ¡>P7rº í×´ÙW ›*ô¶žf;ñ{®æ»Þô ":RRTùld½õÎånˆÕšÊn¡µÁµ» 4Hpm·ä¿°í Aë…´ù-”ƒ„î Ú²ú£³èöÛ׫míÙ±ÚŠÌ­²!†P´ER†âÆ@Ÿzüo£by#5N—·Z,nPoÝK«B[-0c£r †V‡Ôâ ÁÚ—ZiJ Y)IB’«SŸ õHºx³z†æÕȱÀäÖ¤)¹·»vq$)ÏPʉ8JG¡)6×™“›"áÜÒóºL”¸¦Ñ€OPÚ®¸ÇDž¤g&­ß­*Õ>åÒLH‰…ÎÜ÷]ƒ¶Yߌ¶U) í(Â^pîm^2[ Òá>ë9É÷IÒgKwÉuN8¼T¢IÀ~À*Á`Ö¯Y¢Ø™fÃh}vKŠî1ÞxÈÞãÊ ^×BHmzNs•îˆÕèÖ›ì‹|Iõ†¶í,ÙH'«.ºŽ„‘ÑjôuÁÈ9£m·‹5–HÔÇŸx¸»kPw%<[ X^8Hy—é' mÁ’¡-Ô=)盎Ôd8µ),´TPØ'!)ÜJ°=$Ÿ¤šÙfïrjÌõ™6ç–\U͇<¿”œ„¹„„ïN·)Î F¬ËÓÐÑÎÞ™¸ø¤‘°-˜e)L ¢Œ©à¼:pORß燉ò¬Ò” R” U†¦j.’“§Nœ´<‰+K®Kqry˨¥·<¯eå€6í=7b«Õa‰ ½œ\§*B|[¼F‘(-{ËN³$©²Û0 ) íÝÔõ#n¦jV’§Fœ´2ˆËS­Ëmryë %Ç<Ïea”6ív„æ¶fë»»Ò›4|ÿné&SZ—.ceE-.)H;P” ï9I / Ù¡é(Ì?ej%Õä4ìu¦CŠ’P@*uü«-¯'´¶•íÚµ/hI~Ôþ’Ó÷ RþŸjßÜ¶êøš|ÈŽó…éL:¹SŽr)IŽP”'+W”¡!C¿_MÒ,X1ímP#-Ç[‹º¤r¸áS«ZòCmŒnÚ6 J‰ÞÐ:ÂNŽ™"\;M²sïpáS9Èy;Cn }ñ¶ÔsŸ ¨¥fÝ+KZµ+TkS²fˆäx®:¦HeÖ•üjÖ­Ç@ù±„§““³½'WHÜÝ…5¤2b§º‡unÈn:BÕ¼·›É _—qÆ@ 轣̉}]æ‚ÙJ¡1³. mi”„$e¹)QÊPØ9$4œgq4Éo®T§¤¸–’·V¥¨4Ò[@$äíJ@JGÐz«}¿F[œÔ2­³µQDXòã-Ia·iöÐê ¼ÓI! Nä‡T £„…¤)iªÝ¢÷¤¸[d£»¾¶¶Éc…áµDamäìWN©ÉÁÈÉ w–|/¹ø|n~~^ù¹Îm»qÇû6ç®vîÏåc¥jÖÖÈÉÞdø‡>Þîž-¿+“~wné·f1×wªµh¥(¥(¥(&$ßJ¬ÂÙÙÚ‡„Lv1t¹3nä.-X†â”$«i);¶roiš®ešÑLç_‘i¸‹Œyò$¾ûåáòJƒŽ)²³¦ç/vË–½+/³¹×K‚Ôˆc()FPšäµ¸„­· ‡v-d- olmü—k E¢ç§#;iÑÑ­W ¥Óe¦<7ä¾û‘“¹* 8 ½Î-´!HHܦ^1‚ûõôÝ"ŃÙÕ2Üu¸± ªG+n:µ¯$6ØÆí£`À¨˜zºv¡ȉMNÒð­°;*KPåA\²·v7“%Y% l’„„žC‚¬š]”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥”¥`ûŒÿM'ûf†ýzUÕ^jýÆŒ¦“ý³?Ã~½*ê«gZ¨.ÖZ«÷<¯í*¼Æ¹üá#íýëÓžÖZ«÷<¯í*¼Æ¹üá#í zhô;Oº?{ïóLJíúí#æ}=öORj“WnÒ>gÓßd÷õ&©5¿8¶òöÃ>Jè´øÕî’”¥s‰”¥¶­w ö©ÍϵΓ[Yã~3ªmÄdp¤FA#öZµ½`fsט©¶Jj$Ô¯‘‡Ü˜ˆ¡µ§Ìʵ%(#G\c®(7¾êïñ_…7ÏàîýëÄæâÝ»~ìíÝ×nqžµS©™¼½zgQ]Û¹È@m鈚à}Ä ¾U,ÄySПÉE_&ÇžÙµ5Æ£¶Ci÷åObT;ÌFÝ}•Èò¶Ì‚½¬:¼Œ’ ´·¿Ê°x×­á|TKܯËN©“6#ÂD;Ë,$Æ ½Ê„ËåÚæT¨å@¸¥­IÜ­ÊJ”…n¼]í±eÄ·]gC5rÚbBÛCèÁVC PÁϤý5£]ŠÏx´ û¯Ø®°möê¹r®ñÄ„DD«J–Éi²ÊŠL†ö&H ¥+#qFðÇh,2õε—è’õ†¡‘ä)·Zväòâ0R U‚8 ÖóP_ï¼>9|¹Ý87p÷Ék{v7mÜN3œzp+¥ë-'¤U¥¥Ï¶¯MA¸5 ·’Ä-BÓÙu(Š\BR·ÖU•;4`džìÎß”ykÝ£[´í¾ÁoM¦5¡‰JX†f‰Rë—”óJ#r‹l¥Æô (T­«Zà79µÝ#I“g‘¨ÒËŠèq…© ®É93‘nñ5Ox“3ÖC dhíZž eÍù!Ä ˆQWËVâû£j’:`%°£ÔÄS© Å‰¢»Å jr+LÍqaj R8I!kŒgr¾“XµCÖ™ÙXØà·«o |*kPåS®‘æÏ¥ÅñègÒÓtæšMÒnGáΖåõ,¹‰ hº6ºœ<HÂSnHs™r$K”ô¹o»"CËSŽºêÊ–âÔrT¢z’IÉ&±VYl.,§£8¦”¶–¤(´ê\A àíRIJ‡ÐA úA« å飣/t»ù9×1³!O¤n ¸0–²¬+ikpJV9ÔÆB³JRJRSµN¦‹f6XÚŠîͰ¡m˜mÍq,/;“°¸;ŽF:äý5W‹\I¯v?tŒn–ÀÒ®‘§Ç†íâ:ÚÛ2òÃ*p((å¡»–6à(A_“ªu4«0²ÉÔWw­lCrkŠ`!Ú„íÀÚ01Óè­[âïr‹%Æë:dxHãˆÓòâFÚ€I JF=è«£ÓžÿÃQn7¨Ò˜[ ¦ÜÂ. ¸Ëj h”" \wÀKÛ¤¨mXäbðqV ÅâÐoí?}ºÁ¸XªâJ´G2-m)[ÅÖÃ)*1ÛØ¨À²¤  m;Hr»Õâï{”™w«¬ë”„ 6—eÈ[ËTIÆI8ýf²ØõþÅÍàw˯Ÿo7s–¶y6çní¤g8Ï£&¬:þ\笆/÷†®×öåJ[²ÅÖ"© P^BÖ0™#vS¸œ àžÇívó"éR&ÐÜrˆénT˘ˆãR—ÐS©JÈd¼¾©X mv¬ Ó®uªe9-:ÃP¦CˆKkt\žÞ¤$¨¥$îÉ­DVãôš¯WT¶é/UÎn[v91 +í¤^ã:øØ/4‘ßXR¶¸”že¥#rWÈ—5»pø¤¾íݸ9×Çݹ8vî8ÙÉçÛFÿ61ž¹ x„ÿ ð®ý'ÃùûÇuåW.ݼ›3ÛznÆqÒµjO‹ÿKsw+gýnÞõÞÿÕüŒìáäûׯÊé¿òj2JRJRJRƒzãx»Ü¢Ä‰qºÎ™8â4ü…¸†€6 BF‘@ú+Yém–Þ}×¶Rµ’AR•µ úå(àzÔO®®wÈ“UÙ-…/Ý-÷³LaxŽã̰ú"ñ„²+Xt”å'qPÍZ®ÚŠÈeXÕ¥­7èrÞŽ›«WX0¡ ¬>Ûi%,2­É€§ K) j Õޯ{ܤ˽]g\¤!´».BÞX@$„‚¢N2IÇë5£WM.sÖ CûÃWkûr¥-Ùâ‰ëT†(/!k L‚»)ÜNðM.JRJRJRJRJRJRJRJRJRJRJRJRJRƒ°}ÆŒ¦“ý³?Ã~½*ê¯5~ã?ÆSIþÙŸá¿^•GõU³­Tk?‚­UûžWö•^c\þp‘ö†½9ígðUª¿sÊþÒ«ÌkŸÎ2>Ð× æC´û£ð‡¾ÿgÓßd÷õ&©5ví#æ}=öORj“PyÛó‹o/l3䮋O^é)JW8)JP+,F*S1›SI[«J]u- p7)D%#é$€=$Ö*Úµ¢“›EÒL˜Ñy/8ž‡B–€zà|¡“×!¼Þš¼®ÿ6Æc4ÔØ q9¤¶ÛQʱ[ÝRƒiðJ°T¤€IP$}'|zt¨eˆÌ*.ÎWeMe†<ãs{^qaµoH*FÕéIÊA51¨.ÚRãÚ=ÆþÜ›¸…:S³’·m¬-qžSÅÀ•0§‡ÛÇ”‚´gvH!;W’v¤°^xprç Çà¾Ô–"¡ç ¢°ã8à.!-¥|¥aYKA!´‚œp4– ›ÞÕ¿ÖTḃ›açN72Ûn)*uÑ”‚Ú” ’1•'0uÑ£k»D›ú/×Ӣȃ©dꑘBD…¼¶WÀ· [°‘ȼï'hÛ…sš+¯k-y¤oúZ\ K:à¸M²Óómí+.´ˆ¡ RŒ…æfIÈÉO}{Ê·×»FÖo¶ }®û¼‘aE/!q£¨ÚÞ^l¾^ ÊP7$%AC`P«+Ñä2Û.<í¢B Œ©h 8€¥'rIôÉPÈõ¤UeµÎzÛ9¹±Ñn·©“¹ œ‚:¶âT•z}`ààŽ U©žÐgÆÕ¯jx±LùQC‚îÓ°¢ p¤¸—’ö6¡jÜXÈ' Ú”.•'ª/2u öEÞZv?#náÎóØÚ‘çykYè‘éQú¼hískµiÍ5it¾)ˆ7Gä\£7 §z› ˆ•©àKKSY[d% .’B‹iÜÒ²³Cͼã,:â# 8ò‚Ch*Jw(@ܤŒŸZ€õÒZc¦SɈë®Ç Pin¶µ#= ’ ‚I ÇÒ}59'TÈ™¤†™®V‡c.2Œp•¤eÆ‘ñN›p§”’œ¸R„+Ô¥(¥(¼›LåX¾¥¶•¹Iˆµ‡Ñ½.© ZAo;À! ±´í#9VZ­³tË}Ÿ\-nt\åÊfXCvæÔÂVÊ$!(ä/…a\à•lòàŒ+ÓA›EÉV/¾ܤÃ2H ÊB–>“µ$œz:g†w§é-A »¥Û#¯¾˜Á˜ï6û;¬íeÆÛR”Ó§ µ„¨”¨c)V,35•šãÙ|?.ÔÔ{²W 1‡8¸ã¥cr‰‘µ.+™Õ­m*RÔAR÷#fN»´F¿®ýn:T‰Ú–6¡—ô!”G[+y|p)eÀKê…(ÆÀvØHS/–+›…SSm=¸6ôYmIeE8ÜžF”¤î’Js’F3VGp´xºÃezt¸ñ%I–©2ã":Ô·Ê iqÁ„†Ý»®ò06åSêËF–‘t¹Wq r#¶äHl!Öä¡âÒèS¨)[X²—מ™J‚…Jê–Ýo¦­Ú®uÞÎø„Ë…§œ6ÂÛï>†Â]qN15§õ'‘C…©ÂV’¤!gšÝ¥w뤹»¤¯¼>·wI™ã¹Dån`oW^ªÀÉÉÀ Õ¥I÷ßý-áÞ/sÿ­çðíŸé>FÞlò}÷òqÇò+ÕQ” R” R” R” Ë<‰r™‰‡dHyim¦šARÜZŽRRI8V*“Ò—_Õ6›çxðé¬Ëáß³“a{w`ã8Æpq@¾X®6nMLe´öàÛÑeµ%•ãryR“¸nI)Î@RIPÌeXuÂÑàë •éÒãÄ•&Z¤ËŒˆëRÞC(( ¥Çvî»ÈÀÛ•W¨¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(;ÜgøÊi?Û3ü7ëÒ¨þªóWî3üe4Ÿí™þõéTU[:ÕAv³ø*Õ_¹åiUæ5ÓçhkÓžÖZ«÷<¯í*¼Æ¹üã#íýëÐsG¡Ú}ÑøCßžSQZS¤„¸°T@'=p hÖÕ¦|»UÒ%Ò¼2ᾇØshVÇ ¤œAÁ¡ ’Ô6x-p®ö‹Œ™¶ùo½QäºÒZRü‰qÁ·kÍàîÉ;˜:˜¿_MÒ,X1ímP#-Ç[‹º¤r¸áS«ZòCmŒnÚ6 J‰‡ R” R” R” R” R” R” R” R” R” R” R” R” R” ìq'î”Ò`u9—þõécXÆP¡ÿÅy«÷þ3ZCÿ|¿ñ¯½âm+sjÔR2JGZÏavãætქ§/ÚÐ?øUª¿sÊþÒ«ÌkŸÎ2>Ð× :íÅ·Ùæ¯-«flï´c ”ûükÏëŸÎ>Ð×›6MÖÒœqþèü"/6œ;hžïÛoZFfd]5EÂ5¹¥´þé2Râ›F=ChZºãz‘œ š®x³Å;ŸÃ/|á™Ã»v8ñÁ¿v:çnÜ~VzT×i3éï²{ú“Tšç3·çÞ^Øm䮋O^éNG±Û*:õ†˜ÙÇ%Æfän;X*}zS×Ñ‘Ö,vÉ=ã›XØáq>¦‘ÎÌÃÌ‘Œ8Œ+ > ­ªéÕ" é\âA1ÑovÌg9ªmä-BJ/’œá9K%¼«|u#®mö¬Âsz¦Ñ"AB`6Ô ø*ÆS•2ÊrsçÇC‚zf” µj›]­:’Ûlapl‘Í¢¯Ix¾´)×b6òÜ^Ðâ²¥,¤$$yz¦ªµ½x»N»¹Éî4âãEj#jClñ6”¼„€7+*À=Ò~˜±Æíê>˜eèÓmêÅ\vðáhÊ-–¥„¨¨'¡RJ‡^Š&ª;l+7{ÔË?ݽñ¹ åÎs·…§=Î=#ëŒQ.÷(·öoÍÌuW6¥&bdºy^ Þ­ÙÜwu9Î}u£Aa²Úà»V)ŵ0Û­Év$†ŠÒ‚¾û®D‚H(qx åuŽ›6åh€’Ä賕¨÷å…&yJ02>GH‘iY*á_¯*\¯ÃŸ.y±ã;±©Ì$§h;Û!À:ŽžvÐr0zcÐH­Z ɰ"7 ­7D5‰r.“˜uÍÇÌÛmDR3‚ë@ÉÝ×Ð0¼@‰kÓ–ÖÞk7yù˜á*9ùYFÜŒ)~wàr‚Â’@Rr|·-lZÖîbG}×Úohò¸â[JÎq“Ó} ÀÛÓÒrºO—sœäÙ®ò¾æ!!)H%)J@)J@JR@šÞ¦6®ŽÛ¼2ß"¾K­Æ {‡š#.¹°8¼«Ìµ¿ dzj½-1Ó)äÄu×c…¨4·[Z‘ž…IA$Hãé>šÞ¹_®× ê/Ëã¸7ÅÄôfÓ‹‰)C{ØHFÔ¡ m£.D‰rž—-÷dHyjq×]YRÜZŽJ”ORI9$Ð[û(°Ûo— ŠoKƒb†D©¯ð6Ãθ”%aeÄ$¸„ò¼Q<‰ei'rw»5Ò6絕:¯â‘6ö«Rmòt¸ßy.© JÚP #@¬a{ 5 v ¿Û`˜ëåÎBúd–¶Û.¤¤¥Í ¸ …zAHúlÇÖ:º4éSãê›ã2ælï/·pu.?°aÔ•m}ÑAµjµYgh»¼¶¦Éñ{{ Ìu§ á°ßxCè{®yг¹¬å îÞ¹è˜ñâê)q.3¤G´Û­÷]¼!¢Xd„8CŠ 8à€7îØ¾£»DÈ}Î0—pȸ\”·œ“1%ÐñK…n|´ îJR£°dœ«v³‚þÄÀbùsj"v:XDµ¥´´éÖÂAÆÕ “èQ9 ¹ÚtŸzó¦ã;¨'M‹t¸Ç‡!øã­¶ùs´ [eDyCÍ6­¡jÙ”)L¾B†E¡ûœ»{»$Ì‚˜Ùu8+BB\p%M“æÏœd„¯š‚ÿ}áñËåÎéÁ»‡¾K[Ü{±»nâqœ ãÓX¯W‹½îReÞ®³®R€Ú]—!o, BAQ'$ãõš íd‰¨nï[¥]<3l)2[}QË­å–”ém;’¨W™)YÎ0“ž’zkKYï“® fû%‹T^4¢L˜Ì0ã‹X$†SòÓ˜­@”Q^²Þ.öIJ—eºÎ¶ÈZ jv$…²²‚A)%$dÔ+jݪu5¶T¹víEw‡"jù%ºÄ×[ëÉ;–AG*QÉϤý4[®‰´[,É&ãw{Qø¼ûH‹ Þ‡ØzD~ ”!Eį .6ÒNìlr½hz—tSwùWø1$HŠô¸±~:Ûmn ¤îx=È¥4°†–’Já•lƒ^©ÔËràâµÝK¹ 7=Fk„Ê@IHK§>pH³Ðâ±@Ôû}®Eªòçß'w‚Þm|t8'¦aé@«ì3f¹êÈöFŸjÄ„i¦'w€Ó¡×„ËqnåeH+m'RIªZ¬îïjÔ1oªlؖ¶ mø‚·GSj.É@Z‰P딤€ÞÓzzÒßhzB+£Æl—çÙØ™Mª;Šir{ÒÛ‡b‚в«P ŸIH¨ÀD÷Žÿ&KXR˜àŽ—w»ÓjU•§jO\¨n#óMoDÔרúLn.ʼ2´¸‰se/zSµ*<¡@”€0Nq€F£á>ßÞ;„é1;Ë ŽÿªG+JÆæÕƒæIÀÊOCŠ ZèVk^Ÿ²æîw m²;¡û‹\ ×)N4Ã.F ²]ڽκYKj Aì9]sÚÞzñwzÌÍ•ë¬ç-‘Ö\fä,°ÚÎï2PNÐ|Êê如Ôt$fô¢o^ö©†—::[d¸Ôw\mM¶^)C™³çm¶Ô2RáIB—Žù¡‘k‹x}Ë«¨E³Rø¤= Iޱ‡Ix-%JÊx²¦ÂT@Z*Î*áŽ®ð¿ øS|ðþïÝ|AÞ-»xönÆÝ½6ãéXÛÕ:™¶ÐÛzŠî„6†BS5À†U¹”žµR?$õ ²¿ "¹¨m–»må×Ù–‰»1äD ! 7ÊàC­Jq¥8 )ZÛÛ¹²¢”¯p¯kKkѨÑ.Ñ®l<Ày.4ë+S~e$¡ÎBU”“€µyT’pIHÖŸ¨/÷ ¤{¬ûåÎ]Â6Þ OËZÞkjŠ“µdå8Q$`ô'5Šõx»Þå&]êë:å! ¥ÙròÂ$$q’N?Y œÓ–]3+F\oW«­Þˆ·ÑR˜}måBA$–O¬mÀù[¼‹N˜¶ªÁs¹ßîÓ­Zî-B•Ãy%Ä:R ‰øÀ¦V•ì ¢¬ ÁÙo{$¥K²Ýg[d-µ;BÙYA ”’’2Çê– ¿Ûír-P/—8–ù;¹â±-heÝÉ Vä…e ‘Ô PXlz]4“—tÝÚfou‘1˜Žq -–Ô³çu.¨á§0[iÄdV’ãÅÜ4Ô^ÊÚº¯¼¿{¸Mz3|‘ ;—“Œ¡î¥m¯'Ê6ïTe¯XêëTàZõMò F³ÆÄkƒ­¶Œ’N•2I?´šÖøAð/ñËŸ„~Þ×ÝþVÿ½çoÊóz==}4í»I@ÒcÙÆ}Ñ ›2J¡em5Ìó+Cêk!%JùEăƜW-1Íl‹<){ ‹[WHîÅ+à)Åq—ñ¥^]á%g¡'Ëî©ÔÎÅ…ÍEw\x mÈm*k…–ØÂØÎR:1U.:§S\¥D—qÔWy’!/’#¯ÍqŰ¼ƒ¹’Rr”œŒzÑA‹PF²Æz7ݤÜXuî™0»³Œ¹½IØR°|©J²FB¥ŽÛ ãÍÞõ²ÏÇ·o|nB¹sœíáiÏFsHÆzãêñw½ÊL»ÕÖuÊBK²ä-å„HH*$ã$œ~³Z4,vÉ=ã›XØáq>¦‘ÎÌÃÌ‘Œ8Œ+ > ­ªéÕ"±Æ´[ݳÎj›Dy P€ãR‹ä§8NRÉo*ÀÇŸFHëˆzPLI´[Ú³ Íê›D‰Q€ÛRƒà«NTÈo)ÉÏŸ é•êÑo/DÕ6‹²Ô°’ÌF¥%i'qåe ÇLt9ê:zqJÅ÷þ3ZCÿ|¿ñ¯½ZIRXqìŒçNƒó_ÓëÅ|÷þ3ZCÿ|¿ñ¯»\h= ØÚœÌ6¿Hž“Gª¤r|c5y~Ú×zJ»;Õä  øSÃiÏL-?œþ WÀW?œ$}¡¯¾õÓa®Î5jŒ KÇ¢BGU¤úü øçó„´?÷®û!t{Oº=¨‹OåÛõÚGÌú{ìžþ¤Õ&»Î‰Óv]Qy´Ûï°»ÜfíR^J9VŒ,<È( ú@}Ñ?Ni?ð wsï]ã›ãœsvÞ-¿-GÜ}Myîwå› sžÖá1<9ÂqчøDöãÕØÒÉÙ~íEêÏ&M5q•p§#ƒÿ)íÇTv9-)JÒu¥)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JÁ÷8–¾é]&ê÷mA–£µ%Gþ€:ŸØ+îó""7i)?ëÙÿ|%÷þ3ZCÿ|¿ñ¯¼×©£H¹;mjê£=–RóÌ"B‚’•zýŸ³)Î23¿qš±œ;š÷Œ4b¯kÛýžjÀĘ(Ú!,ÉC§v„ž§p=&¼ÿ¹üá#í }ý©µWnÌ5„ˆW368µIiDŸMaï±~¥µ{*}ÕÍåÜÄœ§œUešoLjàðgéàëÇÏS›°ÈÕÙåZ2ŒÏøã5ãF¼{ûjUàÌŠýš×ìÉ÷Wó½ÄúšÙìÉ÷Vß3êÛFéuÜ£VÏÖŠUß½ÄúšÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÅúžÙìÉ÷S™õm£tœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãt«Ê5lýaH¥]ûÜ_©ížÌŸu;Ü_©ížÌŸu9ŸVÞ7IÊ5lýaH¥]ûÜO©ížÌŸu;Ü_©ížÌŸu9ŸVÞ7Jœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÄúšÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúšÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãt«ÊlýaH¥]ûÜ_©­žÌŸu;ÜO©­žÌŸu9ŸVÞ7Jœ£VÏÖŠUß½ÄúšÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúšÙìÉ÷S½ÄúšÙìÉ÷S™õmãtœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãt«ÊlýaH¥]ûÜ_©ížÌŸu;Ü_©ížÌŸu9ŸVÞ7Jœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãt«ÊlýaH¥]ûÜO©ížÌŸu;ÜO©ížÌŸu9ŸVÞ7Jœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÅúžÙìÉ÷S½ÅúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖŠUß½ÄúžÙìÉ÷S½ÄúžÙìÉ÷S™õmãtœ£VÏÖ¯¸½IGÝ/¤Vµ¥*–I'æõ}¥«ô!ÔmÇ\ˆwsX}æ;D-M‹lœ`¤¤¨~¬úÁ üf¿É²Üš¹Ù˜bÛ9œñIˆž'[È);VœH8>‚EJ¯´=Vµ.ï9DúI”áÿï[WlÖ›q¶?ügþØ­o•ׇözÃí][g6žÊõ,(öç!ÅnÎël  §8!GêN¥Ôô$×À×?œ$}¡ÿ½Yhš°0û ¼ÏKRSO Jp®…*êÐjªó…×Táô¨äÔýÆçý•Tp¸XÎ:°êÁ­UטÃFñëÿÙsipp-3.6.1/docs/transport.rst0000664000175000017500000001343313730472040015576 0ustar walterwalterTransport modes =============== SIPp has several transport modes. The default transport mode is "UDP mono socket". UDP mono socket ``````````````` In UDP mono socket mode (-t u1 command line parameter), one IP/UDP socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. UDP multi socket ```````````````` In UDP multi socket mode (-t un command line parameter), one IP/UDP socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. UDP with one socket per IP address `````````````````````````````````` In UDP with one socket per IP address mode (-t ui command line parameter), one IP/UDP socket is opened for each IP address given in the inf file. In addition to the "-t ui" command line parameter, one must indicate which field in the inf file is to be used as local IP address for this given call. Use "-ip_field " to provide the field number. There are two distinct cases to use this feature: + Client side: when using -t ui for a client, SIPp will originate each call with a different IP address, as provided in the inf file. In this case, when your IP addresses are in field X of the inject file, then you have to use [fieldX] instead of [local_ip] in your UAC XML scenario file. + Server side: when using -t ui for a server, SIPp will bind itself to all the IP addresses listed in the inf file instead of using 0.0.0.0. This will have the effect SIPp will answer the request on the same IP on which it received the request. In order to have proper Contact and Via fields, a keyword [server_ip] can be used and provides the IP address on which a request was received. So when using this, you have to replace the [local_ip] in your UAS XML scenario file by [server_ip]. In the following diagram, the command line for a client scenario will look like: ./sipp -sf myscenario.xml -t ui -inf database.csv -ip_field 2 192.168.1.1 By doing so, each new call will come sequentially from IP 192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.1, ... This mode is generally used for emulating user agents, using on IP address per user agent and calling a SIP server. TCP mono socket ``````````````` In TCP mono socket mode (-t t1 command line parameter), one IP/TCP socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. TCP multi socket ```````````````` In TCP multi socket mode (-t tn command line parameter), one IP/TCP socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. TCP reconnections ````````````````` SIPp handles TCP reconnections. In case the TCP socket is lost, SIPp will try to reconnect. The following parameters on the command line control this behaviour: + -max_reconnect : Set the maximum number of reconnection attempts. + -reconnect_close true/false : Should calls be closed on reconnect? + -reconnect_sleep int : How long to sleep (in milliseconds) between the close and reconnect? TLS mono socket ``````````````` In TLS mono socket mode (-t l1 command line parameter), one secured TLS (Transport Layer Security) socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. .. warning:: When using TLS transport, SIPp will expect to have two files in the current directory: a certificate (cacert.pem) and a key (cakey.pem). If one is protected with a password, SIPp will ask for it. SIPp supports X509's CRL (Certificate Revocation List). The CRL is read and used if -tls_crl command line specifies a CRL file to read. TLS multi socket ```````````````` In TLS multi socket mode (-t ln command line parameter), one secured TLS (Transport Layer Security) socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. SCTP mono socket ```````````````` In SCTP mono socket mode (-t s1 command line parameter), one SCTP (Stream Transmission Control Protocol) socket is opened between SIPp and the remote. All calls are placed using this socket. This mode is generally used for emulating a relation between 2 SIP servers. The -multihome, -heartbeat, -assocmaxret, -pathmaxret, -pmtu and -gracefulclose command-line arguments allow control over specific features of the SCTP protocol, but are usually not necessary. SCTP multi socket ````````````````` In SCTP multi socket mode (-t sn command line parameter), one SCTP socket is opened for each new call between SIPp and the remote. This mode is generally used for emulating user agents calling a SIP server. IPv6 support ```````````` SIPp includes IPv6 support. To use IPv6, just specify the local IP address (-i command line parameter) to be an IPv6 IP address. The following example launches a UAS server listening on port 5063 and a UAC client sending IPv6 traffic to that port. :: ./sipp -sn uas -i [fe80::204:75ff:fe4d:19d9] -p 5063 ./sipp -sn uac -i [fe80::204:75ff:fe4d:19d9] [fe80::204:75ff:fe4d:19d9]:5063 .. warning:: The Pcap play feature may currently not work on IPv6. Multi-socket limit `````````````````` When using one of the "multi-socket" transports, the maximum number of sockets that can be opened (which corresponds to the number of simultaneous calls) will be determined by the system (see how to increase file descriptors section to modify those limits). You can also limit the number of socket used by using the -max_socket command line option. Once the maximum number of opened sockets is reached, the traffic will be distributed over the sockets already opened. sipp-3.6.1/docs/_static/0000775000175000017500000000000013730472040014432 5ustar walterwaltersipp-3.6.1/docs/_static/theme_overrides.css0000664000175000017500000000344613730472040020337 0ustar walterwalter/* -*- coding: utf-8; mode: css -*- * * Sphinx HTML theme customization: read the doc * */ @media screen { /* content column * * RTD theme's default is 800px as max width for the content, but we have * tables with tons of columns, which need the full width of the view-port. */ .wy-nav-content{max-width: none; } /* table: * * - Sequences of whitespace should collapse into a single whitespace. * - make the overflow auto (scrollbar if needed) * - align caption "left" ("center" is unsuitable on vast tables) */ .wy-table-responsive table td { white-space: normal; } .wy-table-responsive { overflow: auto; } .rst-content table.docutils caption { text-align: left; font-size: 100%; } /* captions: * * - captions should have 100% (not 85%) font size * - hide the permalink symbol as long as link is not hovered */ .toc-title { font-size: 150%; font-weight: bold; } caption, .wy-table caption, .rst-content table.field-list caption { font-size: 100%; } caption a.headerlink { opacity: 0; } caption a.headerlink:hover { opacity: 1; } /* Menu selection and keystrokes */ span.menuselection { color: blue; font-family: monospace, "Courier New", Courier } code.kbd, code.kbd span { color: white; background-color: darkblue; font-weight: bold; font-family: monospace, "Courier New", Courier } /* inline literal: drop the borderbox, padding and red color */ code, .rst-content tt, .rst-content code { color: inherit; border: none; padding: unset; background: inherit; font-size: 85%; } .rst-content tt.literal,.rst-content tt.literal,.rst-content code.literal { color: inherit; } } sipp-3.6.1/docs/ooc_default.xml0000664000175000017500000000423213730472040016013 0ustar walterwalter Content-Length: 0 ]]> sipp-3.6.1/docs/branchc.xml0000664000175000017500000001104513730472040015127 0ustar walterwalter ;tag=[pid]SIPpTag07[call_number] To: ua1 Call-ID: [call_id] CSeq: 1 REGISTER Contact: sip:ua1@[local_ip]:[local_port] Content-Length: 0 Expires: 300 ]]> sipp-3.6.1/docs/sipp.rst0000664000175000017500000000622313730472040014514 0ustar walterwalterMain features ============= SIPp allows to generate one or many SIP calls to one remote system. The tool is started from the command line. In this example, two SIPp are started in front of each other to demonstrate SIPp capabilities. Run sipp with embedded server (uas) scenario:: # ./sipp -sn uas On the same host, run sipp with embedded client (uac) scenario:: # ./sipp -sn uac 127.0.0.1 Running SIPp in background `````````````````````````` SIPp can be launched in background mode (-bg command line option). By doing so, SIPp will be detached from the current terminal and run in the background. The PID of the SIPp process is provided. If you didn't specify a number of calls to execute with the -m option, SIPp will run forever. There is a mechanism implemented to stop SIPp smoothly. The command kill -SIGUSR1 [SIPp_PID] will instruct SIPp to stop placing any new calls and finish all ongoing calls before exiting. When using the background mode, the main sipp instance stops and a child process will continue the job. Therefore, the log files names will contain another PID than the actual sipp instance PID. Screens ``````` Several screens are available to monitor SIP traffic. You can change the screen view by pressing 1 to 9 keys on the keyboard. + Key '1': Scenario screen. It displays a call flow of the scenario as well as some important informations. .. image:: sipp-03.jpg + Key '2': Statistics screen. It displays the main statistics counters. The "Cumulative" column gather all statistics, since SIPp has been launched. The "Periodic" column gives the statistic value for the period considered (specified by -f frequency command line parameter). .. image:: sipp-04.jpg + Key '3': Repartition screen. It displays the distribution of response time and call length, as specified in the scenario. .. image:: sipp-05.jpg + Key '4': Variables screen. It displays informations on actions in scenario as well as scenario variable informations. .. image:: sipp-06.jpg Exit codes `````````` To ease automation of testing, upon exit (on fatal error or when the number of asked calls (-m command line option) is reached, sipp exits with one of the following exit codes: ==== =========== Code Description ==== =========== 0 All calls were successful 1 At least one call failed 97 Exit on internal command. Calls may have been processed. Also exit on global timeout (see -timeout_global option) 99 Normal exit without calls processed -1 Fatal error -2 Fatal error binding a socket ==== =========== Depending on the system that SIPp is running on, you can echo this exit code by using "echo ?" command. Contributing to SIPp ```````````````````` Of course, we welcome contributions, and many of SIPp's features (including epoll support for better Linux performance, RTP streaming, and Cygwin support) have come from external contributions. See `developers guide`_ for how to get started + Richard GAYRAUD [initial code] + Olivier JACQUES [code/documentation] + Robert Day [code/documentation] + Charles P. Wright [code] + Many contributors [code] .. _developers guide: https://github.com/SIPp/sipp/wiki/New-Developers'-Guide sipp-3.6.1/docs/media.rst0000664000175000017500000000360213730472040014616 0ustar walterwalterHandling media with SIPp ======================== SIPp is originally a signalling plane traffic generator. There is a limited support of media plane (RTP). RTP echo ```````` The "RTP echo" feature allows SIPp to listen to one or two local IP address and port (specified using -mi and -mp command line parameters) for RTP media. Everything that is received on this address/port is echoed back to the sender. RTP/UDP packets coming on this port + 2 are also echoed to their sender (used for sound and video echo). RTP streaming ````````````` SIPp can play a PCMA, PCMU, G722, iLBC or G729-encoded audio file over RTP. More details on how to do this can be found in the action reference section. PCAP Play ````````` The PCAP play feature makes use of the `PCAP library`_ to replay pre- recorded RTP streams towards a destination. RTP streams can be recorded by tools like Wireshark or ``tcpdump``. This allows you to: + Play any RTP stream (voice, video, voice+video, out of band DTMFs/:RFC:`2833`, T38 fax, ...) + Use any codec as the codec is not handled by SIPp + Emulate precisely the behavior of any SIP equipment as the pcap play will try to replay the RTP stream as it was recorded (limited to the performances of the system). + Reproduce exactly what has been captured using an IP sniffer like Wireshark. A good example is the UAC with media (uac_pcap) embedded scenario. SIPp comes with a G711 alaw pre-recorded pcap file and out of band (:RFC:`2833`) DTMFs in the pcap/ directory. .. warning:: The PCAP play feature uses pthread_setschedparam calls from pthread library. Depending on the system settings, you might need to be root to allow this. Please check "man 3 pthread_setschedparam" man page for details More details on the possible PCAP play actions can be found in the action reference section. .. _PCAP library: https://www.tcpdump.org/manpages/pcap.3pcap.html sipp-3.6.1/docs/tools.rst0000664000175000017500000000175613730472040014707 0ustar walterwalterUseful tools aside SIPp ======================= JEdit ````` `JEdit `_ is a GNU GPL text editor written in Java, and available on almost all platforms. It's extremely powerful and can be used to edit SIPp scenarios with syntax checking if you put the DTD (`sipp.dtd `_) in the same directory as your XML scenario. Wireshark/tshark ```````````````` `Wireshark `_ is a GNU GPL protocol analyzer. It was formerly known as Ethereal. It supports SIP/SDP/RTP. SIP callflow ```````````` When tracing SIP calls, it is very useful to be able to get a call flow from an wireshark trace. The "callflow" tool allows you to do that in a graphical way: `callflow `_ An equivalent exist if you want to generate HTML only call flows `http://www.iptel.org/~sipsc/`_ .. _http://www.iptel.org/~sipsc/: https://web.archive.org/web/20120106005622/https://www.iptel.org/~sipsc/ sipp-3.6.1/docs/3pcc-C-A.xml0000664000175000017500000001144613730472040014722 0ustar walterwalter ;tag=[pid]SIPpTag03[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag03[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test [$2] ]]> ;tag=[pid]SIPpTag03[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.6.1/docs/conf.py0000664000175000017500000001145313730472040014307 0ustar walterwalter# -*- coding: utf-8 -*- # # SIPp documentation build configuration file, created by # sphinx-quickstart on Tue Dec 27 16:20:26 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['rstFlatTable'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'SIPp' copyright = u'2019, SIPp community' author = u'SIPp developer community' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = u'3.6' # The full version, including alpha/beta/rc tags. release = u'3.6' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # #html_theme = 'alabaster' html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] def setup(app): app.add_stylesheet('theme_overrides.css') #html_context = { # 'css_files': [ # '_static/theme_overrides.css', # ], #} # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'SIPpdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'SIPp.tex', u'SIPp Documentation', u'SIPp community', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'sipp', u'SIPp Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'SIPp', u'SIPp Documentation', author, 'SIPp', 'One line description of project.', 'Miscellaneous'), ] highlight_language = 'XML' sipp-3.6.1/docs/sipp-06.jpg0000664000175000017500000012333413730472040014712 0ustar walterwalterÿØÿàJFIFHHÿÛC  !"$"$ÿÛCÿÀmK"ÿÄ ÿÄR  !"1AU”Ñ#25QRqs78Tau‘“²³´$3BdÒ±4r%E‚„Á6CDFGSb¢ÃÿÄÿÄ= !1S‘4AQaq¡²5Rr±Á3"2ðBÑ#Cb’ñÿÚ ?ªë¾Ñnzî÷"[?Ý!ìÁ„àÚÛmÁ Sy).aG*É=H-™(i!J‹ óq¶P†Ç$«d„ôÉʇë5[¶:Ü;“J‹l2\PHÉ!)ÉÇî««7„Üä·rå &LîBß$nR] . ($dmJÔ¹(¯`ËyBÃ7n9š£¦šiÆgˆìÃÎuéÖàn÷+L£{š«Ÿí‰þé™Á¯&ꆈ€Í¹öå´ë¬ºÃl8‚RR¡”ç+‰w~¿ø8~Êߺ²][·H’´È…"2Ý 5­ejXIê®>}Noú3³{-ás÷?V"涤4òÔÍ(½¹]O¼1“±rÊ“gtãï´M?Ý1Ó„áÂÇÚo:λÏtª*Œ"˼5øá½Î»ßýœ?eoÝN÷ÿgÙ[÷U¶í¥,l°fß©nW)šN>¢aÜiž2Õ†Ü`¤¶”$…;ÅèH;•[íýŒ[—`·Ï¸Ipvð-whí<$ð×ÝüéÚ¢Ûa Ú’œ2Fôê)RkbÓ.\lèŠëœ"qêׇ_n^8c†0ÇNG¼UTѦ0ëír>÷ÿgÙ[÷S½ÿÙÃöVýÕÔÆšÒ×þÌ4½é›d›Txún÷{} ºÓ’dˆÒ£a·á#q-­Ä¤íð'¢‚HTõ»²=;]ăåq!­@ý™âûŸ ¶­-(uµ ¤„É^å Ãg@5§9.TÓW0ª8Z&>™Ã«¶~B¼LÆbpÓoý8w{ÿ³‡ì­û©Þÿìáû+~꿦էÁiWtNo³öµs’œ”ÚÛ+=TÀd4“‚¬/‰Ó#ÂqXnÚ>kʘ¬ÚeOˆÔâ§ ¹ŽDfK¥Ý…Âßf¤„7‡])u5³F\¹Uþº»|<4÷1U‘¯Îzõöº;Ô~÷ÿgÙ[÷S½ÿÙÃöVýÕ٬ݜhö5Ý’ )w¢›½ÉÔZÝ“§Q"Þ›R$.HKÑËd‡žC~!ú€Ü’F>Å´>˜rV¿*+zŠå§˜ì‰p£MåtÇ\E0Jð”ä+ˆ2S¿hBµ­3ŽçMUÌáêÃêѧDŒÆ½z0ÐÉNC·™ˆ™3Ûá§ÕÇ{ßýœ?eoÝN÷ÿgÙ[÷UËJè8nÍ®¦o3nZíW Å¥j_[±â·¥h %$ü¦Ò¾"âl¤Q7=/ hˆ¨N;f*{5†â™Ï¸—Vâã„éKD…¸¤çË$”ƒ!g•.–•M1:§ Ñ«Æ};¦'µäÛj"&c^˜Ó¯Ãóàƒïöpý•¿u;ßýœ?eoÝ]KiÍ,Æùxµ1ñ‡U÷XºðÒèä¢!kqèKP%n¤2§¼H X$t©û'e6ùbéëÒ%[Jµs¶×Y*,—bÔ¹I_2c%ø¥$#•Œ§wZÒ¶Î ”ÕÂ׆‰ˆÃŽÜ;7ë†ÍžD·®)Ã\õc¦5ëÜã]ïþβ·î§{ÿ³‡ì­û«¢Yt6“¸høzÁmÞ™‡'KÝoBÜ'4§¸N¶¼~V—GNRAê¯*¯kÝ×K{v×LËÓ±¯¯4SÇT6Ý Z¤¥H>2„ (dzNÝß+]/¼U:'LiޏÇñÑ=Ýí{l—mcgÆUQ½×vµ¦ˆàÄNjíœpˆÓ‰+•ÎÎÞŠªœgÏö{\¾õõh *U²Üâ$,¶Ó‘ iM©cýš*HWQá'=GNµ,½#«@ãÚôüu4H¸[ÚZR’§IýD*W³7"ãIÁRu}„œz?ñ~êèïwÅh› ‹&…·ßÐü5-ÇÞ´®IÞuw'õ!=<úÖ…ï)ÞljàSM8Äá3:#LLÇ^]³‹jÃ']í"jš§ 1ÿt8> ƒ}³ˆê—l¶¸‰ -´äNVR±抒ÔxIÏQÒ¥\Ðúà ¬Ö& Püë{KOêRTà)?¨€E]£µ¥Ü£Ei¶šÚ-¥m´„¥´‡gÀ_P®©¡…žï¤ì²¯±-ˆ}×–‹Huýœú€ ¡Ð|!` œõÀÇPÏeüí½ä鲢ʊqª&q˜œ?ãÙ:?˶qt96®—Ú---*« f#DÆ:x]¿o”iê|­oVèçã\–Óv÷T˜ó­rÙVÅmÂ’Ž£µ[OTä,ìº;áu¦¡é˜Qu•²ý*úÒ e¿—QÚ¡¹àrS´« ·`TNÓ’óºgTµ*#%7¨¡£²m•ðç…! A' óÅÍ':êÿµf¹×Fsä›+åæˆŠ±˜Ñ£TÌuéûµ±{¸Q‘2¥ÒÆ©šb"t÷ÄO‡^µÎÊÕÝ«ËHvéÃŒ®OŽß@´…õ#¡##¯ÝçVk‡húQåÉr-’ÀÊŸmEÃÇe·s;’TãM¡X Q9)Ï•rŽÒ>‡Óß„÷ó&©5yR9J»*è™àÄFЦ4aލñ`ÉwI‹ ªŠ°áLã¢'¯Ó¹ÛõUš5¾4g¯1ä8ÓIB[ÉÜá£â=OŸn«_Ç·í,j™QíÒÃñ#ÝË/¤%AhBÀVR9§Jùþ•IÏ‹Z¨‹:¬)˜Ž©ÆuxµéÍ›*+áÓiTOvýÈ×0¤YQcwR>lè Ü.j0¼È^ÃâñuOl=Ú(yPKÚ¾sˆ·¸‡a4»»…¨ËBJP¦ÐWµ)$ÆzWÏ”«yéV8ÿMF¼|û|Wózœ0ãªÕ†¾®ÏÐ{B烂v´ºËâÆr"ù‹ëÎe— Køœ>lFáävŒæ²ÑÔe™‡Yܹƒ%Rø¢ôîî2™à—3ÄùÜ/>a=|÷J²3ÇÂ.´n_9 g]½{Ýëã´ŒK$6å½Åô_þ #8ñôk©ð‡' æ¤îý©G¹X³;:Ø#Ép;1i™! ”æô©Kq´HK-A)KiEG*Q*%Uó•)^xMxcw§FƩǫ¿NŠr-jÓÛ§«¾íë}ÿQG9`Õóƒð™SÖ.îµ%)RYñü)JAÙŽ€V¸×0¸ÖÇ—©qv„%»o楈I pB–C}Ž©ÁðŠà”«ùéV8ÿMF?ïýÎõœÞ§ 8ê°ñwõv€…[¥[ް¸ˆ“ꥲ/N„É. 8]ß(T:ÙÈéXekKd¹ ‘.ø$¸†ŽÙ~o†Ò3±´(„¤n8HÀêkƒR«F{ÚQW ›½?ì~«7(®ž VµLxùþt¾€g´~ÑõD–‚Û­C,ÜÔŽY.œ¹ÂÚ±Ã*ô”àŸMoiÔÛÓ¸×x¶d½[ó’ÔÙ«u—%<ÒÚ[ Ž­Cq;¿^+çU–¹å6ÔÍ5ݨœtOžµöy,¦&‹j£÷Ñ–.Õ¥Ù߸ÜZÕfmê}v•\çÏq÷ØiD(©¥B ¬©88ç£W­ ­ë“îjY.Hº3ÀŸ!wE©éMm á¸á^å#j@ÚN01Šà´«(Î質j¦íF3y/¯"MqUµZñ××þêìê}+´ÇåÈeùšÞë)l>$2½ºâ[tg JTá PÉÁ§¢µâö€Ô[”»¤m[9«”Ä¥2g"îà’êRTöý䔌gp VHÏ9ˆÂ.ÔaáÛ¯{䙯mªßÙ«sºÂÕ¶h6÷íð/‡DuE}æð¸¬«ç6¢•¤«Aè}9­«~¾‹mqnÚõ#–ç\G N¸)È;w!@ã tÏ¢¸+$çͬÄÄØS„èžøcŒÚ³‰‰ã*Æ4ùö»ªum™1aDï„*,ÖÔF7sL%j ^ÄmIR€$’G\ÖÕ¿_E¶¸·mz‘Ës®#†§a\ ä» q:gÑ\”çͯhâ)Âzº‰Í«9®+›J±Ž¾·vF¯´·`ø¾ÝñMÙÊË€‹LwT HRÛ Ú³”$å@õ¿‡VØýcøÉ÷× ¥VŒû¶³Æh°¦1ñR¼Ù²´Ã‡iTáÚî¿l~±‰üdûéñ¶ÇëŸÆO¾¸U+'Ä ÖÊò³š—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6ØýcøÉ÷× ¥> ^¶T—®]×ãmÖ1?ŒŸ}>6Ù=aøÈ÷× ¥> ^¶T—®]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]Óãu“ÖŒ}>7Y=aøÈ÷× ¥> ^¶T·oª]öÕ®í–ˤK”K”DȈúiEÔ…Œý`T©×š+qå/ o9-ÇÔ1ÒÚIó Ž¥ý@©GõŸ:ù¶•¯mž¶¶ÓV1uUGâa±c›Öv11Es„÷Dþaô”^Ò´Í™,£L¯“žÍÁnM¸5%Õ¼ÎîT¡´„§zúäî9'ÅkŽÍTTDK»;ú–ØÔQÒÚIó Œ¥ý@©G“ç_7R±NxU?úcÿµxÏŒãŒùêdŒ‡Lcÿ’wGã O¦lÎ ·(VŽÏ­Óœ»ƒW´©HŸ)÷£¥e½¡¶Ñá@S‡9êI$‹™·\•¸ÿÒ^ÒßÕM°ê’ÚOÔ¨jPOÔ ”Yó®+ð-üæ´‡þyÚ=_jkkmÓíÆCÑ×6kéß˰§CªÝ?P =|Èô¢5¯9Jë–0‹ÝÖš¸:¦f¬tëÓŒLêënÜâ÷’¦fëoU<,1Â# ´a‡[çWnÔèÓ²!ÂìçVÚ­œTÍ›"áÇ–KHXI+ ¶”!)qÃåþ¢I8à×s›“Ä™ÿé_{jKœYÝ–j‹…¹ô;E‘â…·Ð(åõŒ‘ƒÔuð5Óéÿóšër-­—ôÓØÙEU¢#¸ÆuÌÏ[BóU­µîmíëšëª4Ì÷h~»HúO~ ßÌš¤ÕÛ´¡ô÷á=üɪMq™Ûó‹o/l7òWE§Æ¯t”¥+œH¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(¥(;ÀÁA m$µg T²p3ÿàßôWÛí@µDº=qßfEÁ֒˯‰¬…-)òU÷~³„çæŒ|?ð3üå4Ÿß3û7ëÒ¨þŠÏay› œ#X­,¸Î·Ô6«tÍ5t[BmͶ»\‡ Q_m~.‹RˆA8Ip…#Ð+àÛŸÒþ!¯N{Yü•j¯Øò¿¤ªóçô„Ä?÷®ÿ6mæÚëiVt~7›>.Ú#»öýv‘ô>žü'¿™5I«¯hÿCéïÂ{ù…R«œÎßœ[y{a·’º->5{¤¥)\â@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>œ¦“ûæfýzUÑ^jü ÿ9M'÷ÌþÍúôª?¢­j »Yü•j¯Øò¿¤ªóçô„Ä?÷¯N{Yü•j¯Øò¿¤ªóçô„Ä5è9£Ðí>èü!ï¿Ï·÷´¡ô÷á=ü©Uuíè}=øO0ªUAçoÎ-¼½°Ï’º->5{¤¥)\â@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>œ¦“ûæfýzUÑ^jü ÿ9M'÷ÌþÍúôª?¢­j »Yü•j¯Øò¿¤ªóçô„Ä5éÏk?’­UûWô•^c\þ‘ø†½4z§Ý„=÷ùãÃöýv‘ô>žü'¿™5I«¯hÿCéïÂ{ù…Rª;~qmåí†|•Ñiñ«Ý%)Jç)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JÁð3üå4Ÿß3û7ëÒ¨þŠóWàgùÊi?¾göo×¥QýlëUÚÏä«U~Ç•ý%W˜×?¤$~!ÿ½zsÚÏä«U~Ç•ý%W˜×?¤$~!¯A͇i÷Gá}þxðý¿]¤}§¿ ïæMRjíÚGÐú{ðžþdÕ& ó·çÞ^ØgÉ]Ÿ½ÒR”®q R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” ì?ÎSIýó?³~½*è¯5~œ¦“ûæfýzUÑVεP]¬þJµWìy_ÒUysúBGâû×§=¬þJµWìy_ÒUysúBGâû× æC´û£ð‡¾ÿ‡Óß„÷ó&©5ví#è}=øO2j“PyÛó‹o/l3䮋O^é)JW8)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JP)JPvŸç)¤þùŸÙ¿^•GôWš¿?ÎSIýó?³~½*è«gZ¨.Ö%Z«ö<¯é*¼Æ¹ý!#ñ zsÚÏä«U~Ç•ý%W˜×?¤$~!¯A͇i÷Gá}þxðý¿]¤}§¿ ïæMRjíÚGÐú{ðžþdÕ& ó·çÞ^ØgÉ]Ÿ½ÒR”®q R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” ì?ÎSIýó?³~½*è¯5~œ¦“ûæfýzUÑVεP]¬þJµWìy_ÒUysúBGâô絟ÉVªý+úJ¯1®HHüC^ƒš=ÓîÂûüñáû~»HúO~ßÌš¤ÕÛ´¡ô÷á=üɪMAçoÎ-¼½°Ï’º->5{¤¥)\â@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>œ¦“ûæfýzUÑ^jü ÿ9M'÷ÌþÍúôª?¢­j »Yü•j¯Øò¿¤ªóçô„Ä5éÏk?’­UûWô•^c\þ‘ø†½4z§Ý„=÷ùãÃöýv‘ô>žü'¿™5I«þ´\âé¥Ý#I“4ÿ¨ÒËŠê1…© ®Í93‘\æ´zq;ŽùÝü ¼økÅÝ󸜶6íé·fs×w¢ ó·çÞ^ØgÉ]Ÿ½Òƒ¥NG•¤S:RäXïŽDVÎY¦ï !Æð<{Öc¼ž£ NC»Î%isýŽøþçÔ¦8†šØ×M©Vc+r‡\¨mìŠç:TÄie6bÔ›EÝËžÅ!»£h`,çiá °:dq:àõNz$ÈÓ*³£Z.íÜö ][c0ƒ×‰Ó#ª±Ô!éS“åiòü…ŽøÆ×Ò§ø÷†Þ×]ÉN#'jL(îìšH•¤U:*ãØïÄNþe§/ -Ç2<#Œ§)VGA·Î‚•9Íiôâwó»øxð׋»çq9lmÛÓnÌç®ïE#ÊÒ))r,wÇ"+g,Ów†ãx=ë1ˆ^OQ„§¡ÝçAJœ+H£˜çìwÇ÷>¥1À¼4ÖÆºmJ³[”:åCh?dV8Ò4ÊlÅ©6‹»—=‹CwFÐÀYÎÓÂ,`tÈâuÁêœôzTÄ™eV`ÔkEÝ»žÄ!Ë£k`¬cqá°zàq:duV:äŸ+H¯—ä,wÆ6¾•?Ǽ4îöºîJq;TzaGpdÐAÒ§$JÒ*qìwÆâ'2Ó—†–ã™ ‹€FS”«# ÛçNkH÷§¸ïßÀÛÀÜ]ß;‰ËcnÞ›vg=wz( éS‘åiΔ¹;㑳–i»ÃHq¼õ˜Ä/'¨ÂSÐîó¤ ZEÇ?c¾?¹õ)Žᦶ5ÓjU˜ÊÜ¡×*Aû"‚•1F™M˜µ&Ñwrç±`HnèÚ 9ÚxE‚¬™N¸=Sž‰24ʬÁ¨Ö‹»w=ˆC—FÖÁXÆã `õÀâtÈê¬uzTäùZE|¿!c¾1µô©þ=á§wµ×rSˆÉÚ£Ó ;€û&’%iNЏö;ãq¿™iËÃKqÌňÀ#©ÊU‘Ðm󠃥NsZG½8Ç|îþÞ|5ÆâîùÜN[vôÛ³9ë»ÑHò´ŠgJ\‹ñÈŠÙË4Ýá¤8ÞzÌb“Ôa)ÀèwyÐAÒ§ JÒ(æ9ûñýÏ©Lp/ 5±®›R¬ÆVå¹PÚÙŽ42›1jM¢îåÏbÀÝÑ´0s´ð‹X28pz§=•–$‰%3.#îÇÊÒãN´²•¶´œ…$Ž ‚2®³¬¯ýë!Åj~L–Z}™ +á!ÉMîl¤4HLwIÞ°•îÀl9Í+¨jË™r-Ù»lM=ov%¢$öݨ›‹-A üvžP-¤ gÍÈI Ú´ óHŠŽ™Lª[N»-%Ô4àBÔŒõ Q $y>£åAŠ•9"V‘Tè«c¾7;ù–œ¼4·ÈðlXŒ0zœ¥Yß:sZG½8Ç|îþÞ|5ÆâîùÜN[vôÛ³9ë»ÑAJœ+H¦t¥È±ßˆ­œ³MÞCàx÷¬Æ!y=Fœ‡w JÒ(æ9ûñýÏ©Lp/ 5±®›R¬ÆVå¹PÚÙt©ˆÒ4ÊlÅ©6‹»—=‹CwFÐÀYÎÓÂ,`tÈâuÁêœôI‘¦Uf F´]Û¹ì@2º6¶ Æ7`+®¦GUc¨CÒ¦/R4˱R›-¢ï@X*\»£rQƒ– çr|N¹ô W^uWf>³'͸÷r¡êûïÏž˜¥0Ãà`)Õ'rK*!9܃Ð×=ŽÅžÑ:T-IMÅÔláªÕya-§#'.%·’¿4üÒ0A'È é]C²%é_»ÇrS¶©’bÜK³yU¡\Œ¦Ûm:ë n|^u—D¿m‹¬ìroIiVÆ®1×0:×âJ÷'pÛœŒùb‚•×´ÅËS¹j­Öq®1®è‡^¥Ž¥Þà ™*R›ñ¸–¶ ©E%²²€œ¦“ûæfýzUÑ^jü ÿ9M'÷ÌþÍúôª?¢­j »Yü•j¯Øò¿¤ªóçô„Ä5éÏk?’­UûWô•^c\þ‘ø†½4z§Ý„=÷ùãÃöͯX\¨:f3ji+u.!%×RÚ+H”¢‘õ’@dÕ>í]ªé.×=® ¸o­‡ÛܱÄ(¥C pAê+¦9p´YîºFñxzsH·¥ÙQùXÈxªB•6•8“ÜV’Ñ·väǹ«ôï|µw„ýÞÕ>M¡¨/È…$Ûk‚”*&çÊÖ MpTTâBÖ¬®ƒÎßœ[y{aŸ%tZ|j÷KŸ&<…ErZXuQÛZ[[¡bV ¢”“ä QÓ´ýFŒ°·[yÄ) @ZÂÝJI’Ÿ$¨tNN2q€HèÑ{E–w‚ö·Õì±1ø’ºÇd"J‹,JÊd$x’Q•ñ <NS¥ïöÚkQXçÞu,+}ÓÂÄ#¢S,áæKÊ%æ‚ÚÉl„ô9ÏúkœH(ô«Å«W@‹£‘k[×4páIŠí©´$œë¥Ý’ÝVñ‡[â7´ÖÃ7…§¦ÉY®,—è·h²ï:†k/Z"1 -Í Îd4%Ì>~Qigoî^×”ž©@ _[Ò­3¢Ù ÝÝm®JrÝm…¡ô,•µ·zT”’¤½ !@Œƒšiù±í·ûuÆ\®âÊi÷b;¡+ -« Œ( ƒçäk£y`mZ}ùWæ¡—m›-×$\-èeÅ&Cm.oD‚âÝeM%I%iQ)7±$0‰D¹LĈò$<´¶ÓM ©n-G)©$œ+f]ªT[›Ö÷‚§šB–¥59— #yÚêVP£Ž€UáwJ¾½®­'µ­?«•sn5»†©O±H”þÕ¬”¨»)Õ9¹$6T§z#÷ ëiíY%þý&é¬5|¸—(NDqf*VäÞ$g }*“’+#ròú(9í+¨iqd´Y¬w/:…ø.òšÏw5‰\Øya—óÃYhù„¼¢w–Ó»»[éø}ŸÉÓ 5$a‰L#|7ÜÒµ,¶û¨L´4‡@R*iâ‚Ò­©JB…Ó:u²ãqŒÛKnB•—Е¡ Xm*$)Cr’ H8Ü3ŒŠÖˆÂåJf3ji+uiBK®¥´Nå(„¤}d™5Ð~8ÙÑWCónì6†!G·¢ÞÓ‘£<ÒØZœC¥Ð¤‡”Á[(S™W¡$ó艎©L¦[®µ­!Õ´ØZÒŒõ)I) yF~±çA'©ôÕçMJå®ñši{ÖÑ,Émô%ÄÙSjRC‰Êw ÉÜ’@Ü3].ïÚ¦GiɽƵâÄÜÙsZ`´¤<’Œ.B‡GŠ•Ô68ØË))á6F¿²žÐ}3nmÆ]‘ø-Î+’œq.%EÎó*l›”ëšÜ^¹¤—Ý@ð¤¯*VÐ:nV<²qWÞÏuͯLÚôÜ~ô¾0¨W·n7cÂiHqµ% Ú‹Àîà °“²C©ÉNRàs™l.,§£8¦”¶–¤(´ê\A àíRIJ‡ÔA ùƒX«¥Ûµ¾Ÿ‡Ùü0ÓRF”Â7ÃqmÍ+RËoº„ËCHt ¦ž(-!AJÚ”§Fñ¬ ¿Ù¬m5}Ýo% %l¡ ‹m!D)JÚp= ´¥+)[·… •Òõ.·²Ý>/Äruò\3YvR ~<4`‘]ûXÎÔ¥-6‘¹;•‰;θÓW«½†Dw.nÝc¿!ƒ2SEµ6‡Ú ´ê^‘1å0çÊ ¡Q!Mœ¨‡!¥v{®¸dÔÖ†%ݤޓ ÈäeÌl'‰S’–òŸO-$%N­°„-mÈ PY+R”Ùæšòë÷ªæÝ!' ?à ˜å•(†Ò•)I.ºJ” J”§TIQ9$PAÒ¤íÓx+¤>÷¹Åæx?àØFcËÚ¢~Xñ6y§Â¾§ý>u@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@­ëuâïm‹.%ºë:y¨áËi‰ m£mX )C>gë­P)JP)JP)JP)JP)JPvŸç)¤þùŸÙ¿^•GôWš¿?ÎSIýó?³~½*è«gZ¨.Ö%Z«ö<¯é*¼Æ¹ý!#ñ zsÚÏä«U~Ç•ý%W˜×?¤$~!¯A͇i÷Gá}þxðý¶õ¤®QtÔ(댇\iýª“%¸íŒz¸â’”ùzHÉÀHªçʼn©ºwc· rTǯý­l¹âÆÎ2ZB°¬-i蟭I ±ëK„ûT]5>×:Lm4ÿ øÎ©·’Â’A¸š®|oÕ&éÞÎj›· ǹ®ÈS’ovì6â‰S~‘”p¥%(<íùÅ·—¶òWE§Æ¯t£n–ùö©Î@ºA“[Xâ1%¥6â2J€# ƒ÷]³®Ì`k -¦xÕ=ÞìË£–÷ÙvTÚÖðèWoIZãµÔ'ÆúSÔ”îæ•j°kW¬ÑlL³a´>»%Åwïàj º—s…ÑŸ¬¦ÊÑÑô²!Fb ;HÃò]É’¤¡×VÛjR‰%M¡ª€!*RHjÏÒwÈ\ºb2äÈ}1Ñ ‰¬½,:¬áµGB˨VFÒ•$¯ ÁéINøÌè°ÃŸT­ü'bÍeö<s›žme´ìI ^å ‰!JÂH5'v׳n=ÌÓ¶‹g)h}·XˆòäÊeAÃDHu†°0PÙ@PÆìíFÙ;j+Ì‹Z.£!ˆo½½î,Éo$6×ËÉ$¥môÀR#)R &‚F„ÔñžŠÔ˜Q˜æ˜\––äøél0•ìã)e{PÒ”@CŠ!.6dgL®F©rÉx}Ûpb,‰O:ÓIxmG[û›iKi@Ú ­¤(( Xfvˆ›^ …'GÃä Ãµ›iosñøí©õÈ'Àúkå>käHJ‹b>°.뉖íÙBTWâ¾ÃSÞPìUG%.¾]^@VFíÞXè0cÑzj&¢â°nRY—Ô¥,Á.³±´å:Tˬe`,¤’‘„ïÑfÑÝ.ü™Ž‰n,D\bÀØPën­+nÎAe@§o¤ž dÓZ‘ËÇXµ[$KŽø“ [í¯áŒ-% H^ RB] @)èŸ÷cfï­.˜n™®,K\’øØÓn¡( íÎIyD«w  ¤„=L=hÙep˜ë"ä·-´ÀqmÅJÃaäø€QRÒúB Iù,’Á¨z˜zô‡í–X²­íJ]©n%*uÅ;K%…%Hj}EA[SA9«t"í—‘Ë9Û¨–Õ­èéŒRô]…埔RC!.%\E)8eA!90ÿïéÝÜÛøÇ1γÊð·mâsø;wø7oÆÿÎéS—Ô5íV53¨Œ›‚SŒ­+{s ­µ!ÖYp­-,(¡Yl’Z-äçZ鯮W;Ó—ð#JiøB ˜’eL}·Ú‡@+qõ:œ8”+ÀâFR:`¨(4NŒ¿¦ç&ŒÁeqÊÝyë”vã€êaõ8QZ䀢H xN1GÓ7Z/7 ð;§Á!…:Ð}·8­¶CŒ©iu)ÌnPÝ„œu)“ÓZúå§'\²À$þriSm*l•ø{ýKð— IYéÑ;q[µ˜Š«Òäi«EÅw•¬Ër[Ó²…:‡xaAðH m*Ü¢VzåG4áèmE/‘à7l<ü''ÇÝwˆÌ7óÖw86í²‚69ÓÀ¼hÉÓW˜Öavv3B>Ä:¤ -©öÛ^68¶B¸ˆm[‘…©!'z0Nôç#zž{zQzy,Æ-/sfR’¥?À.!Óv†¸­¥ÀrW‚Ö'rí ûrÑÈÓ~V2i„¹ÎJNh§†8!àÇ@„Œð²q¸å^*hÕ¶«™ŽÂEÂR"‡Û`:Pµ©;J“‘¸Œõè2zãÕ«»7nÏØ«e÷¾&ÜfÌa¦[ކÚC ñÖét„¥m)§#hm{ʶ€UPÒ·‡4ö£|b$ioÀ}/²Ôü>"z¤ŠI8VŒã g# Î[5ìÛS6äZ-ËríódËeÖ$9‰áºÖâé!%°„' H@!aYQÏŠwÎôîîmüc˜çYåx[¶ñ9ü»ü·ãƒçt¤}+w‘:TF°ò›ÒuŒ˜À¬e)—8JQD$(Ÿ úxU‰‡»D¹½«YÔÏAiɱâ˜Ìï¹Ü o%Gr\2x áJNöáGÓšÅg××+n£¹_ÛS§à)ÄJ™M§9) aô){°’¥8V¥…(•4ò1Ê1Þ êŽœMds.§n>Wþ!]Sáky;ÑöÓœ¶-©/qQ*Ý¥´´8î]–Ë8e°­ï(-@¥R¤—N¸mÝ»¥Kè¾Ó/SŠ»}¶Ø_viš·R_½Gn¤Gu´-¤”’–Ô’”ïXYlºÊÍÁ/µ4å͸³ Ûi‡"G’‡B]2<’©(´â–ñP©:jóÌ.ÎÆhG؇T%µ>ÛkÆÇÈW «r0µ$$ïF Þœâ¿Ú»³»Ýmþf4øMËaí›7g(q;rHØêo'¶n*råÚö壑¦&ü¬d0Ó sœ”œ6ÑO pCÁŽ ádãqʼUº÷Ÿw´Û´h›ˆÃ;÷íÆVâ·`½ÕºævïÚ:$PkZíóî³›kƒ&t·sÃb3JqÅàp”‚N'î²^¬÷{$¤Ä½Z§[d-ĵ.:ÙYA$ 2Ïê5£J !h{½ò2æ;:"É”ëÍ0XC 8ñ ARA$6@ʇoh½5QqX7),ËêR–`—YŽØÚ ò*He€V2°RÉHÂwèé ¼{%ïž— Ù‘×LWYiðÊÊaÆIJÊTÂFR|«&šÔŽX¶:ŪÙ"\wĘRßm|h ai(RBðR’èZODø—¸1³hŽîŒ—~LÇDˆ·".1`l(u·V•‡7g ² S·ÒOP!êa›¼v´d» aºdJ¸±-rKã`CMº„ 7·9%å­Þ€0:“A9?Ij\º]·ñ}ôÆ Çy·Þmõgk.6Ú”¦8P ¬%D¥CJ±'³­E.CM%ëPóžmþúˆ¶U˶—Fô8 ´t8ÀVã„…(IÝ»Y¾Ï‚ÔV­–È\´Ö'BS.ÊZa¼É%²ËN¼¶›H RvlÁâ¼53L\ãNµéËE°´‡šy¶%h’Ó¨-­µñ^Y¡KNPR¡¼r±é;…ÏVµ§PôH+l,±qа ¢‘†”]Kn¹ãBW’r:`ãFÇb¸ÞxÊ„˜Èi¡Ç¥Kj3)*ÎÔñRS¸íQ ÎHJˆIħV¦Ùªb_¢é«Ub¡ÅÚøe‡°â\Èt8â·güŬ`íÆÐœQu3QÜœÐÓ–‡-“Óª¶-rxu´©(q*rŽt.ü¡éÑ;C+U½3QjÃJ}ØÁ+Òæ *9l¨(>sádŽ"ÆJR  F[­\ÍŠéwyþx-·àÝÆ}Õ­yå?&‡×»|–Þ…I« ^Ñ.-ʶΓf´MŸîõäJx>ô§H*RÒ‡RŒe ”“O ¬* ›Òl½[½¤Å¸­·Ùe§æÖJÛ” [Í„©GüÍÄ’‘@“¦î‘lÂë'‘e’„8ràÂd”/UË•ñpB‚ÛÕ$+æœÖ+ý«»;½ÖßæcO„ܶÙ³vr‡·$Ž¡Öòq»fáÑB¤çë)²´t},ˆQ˜‚ÎÒ0ü—rA$©(uÕ¶Ú”¢IShAê J”“º÷Ÿw´Û´h›ˆÃ;÷íÆVâ·`½ÕºævïÚ:$PkZàK¹În&¸¯¹’PJR*R”¢@JRT¥(€ $MYošãeª$Uñ]›kÕ%MGf8ã8Ñ%âál´T„”;¸‡ÀÊ€5ëÒEžæ‰ÑÐÓ„!ÆœmÐJiÄ)·V ) RII È à‹T>Ò®Vù_´Yí–¤C„ä7æ7Åaoqö)Î?8t•…¤œ”’Sá Óš6ãuÕnX$;Œ0¹RzCIO ñ ÛR–”9”x’B‚TQPFV1/L:a_®¤´ü+JÊ‘"2œ_Ë6ØZ›KÅA³Ä6ø‰Ü@‚T6[ÖÒ¾7ÍÔÒm0gH™È«f\‰n¡([<µ=Å$¶TŸÎ7c ێǪãÚ£Ýc§JXäµsÊKîKð3ÄmIJ¯§Â•´‚ Êú`¨ƒŠ Oh½HÕ™›º ´¨R"™l-ÙYy¡»yBR¢¥–ö’âR ›+ kZN𼯳 ³±šö!Õ ImO¶Úñ±Å²ÄCjÜŒ-I ;Ñ‚w§2ð5Òâw^4ÍîkGR¾Q§·ñ¼<œ¦“ûæfýzUÑ^jü ÿ9M'÷ÌþÍúôª?¢­j »Yü•j¯Øò¿¤ªóçô„Ä5éÏk?’­UûWô•^c\þ‘ø†½4z§Ý„=÷ùãÃöýv‘ô>žü'¿™5I«·iCéïÂ{ù“TšƒÎßœ[y{aŸ%tZ|j÷IS[E¾|U=/TÚ-+JÊC2Ú”¥¨`ÄÊÓŽ¸êsÐôòÌ=+œH&#Z-îÙŒç5M¢<€…¨@q©EòSœ')d·•`cÇŽ£$uÆõïO@‡§!]EÇ“–ûZ­’ÊTòó·Æß$%@ñ-x…Í(6­0œ¹]"[™8vSèe†µà©A#„©JóòJI>€OJ¹Ý;=f4Ûr¢_Z“n–‰‹qôòî8؈È}ì%‡Ýlž†Ð\I*è …Ê}$ôQ=<ˆÉ7Û´ëÝÍw‹.BÐÛ$Â@ChKhJP€”€U£A'ª-Ñ­7Ùø“9ÖÛµü²we ž¬ºê:GE«Ë®@²ö|þ˜M‚ôíçHF¼?l`NS®ÎÒœmRb3µ¤' q㸅©=0’G­ëuâïm‹.%ºë:y¨áËi‰ m£mX )C>gë µZôu‚]Š5ñÍCsnÞ!Hzsɵ!\³í)”pK๹R`ò­ç)ÂǨôm¶Y®[5³Þ‰n‹t[NÁàm 0w«å‚¤#(NämëÄÝ”5_HÒGNµl‚Ê”ܧ堺_}m‡R€­Ë(–<)Nzg>íg­o˜‘$+“¶Ãa–X·F’ú£#„ˆXC®/ ÙáÈÆz“Ô¨Þ×ú)“ƒ2ly\¤¸¨~9Z\YSim嬶  u (nG‡%A::É›u®ÿkj%ª0a«]¹÷šSŽ”ÊqÈÍ<ápïÈܧ c©ŒŸ¨/÷ \{TûåÎ]¾6ÞWå­lµµ%)Ú‚pœ$0:ŠÊ榾ªç æÝÅØ³àÅn$y1cº–ŽA[a%D#ø’¢$Aef³5ÛñÓN[‘g:•váÇ\2© 7%AyH ƒ»Ì ädN5«µ*u=DýâLë¬,rÒg‘-Mã%8î ‰:£­AÐLYmùñTô½Sh´­+) ËjR–¡€w+N:ã©ÏCÓË0ô¥F—¡-¸³/JºN›¹‹ƒ)~Ú„"ZÖ#(ÇXDèM‹•ƒþ§6õ@ÜÕz ºÎálÒ÷&”©Sf,ºÒÐÔe¾ã¡„¥ÂT·CJJÉH€ÛÄuQؼ]Øn;l]g4ˆ«C‘Ò‰ ¥¡KR„¥N8AEj#çÚªu4è²âNÔWyQæ-.Jié®-­!!*X' !œmOÔ(,®h;WÆÝ=fgT4ô{Ì®SˆÈ!øî”¥Km‰.#†¢´€K^<>»GOé;]òõ&$Cº\ÌÙŽ²Ó Ï-až3ÈJ’Tãd)Å4H'ÂU#Xêé3¢Ï‘ªoK‡¿–}˃ªqã Ø¢¬§pèqæ<ë[ãÿ¿{û¿.}ïú6¾cæìÿ3;¾o‡Ï˧•†Ëa¶Áí]²Zàß¡D·L–å§‹p\}´©L¹æ”…„8@RT”:™.Ë¡iWô­êã¨ív‰Ý)¥!S$Jir¨ò\1Zq(KŠ1Ò¥›ÕóÎÄŒmM}gP@»‹²îjBÛrDЙEÄ)²Ò’°èPX(%8P=+U›ÅÝ›3ÖVn³›¶HXqèh°Ã‹|J@;Ið§©éUš‰9¸ÎF¸ÎZÓRoeBÞel)䩥煲¦úJÐ6 ôŒ¹Å‚;>±\Z„Ó3\¸ÎŠûÈZÉ} ¢2ÐT¢Gc‘Œäõ:6½Aµ2Û6»åÎ M>d6ˆÒÖÚPéAAp‘…Þx$yVY:§SJ³ ,Ewz؆Ä7&¸¦©ØNÜ £0>ª ] 6­/.&¢.m »$6µ¨8êeHg‰ãQÁRZI a9Îò­îÈYÓò5¢ZÕ6®ó´&·ä4—C€3DzÙBÑâùSQZS¤„¸°T@'=p hÒ‚û¥lº2mÔDuvà‰6ë‘tÜ 9À[1TóR×7É D«Àr“X­z5ÊäÚãÞöYœµ›¢eIm˜ï„“iC%¤«Œ<‹À`ƒ¸„TBµÎµT¦å«XjHm m›“ÛÒ…•$Ù”$‘éÚ>¡ZÈÕ:™[ÜN¢»¥vÔà(Mp¨) )hçÀ @N: PK¿¤ípd]ž¸êö¨Æa2m¬µ1n8ûkq±€ò[𥥅”¸ •Œ ãÄ'!èý5sÒÈš‹—4·VWr‹¹"àÓa֗ʸúT<+s%±„ ¥Ì¥Šc:§S3yzô΢»·s€ÛÓ5Àûˆ|*X;ˆð§¡?éUdcXêæKÌj›ãN£‹µh¸:•'а·pB¿Ö°¯´@'&‚NÖ»Gý4ºK{LÛ¸F›sôænRÊÊRðFä–PáÆÜšk;`µ¬#OξOu¸PæHL›rÛM>ÃKâê±ãu ÚS€V½D¨ˆÚ§SE³,mEwfØP¶Ì6渖 ÉØÜÇ#r~ºÕzñwzÌÍ•ë¬ç-‘Ö\fä,°ÚÎïPNÐ|Jêú×A'©âÁ‰¦ô¢£Bi©íÏJ• -eo/›}¤‚ Š@JYN6æsž˜Ë­Ù·[utw!Z£5p­óˆê™%ØŒºâ2V\ÚTµ¯ „Tc—ë²£ÚXø]Ï»u–ÒÛÌåÂïGÎJ†IÚIÆ2k-ÇSê+¤¨’n÷©×eÃ^øââñ”„‚|.îIjrÁÆ4;AÕ¯u ®\î’Xa½Å[CªJFI$àԜֵŽÛ ãÆæõ²ÏÃÛ·œnB¸¹ÎvðZsË9ǘÆzãZí>]Öé.é=Þ4¹­÷ÜڽŨ©G2Iè+Vƒ,¶Ì§™nCRPÚÔ”¼ÐPC€'p Áóý`UÎߣ¬-îÏj›pUkzl×›µ!ÄÅu·P×/þxÞ¢µ£ê :Ê”V¢Š=mw„þëî®zOwñùŽWŠ®nÞ&ÌãvÞ›±œt µLСåù»»K•+3$Ã_ ,º¶Ò’”ñxäåÖÎVÊA%+P(+ÕÕv[š› Fº»zyh‹4E~˜lÇu”<ê’îà爖ÉTp+)?uwu÷WÆ›çwð9~W¼àð¶íáìÝ»zmÆ1Ò±ÜuN¦¹J‰.㨮ó$B_#¯ÍqŰ¼ƒ¹’Rr”œŒyª‚Ë©-6»ž¬ƒh¶Z`Ø£¢ÐÕÎS±”ûËR K{ uÕR È Wú†ÉÓúmýmÙÔlµ"ÝxDQ0£Œß1ºs¬¨¨)d¶áB­¡A[ &£qÕ:šå*$»Ž¢»Ì‘ |HŽ¿5ÇÂòäIIÊRr1ä>ªÈþ¯Õ2n–ë¤ÝCs.Øø~“$*GÀ¤«)<ÒœŒ`àfƒkF³nº_îKµF,;k¸¾ËIqИ®7×›-ù;TØYX œäõ´ÄX2ôÞ«T˜M;"%¹™Qd¬-•ól4 PI KÊÎà|†1×:ÖíO¨­r¥É´^§ZW1{ä sÆ*rHðµµ ÊÀ8V&ï×dÇ»0eñ{ão>ëͥǞÃÞ®(Œ¬¸œàPFR” R” R” R” R” ì?ÎSIýó?³~½*è¯5~œ¦“ûæfýzUÑVεP]¬þJµWìy_ÒUysúBGâô絟ÉVªý+úJ¯1®HÈüCÿzôÑèvŸt~÷ßçÛõÚGÐú{ðžþdÕ&®Ý¤}§¿ ïæMRj;~qmåí†|•Ñiñ«Ý%LYdi–¢©7«EÞd‚²R¸—Fã #¥L8IÎzäyŽ2aé\âA1F™M˜µ&Ñwrç±`HnèÚ 9ÚxE‚¬™N¸=Sž›×¹ziÍ9 „EãÞÒÂ’â6c2Ÿ›„¸•nã(#ÂT„3……(—ó¼Ök,˜ò"¸’쬡¸‚’P´…%X>‚•¤}4:~,y×ût)n´Ìyši×B¥€Jœ)P@õQJ±çƒŒWKÔškE36Í63ö†Ðêå3"W&Cju,&d…¡·ËksŠ„¥ —óÕÉ©AÔ ªÄÄë¶NÑÏ“h@ŠÙ¾)ø‹™aJ!ñ$¥‚Y ÈS ’ЀáBôt]º"¯ú‚+£M6ƒ J _ dÅyÈÏ2ÂÖúÉ.´¢x¨ |â•nU ˜òmça×Ç”„ARS¹Dy ÊHÉô¨M#0¹ÛSI! Y.:” %%GªˆÀ8dàI‚ñ£mtÅ,ÞShyh¸¸ÅÝçîa …!°‰±Ô¦C‡/œ$< ¼'Ä7ÉÆ°é™Ök,AOD›6Ã"D¹Ÿ›IfS\BÂBöÖÜw1É^A VÄ´[Y¾”ù¼ÿý´ðùžïøÃ;·w‹ËÄáîônÛÅÛž¸ßMlö‘jlUÙwKdçaZùƒˆòÜmBL•¡mÅ+hiMàNAéUUZg&ÀÝõM´˜JTD,¾êu(JÔyÞ@ NU£pɶ¯ZjógŠ™7Í!a§r[uqÜ ÛÉBŠ™s¯*ð,c¬¿küÿÇcÞ¼Ïxw]³šæwq¸¼ƒ÷îë»vsž¹Îj3EÉÓñn޹©"s1 !³aÍÉÁÚ‰ Ów]äüNr4o¶™ÖKší×ÚD„!·>Iô<‚‡—¤­¥@¥I ‚|ëF‚OT=i‘}õ޽[x-ðTÖ< +§]#ÅŸ7ÿ§¹öYªu%›KjV­:‰«b"Eá²¹¬°\”dFRŠP² Ç‚ÂÆÜ(c)ÇÊa\æ¬Ú[Bj}M³BŒóòÂK³ã°¥8 IÀK‹I>'™N@Æ\Hó PNYפ•£…þN™¶:íµ…Á•OLÿ-ƒÒ´¼6e ¢”¿ à%%Ö¸{:òͦ¡Ù\’ͪÙjSö¸"p.Å÷×)æ˜[Œð Š[Ll[ËÄîÜ‘ò›T”U ëÅÝë36W®³œ¶GYq˜k²Ãk;¼IA;Añ+¨ê?]/W‹½îReÞ®³®R€Ú]—!o, BAQ'$ãõš ÇiÖ ÀÄ0ãN‘)-°órY+~9BÎ÷›æp8v ç‚ÀXR)Juµ»7×µ–žKˆòl6§-ɘ˜¨m „Ò·6ëŠJPÌGvÞà­Ž!E*ƒ‚PqAÓæXôR5Þ”e­„©Knè…]n"c™^äN}hp8r§R€”(…DiøYýo&æ²ÞÄXXˆËWÊfSÛ’Ayþe¶Ô®–IC¬£z ÏÉ«žÒƒ¥éÈÁí†dm#Ì…¦×<Â^C®ÕmuXiL¸èèá!8µ´kg²Pö—ìóPMïy,)Zrh·vÙp>#ÈHuQÊ·<Ðqqw§PH'z[R+œØ­3¯w4[­Í´¹ CŽ|«èe ¡N-JZÈJ@JTI$yT£F_î±mr`³Ä]e* «”t-Ç’2QµN$üßœw žœ…—NZ4½Þ,) Nžˆa•B%\ÖÂûÑ!òÂÒèÚT”°J•†—6àd_[WhíWIv¹ípeÃ}l>Þà­Ž!E*ƒ‚PqZ´di•YƒQ­vî{ ‡.­‚±Ç„ ÁëÄé‘ÕXë«§Ú¶½·3zìkc’šLÇš[l•€µ'¡ê’:¸Ö´˜ò"¸’쬡¸‚’P´…%X>‚•¤}5Šƒ¯i„F‰i§mºjÆÔ‹]щÆÙ«YáÍiÈÛeEÉ¥ â”àœ‚T· Z6‹ŽzíkŸs6ˆ¨¸Z!6æ.(u†å‡ö†—ºRØ1Èw>ƒÄÈã…\Ò4yœ-Æa×–· [AQBJ”¬@JI'Ð>ŠÅAѧLµ¨oŒÚáÚ'¼Òãrpî·fØ‹Â[jT…¥Öä„’—8iB8ëPBÕ»z’¥&ÃlnÁ?N5⋪‹6ôõªÍ6þƒ+Jåøh2S% OÊ!a åjð$ñSɬö™×w$·¶œ\h®Ëq+} ži*YHQÈH'jr¬qÐÖF±MÔˆìnë >©jÏ£nu0·>ˆÅçxqÃÆ|€æ²“ŽâHÀØIÆ+g´†ï.k‹eÎÍ«ÎL[z~Ýsnd…Kj+_VV¥‡8 €OU”“çU ¦uöóÑlm§&Ê_ „8ú YòN土 3ÔI´h,Ý«þTugí¹Ÿ×]FXÞ°3ÆïËmÎnvðy;‚#lóÝ»s.nÏLcÁóÏHÊÞM¦r¬_RÛJ€Ü¤ÄZÃèÞ—T…- ·à…aXÚv‘œ‚(5¥ª:¥<¨:ÔrµëkJ3Ð)@$(æ@ú‡•_mîèÔiHwÉ+®Æ„ôGà¹6H~dî"8nìKû’Ði[÷T‡BJš"³§ô­ÞûLÛq¶p"õ|ɺƌ¦ÆRÅ.¸’¹hNì`’sÒ¢!–Ùqæm\eKAÄ);’O˜Ü• JHôPt¹š_J+³W$±.ÐÝá›s3Z.¬p–Ë+tœ’· FmAH pŒ»£¯"@¶]í iˆ6;t—ùWÓ: KêbPi¾*°ú’ÃIuY s*ÊT æÞ‰¥ªÓ96ïªm¤ÀrR¢!eôoS©BV ÎòZr¬m€ÎHºÚgZÛ‚äÆÚJ'ÅL¸Êmô8ÑR“œ ¤) ' $(:6µuë®¶³'Sê(×{xµ´˜è7Öä³Î"AHqHtð’䔥+Y(Ü •¼cxÞ ïi}”¬=–W+ŒF¼5?–Z.(6…¨†Âo`Q8N¹E 5Êï¶™ÖKší×ÚD„!·>Iô<‚‡—¤­¥@¥I ‚|ëFƒ¡h5[¥ö{—žFêÃé£J»7n—-ò§2—um¸ º”«r•Ã7«§ûÛâïhÜ_ü7$Ï7Êmå8ýáf8_%åÅÙ·¦ÝÛzf¨ô R” R§5•»Ø Æ›q6ή¬×XÒTàÊ“¸%§Jw!iÝŒÎzVŒ«Lè¶h7w[k’œ·[ah} %mmÞ•%$©oA€ÈP# æƒF•1/M^bêw´Ó±šï6¤<Ú$¶´4RËÜâTP€ Y*Â6«qN 黣·9¶æ9äBŠä·¸7Aiâ,¡iYK„'$¥J讞€‡¥)@¥)AØ>œ¦“ûæfýzUÑ^jü ÿ9M'÷ÌþÍúôª?¢­j »Yü•j¯Øò¿¤ªóçôŒÄ?÷¯N{Yü•j¯Øò¿¤ªóçô„Ä5è9£Ðí>èü!ï¿Ï·ë´¡ô÷á=üɪM]»HúO~ßÌš¤ÔvüâÛËÛ ù+¢ÓãWºJ˜²ê+…¢*£Dhq YY2íe/$ÑN¶¥ÓÈyœu5Jç ˆÚŠáÌm-Ç´*9BÐVå¢*ß³Ÿ–SeÌõ8;²:`Œ Ù¸ê™4ÄM9+MÀŒŒn}FC»Ê‚ÖP¥ä0 Á8e-å$—ÝUêPoi÷áſۤÜRê¡5)¥È 4Û‹-…‚­©p(ã8 'ÈŒfº^¤×ú^á6ÍqmWwî0—)µÊv*øˆmæv6êôÇÜ.0à.¡’Ê$ÎT®MJ—´YqîRšøï«äÆ—kD#rS!©‘Ê$ñÒBƒÅÇJu$%åÑ!*ÑÒºžÑn½ßÞ—¨5Sp.q_ŽêRÒ\õºÃ­—¤òBÜâ¥'‰Œ”ï$oU ”Ý«-¦"e]Ñ=ÅÉ+i†”^ãØLicŠÃjù`9‡<=ù8ÚâÈõšËg¹Þu °#Ød[ç3ÝÍ,:ò¸…‚rø.¶É{-…ãae%%Åmåô °Ë•ÞÎ-°S5¡>-Þ[«ŠP½å§YŒàVݘ•7nê:’6u´Ý3*,xúráwå"¬¦<VæÙChPñ¸§úË(¥”R3€Ô¡ZPX{B•^¤J­ÓZ›t¼ÃHZPâÙˆËK) JUÈV2bÑzšn”º;q€Þ÷\`²G7&?B¤«çGuµš:ú²t “Õ™:†û"ï-;‘·pã¼ö6¤$xÞZÖz$y¨ýCoi[…¢“RƸ½9¹rbÄ FCˆÞi쬗R2ÊSÐ+ç“þœ*½J¡mÖÛgf±îFüênqP»M¤µyI}¨¯VK QR|º Ò æRHà5[=«j&{½Véw×o“&Ú-è[-¹è±ä¡¶x²xí:²ìŒ´ãEKJW±Â7”…rjPtnӵśTX‹oŒìe¢R^n3‘œ(„ÞÅ‚Ë..S‰Kc)¤«b±)z¥ûËR[d9wtÀU¢R‰N:íDm¥6Pál(ïoÌ+nÕ 9U¥Bj÷¦¤öéU±t“Ôíì]d¹>B˜ùrê› iN•ô!]2OP3\ö” ˜²ê+…¢*£Dhq YY2íe/$ÑN¶¥ÓÈyœu5JPu ý¦"ãf’ęڅ¹÷;tkeÂ@’§G¾R‚K€,á—•ƒ·qœòT —rjM}§.·K…ÎX¹Ý’BÅÎÛn—oe¸èŒ¥?âƒ|R猥 ÞZJ—•,íåt ë3;E³;®ô¥ý3®û-–ô™ ˆç6ã~ÁÞô×”¶ÕµIÛ½ Hqg ÜEDiýoÝ­äÜçN¹Ýc\¤)ŽÇ,=nJþE–d#„‘‡m䇣-ž{J ë7û5ôù÷g&5Ût¸ÂK±l€ã!n!+}ÂTâR·-J$¨õ$ îÏu½‹MÚôÛ®J݃{vã9*±Å“òjKA)a×W¹ ùåI?(NIm;¹¥(:]»[éø}ŸÉÓ 5$a‰L#|7ÜÒµ,¶û¨L´4‡@R*iâ‚Ò­©JrÌÖZJWf®i—wSÂÜÊ#%p²ÛÐ[R–‘Í’Râ7¡„¹µÂTV¢¾'/¥T¹kí9* –ø¹Ü1{‡tbÑ*ÞȆK¹€—¸ŠQ`%â”HÚpÚxŠÆY¢Ù×zRþ™×}–‰KzL„Äs›q¿àïzkÊ[jÚ¤íÞ„¤8³…n"¹5(,×MQ5:Éñ.îÒXãªíjŽ´¶×EpÑ|VÙJU”£ [A"²@¿[®6û­»Q-Ø(Ÿ*,°ý®Úƶy°€ÂTÒPx’ |Ñäw*´ ¾èÝ_h²E,¤ÝáGjâäµÃcc¨»Ç!°˜r×¹±ÃHB†íŽÇpìB¤ãk‹#Ök-žçyÔ.Àa‘oœÏw4°ëÊâ ËàºÛ%ì¶…””—·—Ò‚ñ­µ—Æ‹+&ã}Բ奈͋{îî†Ó­4–Õ#yZ‹ŠXJÔSÃA xêÚwÙµ6¿Ñs ÂB"\îj¶Ý#K‰t –Õ² äe%ÖÚJÒSË-·–Ò8{v„rPt¶{E—ïöµ¾¯»°†&ÇuÙl†_Œ$4–÷¶¡!eͪJRšN**篑εfîΩÕHZPC×¥€©ë^Å')@xmv·‚òºs×`ªÒ‚à¥Aƒu.|Ö¢¡6‹ƒM…¡j/:ìWYCiÚ“‚Tà9V=|¯R”µÂ=˜Ú[hTr… ­ËDU¿…g?,¦Ë™êpwdtÁJPuDöksQÚn5¥6ˆ×¶.½È`4¶ †ŠÔ†c,¿óS»†0–ÁIÜS”¥­[X‘d¸Nvçx¸[¦Ìä@C)j+­„0á¿”pž*Am)RÔ´`,óJPt»¦º´ÉÕî]#ʹÄSÖ±7hÑ™Ñ8£‰)Ÿ¢ÁÜ^I ¬¤ © Vö›íÑlºÊŸp¼j¤ÙN‹’ࡹ-3+­'jž<]˜sz ARÃ…EÉ©At±]4ÊXÔâõ{Ô.ȼ!QÒúm­¼µ#™eðó…Râ(´ANN7gz¼«$ÍeÞz’å}Ô£„a¦ÚÓ¹‰+­Æœqj_‡fô'g YK IÈ(£Ò‚Ã.T{8¶ÁLÖ„ø·yn®)B÷–f0K[v`TÝ»¨èFHÙÖstÌ»%Ž5–áw‘"ÙQ%Û›a ¾ûÅ`¥õAx'n=äyUV”Ð¥A—©«tÖ§GfÝ/0Ò”8¶b2ÒÊBÒ•crŒQ–[¬«D¥IˆÔ¤. 2‘‚Aè—P¤ƒÓÌ ùŒõ5£J «¤ç®Sœ›!ë˜Ü˜Ñ›ŽØÀ£m¥)O—  œ“Ôš¹èí"ɦ˜†ìû¸•g”ì»#qÞ!€ëÍ)µr®µ¸”„”«sÈX!ÒE ”SMk­4íŸPJ¾I~sùÿð…ä®C¥Íÿâ›CªISkÜëKsr:,·²UêæîÅd…~¾-Ø‘tw¢¡¶dºã©Ë©uJq-îBBÓ„„•'aQMQéAx¹^´‹i) ÎñÜ[~ÂÜÓ ËhJzBPyÜgŠ´¤Œdçý4׺’×{ÒÖÈ-ß/—‹”9²rUÆÞÓ*u·PÒ|N%çµ'‚*ÉÚ œ€„ƒG¥F•ª­;MÖ3ؘÔËeõo†æ7nD Ú’Ü„ž^HB\#`A Æ *I;FèÛUÚÄu•ÞêÛ[!DÖYBØRL—Ü„¶Km…%¢ã‹.lÈm°JB°”æ—J)J)JÁð3üå4Ÿß3û7ëÒ¨þŠóWàgùÊi?¾göo×¥QýlëUÚÏä«U~Ç•ý%W˜×?¤$~!¯N{Yü•j¯Øò¿¤ªóçô„Ä5è9£Ðí>èü!ï¿Ï·ë´¡ô÷á=üɪMtÛ¼x2¤évîL:ôTÆ–âÒ„-ITàoÇÁ!Nxƒae=@¬hûmîøÊmæ ¦ÔݠܘÄÎ#r‡2¦JÚÔÆÀXh!Õ$íhÎ(‚¸<íùÅ·—¶òWE§Æ¯t¹Í*úî‘Ò-7™=K9ÅÁ¸ÄŽÓÖȬKiM¾Ã®ŒøIppÊT²”)$æíÉÖVÐ% ù/êµ›ŒËx·7kC€Èd3µ®®¥i$¼;TrHÙàÊùÄ‚—JºCÐ컢›¿Ê¿Á‰"DW¥ÅŒëñÐÛkq'sÁî"”ÒÂBZI(†U³e­3f…¤5–ûR¯Qí®(IiÄrá÷£–•ìp¤aÀâR¢›€R¨(T©í™­[j^¢ŽÔ‹9”„ÎC…À*8Z¾H…å • 0:ÐÛ®ºj)U›NK²A²Þ¥J&d˜î<÷3Nºß Æño)[Q!)iJu#аæ•}w³ømjv­.ê6›‹.Ñ"çhi·Ñ†RéR]à:âR?ûյ:G‡Ã’R˜Í;hÓ·­Ö4{”çƒ6éR ó6Ðñj+®¯‰²FZ)(ð\à¨c)!U¥]4…®(Q»µD‰\œ$;ÂB{ è¥:ëdŒ­ ‡–3Õ-…ìÌÓ6kŽž°É‚ûVû¬› ‰ªˆÛN-®JâºâÖ³Ã+i@P+JKI ¡R¬78°GgÖ+‹Pšfk—Ñ_y Y/¡´FZ ‚”Rã,xBr1œž¦O_èvt¤RMþ ɱår’â¡øåipeM¥·–²Ø(P%Ô4¡¹•….•aìÖ,ý éûuÎSaM¸³ö\ZÒ‡NP¤¨»#¯˜ÈÈ;ÚFÃë­l‘îíwdKëè]½¦2X\’ÈB}%­Èq$ï+Âzg!T U‡DØm×éR¸^Ú¶†€Kndõ ¾ó-àz~Swˆa*Šeåi;Zr;©™©{îEÒm¹˜&ÎÎ:φڑÆÜÚ;±¼‚¢ŸVzUÒ‡eÝÝþUþ I"½.,g_Ž€¶Û[ˆ);žq¦–ÒÒI@Ü2­”º+jz #—ä$És Süxékc½w%8Z·$tÂŽÒ~È­Z)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)J)JÁð3üå4Ÿß3û7ëÒ¨þŠóWàgùÊi?¾göo×¥QýlëUÚÏä«U~Ç•ý%W˜×?¤$~!ÿ½zsÚÏä«U~Ç•ý%W˜×?¤$~!¯A͇i÷Gá}þxðý¶õ¥Â}ª.šŸk& ¶š†ügTÛˆÉáI Œ‚GÜMTþ0_û÷¿»òçÞÿ§ókæ>nÏó3»æø|üºyU×P=iŽî–zøÇÞ–¤q›à©ÜúáK­âÇ“‰ÿ×ÈÇ÷މ:§˜36A d˜îÚž*yÍù´‹PWÌVàûCjT:ä¥È<íùÅ·—¶òWE§Æ¯t«ìêLÍåëÓ:ŠîÝÎBoLD×î mð©`î#ž„ÿ¤}U«n¼]í±eÄ·]gC59m1!m¡ô`«€¡…(`çÌýuŽè¸Nqv¸ÒcD8áµ&B^q=r´¡õÉù£®2z7gZ¦D=+çµ°F•¸½p‹SHæ–ôuðÈR›4°€r™ŽP7(ó‰  ¿Ûír-P/—8–ù;¸ñX–´2îä„«r²Èê+,mS©¢Ù–6¢»³l([fs\K Îäìn㑎¹?]_4ÖŸÓ—íìÛÅÊØosØ—$Ë“we29\á¥eÙMð÷­ÈSä8UÅNï’ƒÕpì°´-ŠT+MŒÎ› sìÞxï2ðuÂ’#¥â¤)M%°¤ ”¥¥QêOãÿ¿{û¿.}ïú6¾cæìÿ3;¾o‡Ï˧•\õe’™ÚCsšz WPÜ9Ñáê6Þj"Õ-òp¥:úšo†R²¼) RÉÆIß Ë¦í·V.{•¢yVë¤ ¬&uH¡Ö׆ß2OUº¢7¤¡ mÜ åj×:ÕR›–­a¨U!´)´:nOoJRTwdP’G§hú…k[µN¦¶Ê—.ݨ®ðäM_[¬Mqµ¾¼“¹dTr¥œùŸ®¯Uˆvî×4¼ÛdËE™qVÄ›ÂX¼¶"FAKˆiçPp)‚…))qÂJÜO ¥:=ŸXtâ']¡êw­Ü#ðyfU5—#-µL¶¥$ð€â|Kð+ +05Ž®·ó†©¾Dæ_T‡øQÅuXÜâ°¯ŽTzœRé¬uuÖ .š¦ù:#¸â1&à믔©DÞXtã Šïhñ´â§)–­!’‡Rã¦0Ÿ/r™% -ÛÊIAN㵊×kÝÝ#¥°4«¤iñá»xއ6¶Ì¤<°Êœ 9hcnå¸ PWäêM*Ì,²uÝë`BÜšâ˜F6§a;p6Œ tÀú«ýA¸ZãÚ§ß.ríñ¶ð"¿-ke­©)NÔ„á$ÐTå÷Ÿÿ¤ºWœæxésäø»¶ð¶DÏ=6ñ8ž]7nôæªV»„ûTæçÚçIƒ-¬ðߌê›q)$Hû‰©/Ž:»½;×ãMó¼8¿5Þñ¸[·p÷îÎÝÝvçëZÚ¡® öC|•²6ü…º_3ó\â9»>gÆpI1'Ù¼+\íGû±èÈaKÄ™0Ø=$-æ8ž5. ä…x‚J±õŽ®:Tøú¦øÌ¹›9—Û¸:—Ø0ê Ê¶Žƒ>CÊ´m׋½¶,¸–ë¬èq棇-¦$-´>Œµ`0¥ ùŸ®ºµè‰:ŸP74Ú al¦d¼S·µ\UnvkYq* Ž’I+^Àâ0´âq4þœ“hµÛ4ÐvÒz¸]n%ZŠ®aàZx&B†×|,¥Ôø%̸x‚ÿoµÈµ@¾\â[äîãÅbZÐË»’­È Ê@#¨¨Ê¾Ü»šå m‹jÒ°Ý‹nSSæ.cÌûª,ñIt¸‚ÐßÂZApÈJ>OfùhÑHìÕ¹Ö×v☱܉,¥ó!Eä- ”V¦ÒTè1|(Võ$)k û„û‡/ÏΓ/–a1Øãº¥ðšNv¶œŸ FN: Ö­uM`Õ­ŽÍ.Öë4‹b-í^ãI€–ï­:ä¸Á¹ ‰ Œ\+mòϧ¦ß“G ULìÞ5¶f»³E½5Ëc²’™‚d®]¤²~z‹›Ñ‚”åCÅÕ@ +;HW©] OÀÒÏëy0/0­–ö"ÂÄFZ¸¦S2žÜ’ Ïó-¶¥p”²JeÐN~MY ›J|a¾Eƒ ²…Æ£K¼±´²¶Ôd8Óáõ5ÄCœ4¶•8î³½.¨¤9Í+è='-»ß[Z-7XÚz4}BcÚ£übDVã¸ÓRZmç Ýh/–S‰êG@°‚Ýs›=ºÄçgnÌ‘O*ð"Ë,qo C®!.28ªoˆ^@S¶ð8©Þ¢’[Hx(T®—¯,Új•É,Ú­–¥?k2'ì_}rži…¸Ï¸¥´ÆÅ¼°\NíÉ)µIEAë^âN„âs=ßÝrx·px¼üž&ßFí¼-Øë™ôPT)Vm{ÿ¸9£;’7#ÿöæ<ü_ø¾kçðø6ÕÏQI¹Fí‹´ì÷m÷÷–ò-eJåVÙæšRv>áHh©p$•$ ÀäÔ«¦§Ÿ¦\Õ2ݺÃuY‹ ½o¹6ÈT¤ÇBe-K,º*x,ï ;••n§;1¾Û­Q_“1·ciæ®*yöY¾°Ô‰láá¤GØW5²J@Ch˯nRR¥/¥[ásÿôfíÄæ{¿ã íÜ///‰·Ñ»o v:ãf}Šæ˜ìöSb1]w|«¼ã4–ÓQ¸C ž J]Y´ºà©* ­*ñÚDI©°iWeÝ-“…kä ^#Ëqµ 2V„a·­¡¥7‚< a9¥FµeÔcUÇ-ÈÖKº˜2e<ØTr¶ÉoyëÀtäm+Ø[% %oY¥uNm¹½³hÆÌ¾m×_ãÄœ‰Î¸´­·å#Á's+ $jKD…f«¢“mæ~–e×dB¼!qáñ[Zå'*† œ©-¸§Z*$„¡çFà P ­+j<Ÿƒ*j5g.Im+qÀØÚ”çëØÑÔàTçÿ¥ßã=wÿ³?þ3Ëÿ‘ùßü?ë ¬Ò­W4Çg²›Šë»å]ç ¶¶šÂñRêÈ$ ¥×ITƸœóÚ!ˆ÷;Ôk”¾u Š[¸7)¾×sË´€“  !L­ ¬ðð”p”š{Jµv¢˜ÿ"¾Û®¹"M¢Ý&b–ØNéDikVì’²¢­êYÁ*Z²ÎU¿QI¹Fí‹´ì÷m÷÷–ò-eJåVÙæšRv>áHh©p$•$ ÀäÔ«¦§Ÿ¦\Õ2ݺÃuY‹ ½o¹6ÈT¤ÇBe-K,º*x,ï ;••n§;1¾Û­Q_“1·ciæ®*yöY¾°Ô‰láá¤GØW5²J@Ch˯nRR¥/¥[ásÿôfíÄæ{¿ã íÜ///‰·Ñ»o v:ãf}Šæ˜ìöSb1]w|«¼ã4–ÓQ¸C ž J]Y´ºà©* ­*ñÚDI©°iWeÝ-“…kä ^#Ëqµ 2V„a·­¡¥7‚< a9¥FµeÔcUÇ-ÈÖKº˜2e<ØTr¶ÉoyëÀtäm+Ø[% %oY¥uNm¹½³hÆÌ¾m×_ãÄœ‰Î¸´­·å#Á's+ $jKD…f‡kf:¥]S<ñÛŠñiw'ÄU¥Œ:„‡’ðM‚ær|+Æh!éVþÏ9þæÖÝÛÌñþ/ܾíÜ.r/;zíáïÝèÛ»=3S–K‹mèèM‹´f¬Ù'³t€f¡èü!ï¿Ï·ë´¡ô÷á=üɪs1ä<ÛÎ2î":)$6‚¤§rˆò”‘“éPšèz‚ó'O;¥®ñ½øíHÚ8ï3ÞãehXè£ä¡õŒƒ÷h3äêÖu<¨Žª|X¥ˆ¡iØI%YRœSÊ{V±µ dƒÔnJàó·çÞ^ØgÉ]Ÿ½Ò¥Õ†Ñ£/÷X¶¹0Y‚â.²•UÊ:ãÉ(Ú§’~oÎ;ÐÏNb.“ž¹Nrl„FC®crcFn;c¶”¥>^€2rORjˤ5< NŸ’ÄÆd½pˆû’,¥´¤%‡^alºá^AJ’¡Ô(¨à ›Š«œH*²Ø\YOFqM)m-HQiÔ¸‚AÁÚ¤’•¨‚Aó±WPÒËI@П.Ë»üY-Êi¨\FƒÎqn¤ M¶²Œ´ ]ik FÀK{!õ^®ná¡lVHWëâ݉ 'Gz*f@K®:œº—T§Þä!´-8HIRvÐSam6Ë‹SD<‚´:•7)> )9Iè¬`ã³g´Î»¹%¸ ´âãEv[‰[èlð›IRÊBˆÞBA;S•`ކ®š›Ri˽ËKO—|Ô·‡í|8óŸod¸ûK¯)`¸ó¡J p !i)!=N<5e—Ú‹™q·Ý$J¾*á ‰ìqœ´‰\THÂBWÍNt©-¨­{ (Võ ©ÊŠƒ“X-3¯·˜¶‹cm96Røl!ÇÐÐZÏ’w,„äùž¤€2H£] &ª°/´;þé>ù)«7×'."™ru¹Ð]Jžðá q!-#¦Ô»>ÕzIN»2÷7“Á1îIŒã2R½¢†%´BT¥‚~X’A($‚€¥Ú­3®Îrm)"ª\•8úh))ÎVFâT´€‘• 5µM^dÙÙ¨ÍûêPd¶—ÜmÞâ*â-´í^V””‹É‰xWKKòuÊ•"5¹«¬'9Šiµ(Maô´–Ûâpò†Ô ŠRp ±Ö±F›¦[ÑFW ¼+›èZ§%»sn¢RÒ²ZoŒ_JÈÃj)><¨ïÚØ@Aʴ΋fƒwu¶¹)Ëu¶‡Ð²VÖÝéRRJFô( …2kF¬79POgÖ+sSZzkwÒŸeX,!ÄFBŠ’Ià¬øJ°1œ‚½@¥Iꉽã}‘3½îwŽ&ßñ—mîˆqòƈôËÈIöo{‰§µyL•&2•ƌ]p“ŽI}•·Ó>$82“”©@…f²ª<„ÅnZ˜u1ÜZ›C¥b–’¤ƒäH I#Ѹ}bº^í×Sê ŒƒwÍÅl©›‚}ÙhCiRKyD¦\ ¯);Túö†“Å 85N¼&Í6ÊõãPÛm‰¸Í’Ì;JC K#oøe ºS±±X<~sç£Èe¶\y‡[D„RÐ@qJN䓿7%C#Ò’=Н5—yèXVK•÷RŽB†›kNæ$¬:·qÅ©~›Ðœ5e,$' £zù®,×Í[ÓÍFv<†âÇe1Œga6Q½ä(Êá¶âö­EIŽò‹I'r– £¦Ó9V/©m¥@nRb-aôoKªB–[ÎðB°¬m;HÎA£]Yk%óI\ ·6îdI¸±67mí 6€òyD<+, >v ‰HØp„ñŠ×f÷¨ús]Ù¯²äÎ ¤¾é†§V„ù¶RF<'æ¨ô>D+Õ½e¼]ì’•.Ëum´ÔìI ee‚RJH8ȨUÓOëx–ío&ç:uÎëÂå!Lv9aè#rWò,³!$Œ8ØCo 8}lÉYµ+Ö«¿|þççùwùþfëm6Pë·*^×ò–à[Î {”•dì f˜Ñ—ýK™´3ÔsH†×(ì-O,„¸âTJ°­¸-ª;N+ÕØšíLC­Ÿ¹Üîò™¿]Ü)q«kr Ã’È ­çP¦œ-ÈÂNß@)*R*µgÖâvvî]çP·)ȲÛAi–ËLe\ºIp+‚ï EÃÐ…lÚ.‡B¡z¼]ïr“.õur„Òì¹ ya’ ‰8É'¬Òu¦teºã%¶‘â…¹¡KZ²Ú”P RF䨠3´ã85ÒûVÔL÷z­Òï®ß&M´[ж[r;ÑcÉClñdñÚueÙiÆŠ–”¯c„o( £êyPeé½(˜ÓZvDKsÑeGX[+æßu$’’—“¤ùã¦B"d pã‘%­N`¿[ÞØqm“Ðôñ¶±ƒƒÓ>D’wIßÔs4û¬FDè9æ÷Md3ˆöþp¥%TÞ AIP )*J!IRHRT’B5½eÓW›ÄUI·Æih -6’ÛKà–ÙJÔóž$ø W$æJç«ÝMíé6˜ÐWʼnãk%jDfÊW‡P°ÙPF┓Œã*Ú oh½h‹êãü{ƒsLÈÖåY¢® wÂC‰qG1ü@nK-¤©-¡!ið©UM¦r¬_RÛJ€Ü¤ÄZÃèÞ—T…- ·à…aXÚv‘œ‚(–.H°9$)Ô[””]Ú‡^BFŸBVr@;8€7ŒÉÄ•®Î.PU5£>UÞ#¨Š½á¦™’áVݘ%ä€7nèz‚rÜfĕ٥š"3rm÷IŠ’„¸CŽ›c†áJ•×ü—v $!€+Au´Îµ·É´”OŠ™q”Ûèp-¢¥'9A;HR N H R3+õä¡ vdùK[®8ë½Tz­n8µR”µD€ ©Íg7L˲XãYny-‘UB]¹¶â ï¼V _Y‚vãÐNG•k^­švÏ©Ól7·o0š@DÉ B^)!\£òÍ¡X9<>&…ÐbE‹PA¾µmŒœN”ÂÄs[kL¦Ô•¡IiÄ(¥ÝØ[{RIR²Þ ¼5¹eMa®#6ÊT › ;BŠs»°’¬`  ’ âߨms;QÒ—$?Âh}‡$I}†¡¥M´úŸZÓµÚÂIHm²JÔÀ¸EW´Ò Ó9 Aµ\ôIH@[Gi䔥{ Rœ ¨¡Ô¤’¢¦“Ô+ ^­§ KnÖÅÑmb$‡Ýa§7¥µ,c9·ÔŒÝ<ŽÑPe.D™-ËNÎY¦ã¥m¹“ãÞ²°QÔa*ÉèvùÔ“sâGÐOÚÐï\ë£OºÞÒ8 ÇiÄ çWÉs  §ƒ×;ÆE,\‘`rHS¨¶;)( »µ¼„(Œ'>2„¬ä€vq$oÚ½i«Íž*dÜ#4„†œ ÉmÕÇp‚Co% *eÏ ¼«À± ±½q›Wf–hLˆÍÉ·Ý&*Já8mŽ…*W_ò\IØ0„n¬d¿^lŠÒMY-òÁ”™8‘¦ pŸ&§9–rîŽ%!)) ïì\£Þe"ð§W=Kâ¼ãŽñ ¥~>&ðHXPPPX$(((kyÝ'|kQÌÓî± ç›Ý5Ì|`#Ûøi””uPñŸœq[]¤M‰2ÿerv¸0žu‡ ÒëÌÆm·íÅ*Â’P‚ÎåK߯–%ëýj³=Ù6}B·‚'CŽ¥­¤.Sr’CNðÊŽ[ ”ã$‚ PS.%Û'9 k\'ÛÁ ()*%IP$)*I J’HP ‚A·¬ºjóxŠ©6øÍ-e¦Ã’[irÛ)Z‚žsÄŸaJñ cÄœÉ\õ{©½½&Ó 㘱" Ümq¤­HŒÂJðê*ÜR’qœe[A­í­`âÝ\pni™Ü«4UÄøHq.(æ?ˆ Ée´•%´$-> *©´ÎUËê[iP”˜‹X}Òꥤó¼¬+NÒ3EÅÉ$…:‹c²’‚ »PëÈBˆÂsã(JÎHgFñ™8’ µÙÅÊ ¦´gÊ»ÄuB¼4Ó2Bœ*Û³¼íÝ@0N[ŒØ’»4³BdFnM¾é1RP—qÀólpÜ)Rºÿ’âNÁ„„#p`¨!î¶™Ö¶à¹1¶’‰ñS.2›}´T¤ç('i BIÂI Fbå~¼”!NÌŸ)kuÇwªU­Ç£€ R–¢¨59¬æé™vKk-Âï"E²*¢(K·6ÂA}÷ŠÁKë ‚ðNÜz Èò­kÕ³NÙõ:m†öíæH™2A(KÅ$+‚T~Y´+'‡ÄÁ º H±j7Ö­±“‰Ò˜XŽbKmi”Ú’´)-8…»» ojI*V[ÁW† ë¡[õ ®gj:R䇸P-°ä‰/°Ô4©¶ŸSëZc¶¢ÛXI) ¶IZ“¸·ª­®ä†e]^n|ë"%Åy´³mJ”‡È":÷:Á>D’³Ðd*ƒVÕitnsÛiHRä©ÇÐØCAINr²7¥¤Œ¨•­¨Újó&ÌnÍFhÇØ·Rƒ%´¾ãhÎ÷ÉWm§jò´¤¤l^HجléPYµjˆ“&µsm¨ÅÄ-AÇS*;Ü?N ’Ò€' Î2GIÛu5¡‹4^…ÎÝh™if:AaôHæ~UN•… §šW€6¬ðLjoð….¦/ZjógŠ™7Í!a§r[uqÜ ÛÉBŠ™s¯*ð,c¬$ê+„‹0´¹Иá@[vˆ¨ Æ>Y-‡3ÐdîÉë’rjsYjÖnc‡!ÛŠ”ÜÉSåZãÃëˆCˆHW©NŸ•qJqÅ­J*…N™¾³¨FŸE¹Ùw5!7L¢âØu*Ah¨,¬¤ž•²öŠÕõk:R]¡Øw‰ (a‰KCÓ•$lZÈB‚ŠHI…‰$‘Yu½Æ"µtyv©‘® C…oeråL¸ãm^7'{jZpG˜Á¬š&ò‡;P±ßõ ͦãT¹n´£¸4¤¨©A´•)Ålꬥ+rU*‚«JRJRJRJRJRJRƒ°| ÿ9M'÷ÌþÍúôª?¢¼ÕøþršOï™ý›õéTE[:ÕAv³ù*Õ_±åIUæ5ÓéˆkÓžÖ%Z«ö<¯é*¼Æ¹ý##ñýëÐsG¡Ú}ÑøCßž^’2pR*¹ñbjnØíÂÆÜ•1ÆkÿkG[.x±³Œ…–¬+ Zz'ëRB¬zÒá>ÕMOµÎ“[M?Ã~3ªmÄd€p¤FA#î&«ŸõIºw³š†æíÈ1Ànk²䆛ݻ ¸¢Tߤe$)CÉJ;~qmåí†|•Ñiñ«Ý(Û¥¾}ªs.dÁ–Ö8ŒIiM¸Œ€FR È ýÄU«Lé EÞß`ýötUÝ.2!ÈÛnBÛ††[C‹yK/ ¶”ºÚ”HNÔ‡ ùƒu.§,ž}’ËtµÃf1MÇn÷œJ‹a§šVλ|MÈupWEda@Î$i›‹Ð_º[Ûã[ÓÄq‚ó­5%öH.¦>òµ$«r”ì^UàQ úNùÅù-ˆÍ[ä°—ãzVôm!(ê«›wv¡<…ª±-†CjË`¼ñZRÈ. ƒç«¡% oÖ‡©$jg§ÂvA¿Eq¥på(\T†ßJ÷8RÀS@£¹@ŸzÒ¬™ÌÜÁ‚¹ EziëqãÇ{‰Äi ,6B¸ÏuR¡Ä8PÂv†´ûDv4•ªúÌÇ]\ÙR¢¼ÂØ -ʲ•áIy>a8 ޾uSîñßÒV«0Ýip¥J”óë|(:·ƒ)ÂR6¥”ù•d’zyT=ÕÒ ÖÙÎB¸Ëu¼nTi-ÈläÑÆÔ¤«ÏÐNAê d²Úg^%*<Ú%.8ãÏ¡–šF@Ü·)BJR ˆÊ”:;¤–fNrD{|ksKÆØÑ”â›F ‹Zºã=Tz“Œ ÞÒ7ç´Ýå7XÑ‘!RZ+‘!’ÑWB¤©‡X8Ê~v0£Óê ‘ô­ÞDéQ6ÃÊlHUÖ2c±”¤>\á)D¢|+éáV6žÑÆ4à¾HU²3Aù,9MÅ–d¶¶6ñYZ‚·x±°¿,¤ «zËÚN µ^n×á .«C’YnDˆà­‚ÄeÔ:¢—’µ«yQR÷¯ )ÖUÍs–›eÉ5éñùÎ3Ü«îíÞ´îsås±¼‡ø ì+Ü®ºBémÓÐo¯É´*,Ø©”Úº0§Â Šlýäå';AÛÔ+* Ö“¦¯1¬Âììf„}ˆuH[Sí¶¼lql…qÚ·# RBNô`éÎDjG;‰«cö«d§c°¸Ñ&>ÚÔôvV¥­HJwpωÇR¥¤¯)P)FÙ;—hWÛ–ŽF˜›ò±ÃL%ÎrRpÛE<1Á:$g…“Ç*ñPb»h©Öíúâ§XÕŨJ± @ÞÛ‹!N%d¡äˆ[+JTè'çb ì™×ÛÌ[E±¶œ›)|6ãèh-gÉ;–Br|€ÏR@$ œ½ëW®ÖkŒB$\å76löÌ€ûÒ¿åp]-¤ž+¹ @OŒá# ÄF•¼9§µ ã#K~é}–¤ïáñÕ$ìRI°¬g96~)ßÓ»Äf䥎;áɬ¡1S»i¬¬%…a%¨)IIPIj ¥Bfß¹øÛ‡°— Ææ’ÒŠ°êœNTÚ[*.”µµ§õ”Ý=¨äÞ,p£[‘%ޱ#?% –ü'Àï>$%YGÍ%&Jݬa\¯·I¶?ßpà¼ä~‰¹}”–ÛVåËmîˆ[£«ª,m衽¦»2{jùrn—c”†®I‡ ©ÍEh´ãŠyn!ð@eÀ¤€Tµ )GhªÄÒwÉV.üaˆÊ·†}O¬§jYShp)%y Üó@ Êâ'h9« ¥=ó{žÎš´Hkºî‹DÅÈYIW Ä%m„¾êJTœ,-A@Œ TºaÍ>,V‡cº‡®¸‡K®:¥$¡âC€q )FHKŽ‚Ä^A}Ñz’Ér®0ZCHCnå©l½–\ ØòBJ™%IHteŽÝÛºVµîÑÝd°ÎLÇ^‘tŠä¥²X C(Kî2¸•’ZQ>ã#Ï' s«m2Ù´Ô-=k‡lé1ÔÔ™ a 'aOÆÂJ˜iyJR®9#q]~÷wq²X`¦¬ÈµÅr*Þ/…!ä)÷IÚ .¨³åƒÕ°[Þó؉-F\•ðÛ[ºàÞ~jv´…¬•$¤õ#ï«3š* ]¨BѧR5*<ÙMÇjá"ÕŽ*°ÊŠ( µnB”«*_¦«t’ÌÉÎHonixÛ2œShÀ¡qkW\gªRq[Ú.ïÁ«mWÙ0Ýš‹|¤J 6øh­h;’7«péÔdtÎ@CÒ” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” R” ì OÂSIÔæ_öo×¥Œ!cB‡þ•æ¯À·óšÒùåÿhõ}è§ëi[›V¢‘’R:Ö{ ·3§ í-8 ~Öÿ¥Z«ö<¯é*¼Æ¹ý##ñ z®Ü[}žjòÚ¶fÎð;F2 Hÿ±?¾¼þ¹ý!#ñ wù³aÄÝm)ÇîÂ"óiö‰îý¶õ¤ffEÓQä\#[š[Oî“%.)´`ƒÔ6…«®1Ñ'©ÀɪçqÛ;Ó“øãcàp8¼çgvìpñÀß»s·n?Õž•5ÚGÐú{ðžþdÕ&¹ÌíùÅ·—¶y+¢ÓãWºS‘ìvÇgJ޽cca¦6pä¸ÌÂÜÃ'`KCo‘Þ”õòÈëH;džc¬lp¸O©¤qÙ˜xÉÉØÂ°“è Ú®R*•Î$­÷lÆsš¦Ñ@BÔ 8Ô¢ù)Δ²[ʰ1ãÇQ’:á&ÑojÌ'7ªm$!FmJ‚¬e9S!¼§'>u½r¿]®Ô_—ø7Âá=´Çáp’”7±-„„mJÐ1Z2äH—)érßvD‡–§uÕ•-Ũä©Dõ$“’M¿²‹ ¶ùp¸¦ô¸1à&(dJšÿ¶uÄ¡+ .!%Ä'ŠðmDñÊÒNäïvk¤mÏj;*u_É"míV¤Ûä0è q¾y.© JÚP #@¬a{ 5 v ¿Û`˜ëåÎBúd–¶Û.¤¤¥Í ¸ …y‚‘õ Ù¬utiÒ§ÇÕ7ÆeÌÙ̾ÝÁÔ¸þÁ„oPVU´tòTV«U–v‹»Ëjlž÷·°ÜÇZr ócbuÏ ;šÎQ€@Îí랉.¢—ã:D{MºßpiÕÛÂê%†HC„8 Ó€>~í‹ê1PîêY+´L‡ÉÆî ’–ó’f$º)p­Âž”ÉJTv “•nÖcP_Ø‚˜ _.mDCÇK–´¶– ºØH8Ú²R|”@Îh.v§Þ¼é¸Îê ÓbÝ.1áÈ~xëm¾.v€¡!Kl¨y¦Õ´-[2…"©—ÈP#ðdZ¹Ë·»¹LÈ)—S‚´$%ÇÂTÙ>,øÆ@èJù¨/÷Þ~_.wNî9-op÷cvÝÄã8Çžb½^.÷¹I—zºÎ¹HBiv\…¼°€I Dœd“Öh7´M’&¡»½n•tîͰ¤ÉmõG.·–ZS¤9´îJv¡^$¥g8ÂNzIé­-g¾N¸-›ì–-QxiD™1˜aǰH;! §æ/§­@”Q^²Þ.öIJ—eºÎ¶ÈZ jv$…²²‚A)%$dÔ+jݪu5¶T¹víEw‡"jø’Ýbk­õäË ‚£•(äçÌýt[®‰´[,É&ãw{Q÷¼ûH‹ Þ‡ØzD~J¢âW…G‹i'v6 ¹^´=˺)»ü«ü’$Ez\Xοm¶·Rw<â)M,$!¥¤’¸e[ תu2ܸ8­EwRîH ÏQšá2RéÏŒ’0¬ô8¬P5þßk‘j|¹Ä·ÉÝNJĵ¡—w$%[”€GP1AS“ìvÈÜ¿XØæñ_KKà30pRs—½„å#Ò¹]z$Ô('$XíN‹ÆÆûOïâIm™¸ûFFð¦Žï!±*ëçÖÇlïNOãÀâóœœÛ±ÃÇ~ìuÎݸÿVzT(6®‘™‡9Èñî®-#dÆK‰myô! éœuHê20j_HÅ‚õ«TK™ ©k…hÆ-i º©QÙâx2R—T@9Nq|ª½[Ö«´ë[s›†ãIDøª‰%.0‡Ú*J±…ƒ´…!$(a@¤E›NhÛlø°œ¹ê`=.Ý*膚ƒÇÝ8|($ïOËG^­¨Û׉»1—8°GgÖ+‹Pšfk—Ñ_y Y/¡´FZ ‚”Rã,xBr1œž§.ŒÖ·0̸‘ÕÎ[f0ó/Û¤É}1—ÅFŬ¡§•lðäçêD<«´éVh6‡\k’‚·\aa!níÞ¥) )dì@ʉÀHbƒô@G/ÈI’þæ§øñÒÖÇzîJpµnHé…¤ý‘[Ú:<Z…†îL:ôP‡\ZP…©#cjPS¿‚áGˆ6SÔ ÑŸpŸpåùùÒeòÌ&;wT¾IÎÖÓ“áHÉÂGAšZîíS›Ÿk& ¶³Ã~3ªmÄdp¤FA#î&‚çzÓ–i3“.:Z¶YcÚÁùpäÎle‚¶[„´€êÃ[)!-npW‹Ni‹+ZñËeò\—­íZ×ud³*}±›m.'ˆ’œ·ÉJóR•ŒñZøÁïÞþïËŸ{þŸÍ¯˜ù»?ÌÎï›áóòéåH‚ÿoºHºÀ¾\â\$îãÊbZÐó»”­Ë*Ê€''© ²Ê°iišr=ÚÝ.ø.+¤Ø`Fµ¥m¬£‚Yl ­9 g.’0vnrRlj ûãÀ¾\ÚïL÷†ÉkÞwg‹ƒò™Ü¬îÏÎ?Y¨Ê ¥óC³jÒMÝ¿ÁçÌXòÕ¿6øB”$<^.¸…¦PÈQ%xµ6—²ÙaÛf÷ÝÍö. ·›O IŒYiÅ-@»€è.à5ŸB\ÜãeP/ýÅÜ=ùsîÐ9µòÿ;ùyÛó¼^^}|ë,S©¥8“¨®ï,JDÀ§&¸¢$!!){$ÿ˜¼Àg¥•-§]×OZ%Ü'3nM„\Xz,!ÄYäÊm­å’2TÈ*S°(T/Q#Ä”“S€ú‘_—G[ÈÉJ”±€´­9 ?4ùµqÕ:šå*$»Ž¢»Ì‘ |HŽ¿5ÇÂòäIIÊRr1ä>ªÑº\'Ýg9>é:Lénãˆü—Tã‹ÀeJ$œÜÍŽÛ ãÆæõ²ÏÃÛ·œnB¸¹ÎvðZsË9ǘÆzãfŽÙ'˜ãk.êivf2F0âv0¬$ú¶«§TŠƒ¥ÄkE½Û1œæ©´Gµ5(¾Js„å,–ò¬ xñÔdޏI´[Ú³ Íê›D‰Q€ÛRƒà«NTÈo)ÉÏ é˜zP*û#LÙ®z²=‘§Ú±!i‰ÜÀiÇÐëÂe¸·r²¤•Œ¶“©$Õ ­V wwµj·ÕFƒ6lKwwF[ˆ[¶ƒ| ­ÑÔÚ‹²P¢T:å) 7´Þž´·ÚŠèï›%ùöv&SjŽâš\…Æ^ô¶áØ ´,§jÔ'̤T`"ùŽ~L–6°¥1ÀŽ—w»ÓjU•§jO\¨n#ìšÞ‰©¯±õ;:˜Ü]•xeiqæÊ^ô§jTx¡@”€0Nq€F£á>ßÌr¤ÄæXTwø©V•ͫē”ž‡µÐ¬Ö½>ÿeÍÜîÛdwC÷¸®&Rœi†\Œd»µ{t6²–Ô‚ Ørºçµ¼õâîõ™›+×YÎ[#¬¸Ì5ÈYaµÞ$  ø•Ôõ®‚Ôt$fô¢o^ö©†—::[d¸Ôw\mM¶^)CŒÙñ¶Ûj)p¤¡KÇ|Ðȵż>åÕÔ"Ù©{R„¤ÇXä¼’¥e<,©°•‚ ³Š‡øã«»¯º¾4ß;¿Ëò½àï…·ofìmÛÓn1Ž•½S©›m ·¨®èCha´%3\(e[™HèQ%#ý'¨Å•ýÍClµÛo.¾Ì´HuÙ" aa¾+µ)Æ”à@$¥konæÊŠR½Â½­,q¬F£D»F¹°óä¸Ó¬­Mø”’‡8.8„«)'jð©$à’‘­?P_îH÷Y÷Ëœ»„m¼ OËZÞkjŠ“µdå8Q$`ô'5Šõx»Þå&]êë:å! ¥ÙròÂ$$q’N?Y œÓ–]3+F\oW«­Þˆ·ÑR˜}måBA$–O¤mÀùÛ¼ N˜¶ªÁs¹ßîÓ­Zî-B•»xŽ’â)Äü S+ JöHQV`ì·‹½’R¥Ùn³­²‚Ú‰!l¬ JIIãõ ËP_íö¹¨ËœK|Üx¬KZwrBU¹áYHäu…M$åÝ7v™›ÊȘÌG8H e€µ,øÝKª8iÌÚq¤‡8x¹ 5²¶®«æ_½Ü&½¾$cÃcÀQØ´¼œeu+my>·z£-zÇWZ ·תob5ž®¶Ú2I8JTÉ$ýäÖ·Æ ÿqw~\û£ôm|¿Îßþ^vüï—Ÿ_: öݤ i 1ÃvqŸtBfÌ’¨Y[Mqžehoü@B€-d$ )_8¸xiÅrÓÖȳ—²µµpTŽX¥|2œW ¸¿•( ðï +8 >‡uN¦v,(Žj+ºãÀ[nCiS\(Œ¶Æ¦Æp‚‘ÐŒz)qÕ:šå*$»Ž¢»Ì‘ |HŽ¿5ÇÂòäIIÊRr1ä>ª Z‚5–3Ñ»Ží&âìotÉ…Ë8˛ԅ!k”«!Da`t ŠXí°®1š¼¿mkΈ„½%]êòPP{©á´ç¦Ÿ´ýàWÀW?¤$~!¯¾õÓa®Î5jŒ KÇ¢BGU¤ù$?ôð%Ïé ˆï]öBèöŸt{QŸË·ë´¡ô÷á=üɪMw¦ìº¢ói·ßasq›µIy(â­XyPAòQýõðˆÑúsI÷p[¹>k˜ã|³ŽnÛÂÛóÔqÇËë¯=Îü³aNsÚÜ&'‡8N:0ÿžÜz»Y;/ݨ½Yäɦ®2®ã„på=¸êŽÇ%¥)ZN´¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)@¥)AØ>N%¯„®“u{¶ ËQÚ’£ ÿ :Ÿ¸WÝæDD$6n6ÒR0dzÿ:øKà[ùÍiüòÿ´z¾ó^¦"äíµ«ªŒöYKÏ0‰ JUäpÝ÷e9ÆFwî3V3‡s^ñ†ŒUír³ÍX“åCÄ%™(tà¢NÂp0“Ôã®™¯?îHHüC_jmFÕÛ³ a"ÌÍŽ-RZQ€±„Ÿ?Ô¯ýA¨ ×À7?¤$~!®÷ p¿§´ÇêÂ&Û:0ìýºçcÿý©¶þÄ•ýv*ákÿåŸþoÿñª3·~4V#I B6^h,§>~u‡‹ê[W²§Ý\Þ]ÌIÊyÅVY¦ñÆ®~ž¼|õ9» ]žU£(ÌÿŽ1ÁÃ^1TkÇ¿±F¥^ ȧÿsZý™>êþsq=Mlödû«o™õm£tºîQ«gë E*ïÍÄõ5³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜ_SÛ=™>êsq}Olödû©Ìú¶ÑºNQ«gë E*ïÍÅõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq}Olödû©Ìú¶ñºUå¶~°¤R®üÜ_SÛ=™>êsq}Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¥NQ«gë E*ïÍÅõ=³Ù“î§7ÔÖÏfOºœÏ«o¤å¶~°¤R®üÜOS[=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÅõ=³Ù“î§7ÔöÏfOºœÏ«o¥^P«gë E*ïÍÅõ5³Ù“î§7ÔÖÏfOºœÏ«o¥NQ«gë E*ïÍÄõ5³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOS[=™>êsq=Mlödû©Ìú¶ñºNQ«gë E*ïÍÅõ=³Ù“î§7ÔöÏfOºœÏ«o¥^P«gë E*ïÍÅõ=³Ù“î§7ÔöÏfOºœÏ«o¥NQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜ_SÛ=™>êsq}Olödû©Ìú¶ñºUå ¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºTå¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜ_SÛ=™>êsq}Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°¤R®üÜOSÛ=™>êsq=Olödû©Ìú¶ñºNQ«gë E*ïÍÄõ=³Ù“î§7ÔöÏfOºœÏ«o¤å¶~°µ| Ô”|%ôŠÖ ”¥RÉ$àɽ_ijýuq×"ÂÖ™ŽÑ SG¢Û'))*«>H?Ù¯òl·&®vf¶Îg<)1Âu¼‚“µiÁƒƒäH©Uö‡ªÖ¢¥Ýç(Ÿ2e8úÖÕÛ5¦Çm£Oÿÿ¶+[åuáýž°ûWVÙͧ²½K =¹Èq[³ºÛ(()ÎQÆz“„©Dõ= 5ð5Ïé ˆïVAÚ&¬ >Âo3ÒÔ†ÔÓÈœÄ+¡J†zƒõª¼áuÕ8|Ôrj~ãsþŽÊª8\,gXu`ÖÆªëáLa£ xõ¿ÿÙsipp-3.6.1/docs/master_slave.png0000664000175000017500000014217013730472040016204 0ustar walterwalter‰PNG  IHDRâf±È™msBIT|dˆ pHYs Ü Ü˲ptEXtSoftwarewww.inkscape.org›î< IDATxœìwXWÆß3³ô"HQE,Ø{ï½·ØQ“˜Ocb4&ÆÄ4M4‰±ÄÞöÞ»‚Ø@QP¥7éìÎùþ˜A"*–ù=Ï>»³÷ÎÝ•3÷´—˜ ADÓT~ÎáÏ™YMDe ÐÀûœÐ3ðGŽ1épcæW·r…’AD-|ÂÌ#߀µèØ € ̼å%Ìù€_\`€¶3s˒ίP4T/{¦ÝT kÅ’d!Ä—=ÿ»‰È˜£3DÕãËû6G—özò!@¢öß ´PísÖÚcÚ×¶00²1ž ÀÀǾ @@s_Q]fŽ|MŸCA¡¸°/íEh©  ;I/: ™0gæ S ÿ.Ýt…l„u8ažúÌ|õEסðzY;âÆ=Ûu,g%Ö¬d§_ÁÌH´T¤óR&ÇÉTK©±‰êÈÀé“’%Q'ÍóìÞ½OJ{]ùADqÚ1󵯭`ÆÌƒr¼æ À@cÈÆ7€!3§æx€?˜yÝkZ~¾´k×N•ªcá¨Qq9*+Ö°^i®ç­EàLHR´Š4Ɉ öððH+í%"êy· `#3ß%¢Îf2sí{ºhÙXy0ó>"j <3ïѾG0ÀfN&¢ÁZ°Œ™óœ<ïZjèÙ{´ònõwÝü ùF·€¡$kŒfæ…ÌY Àç=W0ð€uÚñ5œÀ¼™ÙZ;®2€1 ìeæ3Ú×°†¼‹ÞÈÌ)…}.…üy);â=8”ÑGth^¦‹ƒ­~kKÁÌD„޽ŒéßyÒÒ$Ä?Ñ ¼fFê ôÞ‡–}ú¬{Sq>´0:ç Ì <Q¹g‘.ä Éý×±ÀçáÚýƒÊ‚U'Q s» F嬬MlutEƒÒ\ÓÛŠZ-eÄE$‡G„%†'“Å“¦Ýz^<¼Ã·´×Uˆ¨'€9~‚†ñ&¢ŠÏ¼g,€¯üÀ À­‘ °œˆ0³rxf83ÿADh`1€Î.Q=fÎ(`-u°²>هȀ¼k5p²á¼ À@E Ÿ™K €Ilü *3ÇQ'j ‡“>|Ãc|í9–C'm!¢É̼ƒ™;QSÌ&¢-o>îòu+JÒÑÑ;–µ2rñØsgY“C.ÜáSÚkË mÐQût1€?TƒlT¢!»j¯?3쀟´®èLÆNi­0ŒˆNè Ù5í @p2‡‘SAÞ™DmÈyfÞ«]sÎq5œËñÜó9s¹ù†Àˆ–X_÷¸–jQwís3¼ÔÀg]æ[!»©ŠI‰·®¢òv–ºU*•×WŒðKÀØP„•ŽPÅÁÀQÔv¥½žÂÐÞaû@veCDDtKSËâfžÍÌs™Ù™Ó_ëbµ¸º~¬AèV¿eÅffÕšvtD¥êŠ~ è¨PÑÉ -ºTF™²úv:8v‡„¶®—)íµ=ÃtÈ.ä¶Ýµe!»œ­!»qÇA6`9Yù&²3×pÈNHÝ Ùõ<rì8@,äxkfvdfGÈž  …¬-òM€ˆºQ³gÞs€kŽçùîB™Y­u#·0@w‰¨G!kÈ"Àê믠Ÿv]lp€33OQÜÒ/ÆKð! &F†¢…‰Qþ Òûß¡Š+Ö^Îs®î7ˆ}>Øü™Õ?Á©³^Šõ7i¯x/¦ÈsÝ Œ¦ÑŸìz%Yá&F"ʉ$H&¯bþWÀ,³ˆ¨ɘCv“™BŽ/½YØÅÙJ»JeÚ:Õ´„q%7ëe£g BõÖ°´1nbd¬g¢«‹Š…z}0s(3ßÖ>¢ »T·A6Ä·¤ái¥@à1€D"êÙm /råÀ\È»c@ÞA_0ˆl‰¨äxoa¿ë­> ¢zDTòŽ=æ™÷œàHDÓ´ eßá3_cæ±\Ü)ìýZ6CD‰È rb×(í±9\˜y13¿p·ÂK1Ä“ø$ ßÐêWr¹[·#éÔÙt?8ö…çþ{éáôù’âë¾á¤«#ÂÙÉ¢ÈñØjΖlh¨ÃW®?z%nâü6o"Ì|À§oäy™ùK8S©5¶ål -D•`deg\ÚËyg±´5`ë`RA#Ioºwg#€C €ÅÌ|@€¬ÿh yØr’’&ÇK!Þc@¶·hä¬fÈ.ÜÌü°µ¬°²A_`3@Þ)_ÖÎyw]²+|€¨¢|PfŽfæ»Ú§$kÿ}UûyӜ׾wdïÁÏãß¾ÃJ`æ‹ozËÛÂkÉJiXÏŽïÜ¡ãÉÑÞŒ`Ë_êÖ¹*ß |ú·sÑ;„  „Ä44jPžG ©Ë‚@ˆˆLÂòµ—…Ȩd¸Ö·ã‘Ôã»÷cÉër(éé©à~ê>µoS™<Œ§5¯PRrzw«ÎmZ:2lÝéKU*—Ŷ]~Ô·G nÑÔ>×Ï7?æ|×A¿[öø ’’2pöb0ulëÄÚTæ%+½„ä” ŒZOr¨høpx}žöý1áľ19Œï4Ìlþœ×7جMPIÏévÖÖ ¿17ÌTƼœ‘• ƦÊnøU¡R 00Ò‰¹3Þ4×t.´å7í#çëÞ¼µÿ¾``sœÐë™×"|økùGûÈùúU€ˆœ fæÑÚçãÛ-ÌÌäø÷9õËñúZÈ1s…WÄkIoz»HÛwûe_Œwí¿Eû¸HYÏ=ÏÑ€‘[ÅJf¨SÓ³~u—®ò ßð-bRrÚ¶¬Ä+Öù?þî!èpS}”)£²æ Ž£f—‹ £’ƒ9F~¼Sܺӗ`õ†+¨w Ì@rJîªØ¸T\÷ §F Ê3ø\{„ÑïOœºGŽöf9a‡ØmÀz1-]'I2Æ-ÛݨAy¾q3œ¢¢“¡ Ã̉¥û-*Ìto i$\8zVü´C¸xì:\{êy™>ø/q@µÉâ€j“Å”¤7«TvãüýÂ,Òïú-þv‹ða³ïÄ?íÈ5F©Á€j“Åþa%º""ˆÀÂs#VÚQ"ZûœÇœ¢Ì¡Å¶"""Ú à{íCá-äµÕi îW‹'O?$|ý¿–¸|5ŒíÍaea”}¼Œ©>¶¯¢iÞÄž335¸æŽ›þòn9èAÝ»t¬Â-›Ùk’S2©ByST«jÉ&ƺ¨WÇ–ÿ÷Í!¡g×jÒÏßË;[›rÆüËŸ§„ÔÖÀ¨¡õxú”VÒ³ëò½A•ÍsíË”Ñç¥ zIpÌý•·3åo§¶–$‰a`ý“*.>æf "Tr0ç[·£¨MK£—î¢ ¼YˆêBNø`È.®Âþ]Zï{kænÔåÙœÜ03F6š!&Ä>!‡ªv¼ú—]‚½³-o󛯀á_ö”®Ÿ»MµA4yþ¤JøèD,ùn«0}ñ¸"-êЦ3ä{1€â¢ ÎÌíÔQ鈰«d…_®þö½ñø¼&"Ì~αçÖ? 3·ÓÖ:[ðÕÖ0+¼…¼6CܼIEŽŒN¦{A±´u§}0 V®‹…£ƒ7ç¤0ñËýBF†*•€feò–Õƒ4Óg>ÿê ª}›Êüã·í$ ·‡ôN`4]½þ˜NxÞ€ÐQ ÙÆ1Ë%þ,w£ÉÆ:wîD•ÊÙÿ65ÕCý:rÕ€ T¢€ôô§×%[kܽCYnð—IfFºGÈ[7‚ìÁ(ìߥõ¾·fî€+gÿ­^¿ÿs¿wÏ[ôèA$ _¡ÖÑUaÿZOÚöï‘ìcí¦ÎlRÆ•7ùõìÁ+ä¹Ç›â£©fã*Ü}dkɺ‚Îì÷!¯“¾$ˆú~ÔžCïEÀë¤/‘@è=¦W©cÏþ—ïÓ¡§)ùI*ZöhÀíû7)ÖßÔ®å'…2eÑmÄÓÎäÄTœÜyQ¸tüéèªØÞÙ#¿î-éèªðéÏHðuÿ?óM:ûmi|›Ä{~!äT«¢ |I0s&ŠVw_”¹B 7Qx‹ym†˜ˆ0 wMÉm·Ó¸ëà.>xÞñŠÎ6¬ÎPcæÐÄÝë³kûZܾ“"í ¯Ÿ»Ci)éäTËž/¹N~—îŠó÷|­IIJƒÇn/A¥«â#[sjrvþw\¨Þ wù /ør°w•»Ð}DkI¥ñèEbðGÒ‡3úyË}íŒ?5ëROÒÑ}ú³^üífáÊijÙ£‡?Œ¦¥ßo£öš²cu»Bÿfë·®Áe,Œásê–bˆ^!¯µÖþµøïÅ„Z.Ö01Î$Ã̰²4d+KC\¹þˆ6¹]RÓÔDŒüx§°nóU²(kû e²Ç–1ÕÃí€h HÂðAu¤ [® ÇÜïRtL Æ~¶G\³éj¡q)×úvp·èeKÏp7†ê×µU.RïÖ,°ÜóG±™!¯üy'õ©ü¹¸æ·ÝEú­ôמ«Õ¯Äw®x”óº mÁ“~®‰J$»JåØÜÊ̌߷©qªU‘·.<,ÔkQÍ,M`ac—FUxÍo{I*úŸÖ}ÿPr¨–;9YßHqQ‰õ(Ž\9ñ»¾Ò8T+úß«½³ßyTä5(((ŸWnˆ­Ë£N-k€&®¸y{?º¡ææúp­/'IÍú¦-ÇŧQƒÖKÅ—]¢e÷Öé²( ذl€äyö5n·Lô<û€vl]j}{Ôà°G‰X´ü’гk5þó—.šÙ¿y]û¯«;[òOß¶— N-¶²4Êw}æf°´0ä à8kÔ¬Q.ûBåRÍŠílL²Ÿ·jîÀººòŽ9ð^ YYqYs¥5ñ»Ä™ý>õ(³VM”ö-ÖüµÿÍÆùû…Ôä‚sÐ’R0ªÑ ñâ±ëT½A%.ca,ÇH´tÒœ-mÌxóß…•?ïzi+•+_©Éé4ã’pí\{@ƦhÔ®&§#ÌÀPŸŸMë>¢ÿ¹ë+u…²|öÀúaô¢b%`¥&§ÁÈTùûVPx•¼r×tǶNܱ­SöÕh÷æ¡Ù.¾uíxÍ’~ •ÌyíÒ~¹Ü½»Wg¨_×6û}9©Y£{›=fäõxäõò¸³ÜÕÏãÛ©­¥–^¤¿ïÆ]:Tá.ªdŸëûims=²kTöü ÿ»H3¾l¥ì†ß1à-A£Ö`@õÉbûMù‹¹ÃK´‹•$Fÿª_ˆíú5æÿý1ò…ç:{ø>n_‰8ès:t³×Q·‚‡^/l`3€ ¥½…÷šÃ”±–¬nYEÅÆÚëRÞD•ˆ9¿Bï½Á.ftþ 'kû­áñ"€-Ðêe+(”"UlR ±‚Â;Dí¦Î\»©s‰çD!»ÎøÄŠVxs¨öZË—róÚ qf¦}>Ø,úÞŒÈÎ>IJÎÀÒU^BË.+ÅÑŸìoDe»éI“§š´_&~ÿóIáaHBóoÜv¦Ï>žëóøÝŠ ¾C7‹MÚ/çÌóÒÓåp’ÄX²ÒKhßs8êã]âùKŸ›³j½Ð¶ûj±K¿õâÙ ÁÏ}_¿a[Än6d?޹ß-qoÝ;WÎw ¢CD4˜ˆÞÞ¼‚‚‚‚Âsy-†82*ƒFm½CéOÛ¡~ôùñб@Z³¤ŸÔ½sU©ÏÐÍBB¢\9|ü¡š³öm®!"|þ€p"ZJDMJ:¯‚‚‚‚›Ãk1Ä-:¯Ö·cG‡§Æ„}‡nÓÓÛJÎN<¤-¶)gŒSgPHhÖ³ã‰5–¬ËcÆ—­$3÷)6.5ÏÜ¿üyZزã}4ªa®xÖú-ׄmxèÀÚ\±B,ý«—¦qC¹yˆûiY6±²£9wéP…ŒtÙË'4Ïvñr/áËÏ›KukÛð¨¡õ¸c»*¼u§ožïìêõÇhT¿±xŠ\)©™8qæ><Ï?ÈsL­–°qç ,^ã…ðȤ\Çž$¥cÓ®X´Æ ‘ÅT€‹KHÅÞ£·áë‘ïñÌL ÜÏaÞ’s¸|ýig5FÂQÏ»X¶á2¢cónJ ÂûZæ.>‹c§Š§Öƒ…«.a÷adf¯Zôªßcü½â"ƒbò=6wñY:˜çX\B*VlòÁÁP«s§J$>IÇò>øo}ñ¿ƒçñZ ñ™#ã4ßOk+‰ÂÓÓ,- sõuÎTK¸M+”ÁªE}³?ýþ#wÈÆÚæfyKüÆj(y«q´Ïõìw+U,xòôÃB·Äž÷¨^m¹µßðAuøÈ‰@Z°è¼ðÅ´C‚­ 7¬gô0ÎNO ªV±à»÷có¬áêÇŸŠN}Ö‰-»¬7l½Vâ²°œ0³†™2óP6wȽÑQ"JDJû£—DJR¶.<,Lí3OÝäÛ<ž˜ØÈ|ÚqŽØÒh¤êËÞsňЧ?òþa4¶ùwb+㑪©}扷¼ï)DÕ¿ì>í8GünøÂ|½?!wÃiÚÀùb;óUkL“ŸÈ7¦÷o†Ò´óŶfcTÓÎïßÌ{Sù e³· _.­é9ª Ïö˜ó¤Ðq¾é·‰+…!ŸwãŸ7~¡ññ¼E;–{/?Ó3Ô¸~+O’Ò¡ÑJ`êêˆøíÛð><¡ØóõìXǶŽÄ€.yŽ%$¦aÛ¾›8á6 —L@§ÖNøê§c€ó—Cœ’‰`ïÉ8³g,¾þ´9æ/;_¤sŽÞ—ŽG­êåò=Þoì6éâ×dÞ4+þì ðº†+¾qëÔgð:4=:8cÙŸ"sõ–«7L>ïß?uŬyE·vÛ5|2ÊW‚€s“ «#bÛ^¿<ïÓ<#Gšž¡Æ’uÞØµj~èX ½Mܰ|€&g›Ì¢`b¢‡v­+ó„]¥Ö-yÖ7í¤ÍÛoLúú 0vDÞ¹á·ç'šÐ°Z±Ö'ÏwQÖÜŸ<½(>IJ‡­qžuü0½4ÿ—®’¹™Z·pä±#H«7^yåß-30óLÈR‰8NDD4S«UªPLê4¯Ê›®ÎÓ œØ9ßò+ž·¨ïøܤSžüÇHéü‘kô8XÖÎî3¶ÿ´ásMö5yЧ]øÎÕ Š /ôœ=G·áµÑ´ì‘«õì´vá_6¡iÔ¾7í\—³nL¯½M7—¶­É6•®»S¤›Às‡®k[î9º Ÿ5PRéˆðñ¼UèX3KÌ\>A6¥‡Ô¤SnÞ­Ÿ=xå•Üx¾I|:ã ÊÖ˜‹ú–Á¢æ<ô½%ûX¯NÕà\©lãÿZ~.sÿÔ{P³Z9´iæ=ݼN¬Ÿ…¹Üí¯¢)¼¯ÉnâN­àçù) ôu‘©Á…Ë¡ÐÓ-Z5js׊p­k!ŸMtl .^ Åøá °pÕ%=ŒË>feaµF² >Ø{ô6._„rÏéáŸfÆQÏ{Ù7ý»×@èãD<)üw±l^/üñ}gòN5,ü tŸù®füzåjÿœùGzº*X˜bãÎ8vêö½ +‹§]»ßÅ€5}ºVG|bnߨY›ÀçÆ#ì;vË6ú S-¡¬™Aö¸ö-+Á¼ŒtuDôê\ g/=,ôsF©Ö{û„ш!u¥Ï'ÈùGí{®»w®*À®}·hÒ´ƒâÞ-Ã4Í›ØÛÏëTÉœC%f?×Ó‘œ’ @–.lÙì霮õËsHXÞ¬ìÊŽæ¸{?ÊÍwîÆP³Æyåàþ^rAhߺשeÖ®†•¥ÑkköÁÌ€N‘)€A}—‘ IDATÆø‰ˆ<¬°‹™_N@ã=§Q‡ZìuâuÞÛ—#½A¶VüÑw$ˆ ‹Å÷#ÿštªÃ6Åk“^'}©¢“ ·2¥ªÙ¨ 1w¸T»YU€;K³Ç,·ü}o_ ¢S{©þ×÷b ÕkUƒ¹n¸nójx#˜Ú(X¹b®XEþ[?½ï2m]xXøzá‡ïjÍ1 >1 Ë6\Æüº`Ô º8x"·¢Š5G—¶U I 3Ó¢uÑ´²0B¯NU1âó]hTÏ‹×zƒH6lD] ‰iè?nü£áuhü‹|´\\ô …$1Fÿo\ëØaö|OÞ4ZVF%{s´nꀯçÈ»r•JÀ˜!õ S£aÄÄ¥ÀVÛµÐÚÊ¢( &.Ž…4RR©„ìuõ½Ý;8cp¯š¹Þ3¸wM8T0Ës#2fp=üð§Ö¹]lûoPö±ˆè$Ø–“uèÍLõa ¯BŒ6iXÿÚøö÷“è3F¾ÑjÓÌÕ«XÊ㢞ŽÛrÆÉag^”RÝ8@?Íõ’’3°qÛuÊÈÔ S;' Kʉ»Äo&·’224ð<Džg‚(9%É)ø{É!55³À¹ÇŽl í9à/xœ¢Ð°Dü¹ðœÐ¿— kñ/žâÒpÑ;„6ï¸!|4Rî¿»ÿðºtYޱÑ@š÷ÏYáºo8mÝéKç½bHÿZ ïֳʞ$‰1eÆa!ìQ"ÜOݧۮ ÃÕ.•®[ÌœÈÌ«˜¹ä®-çü 9ëzµ¢‚– eЧ]øž_Ú”£:¶õœ ”Keé®ïCú°ÙL•®žænÿ²Ä½È%‰•ˆû·Bè·­“5öUmyÁ—ë³»A·BÉÔÜöUmÙÄÜî<*Òÿ¯ 49B2Q•ox:_¶/9*|ÕÿOqØ”RŸÚ¿Ó†ØÌT¿ÛË7úÀ²æ<üü÷i´hd_¬9\ªZaê'Í!£øª}Э}Üð@›f¨lož­ú8-û®ÆÃМÞý!*Ø–¥•$IŒ–]VнCiëêÁ•JÀ¯jXÏŽwï¿E?þî!d=""“)-M½ý)=#÷õ­b…2\­ÊÓÄ*‡Šfسy˜æç?<…ƒ7ˆ®õËóìí$˜4¡©Ô²™=÷²Q\°ø‚°d~OM-Yªñ¢wÝ ”]ú¸ðþµxÄ„¶~º¥ý¥,Q/Ÿ0ò¿#×=Oü¨‘Ô½sU8j«øÓ\Oáßy=4ù%½n˜ù3ÏP @ÈÃîÑ,"r,Åå½µØWµåm~ó5+Ný¨2©›$ˆj¸Ê¡ow?×j–X­ž#/?5[c\¦h""!„ N6fLj…iŸ¶€÷µG=XÞÞð@Ó+aj¬‡ Æ¡ŠcÙ|“†rºÁ‹B gKèéª`e!»œ tð$)•kK#tië„ÎVhÕÄÂóæäwΖí±i§/˜›wû¢a»KrJF®çñ‰iHÏPc`O8V4CïÎÕ•œ=«&öؼ[^Ï–=¾p©j•mP#¢’дa¸TµBç6N°-g‚0íçlÕÄg½"84q ©8ìˆKÞÉ®Äٽͺ2¨«Åw½Ú™Wwª¨—¼ .]‚ƒ§ãŽ¿”ðÛ¥Ûϼ̹‰Èrœ²vÖ&°¶2Æ­€(ŒP+ç÷Ff¦5Ú,Ftl Ó`b¬3S}øy|ЬdÏ´t5ÊÕþÖCë¦Ùóþ´àþZ~É©™ˆ`h ƒßgvÄ„ “Œºÿ„G&á“Q®XükwƵ+7_±‘.DQ3ù’.yš0véJ(šö\‰M‹`X¿ÚÙ¯ût'¹âIR:ôõT02Ô…Û²AhÛÜðû¢³ø~ž;*˜!ôQ"ö¬ù]ÛUAL\ Úô_‹‡a °07@xdv¬Œ«fϽzëU|ôå^\;ñ êºØd¿îw;Íz­„‰±"¢’°iñ|ЧVöñÌL ʺÌÅðþuðßÜžÙ¯›Vý IÉÙßcF†?~Õ_Ú"û=_Ï9†%k½å7 †Ogƒ?ÞŽ=Gn£RE3Ü}‹ï&·Æ_µÜAãî+`h ƒðÈ$¬^Ð'ÛÍ~Ø=ƹÁÊ OÒagm‚S»Ædßœ šà†C'¡£#¢fU+œÞý!D±d{ÚLj5ÌÑ OÔa1ñêêNJjÐK!&Aˆõ#QÐ)^ª0sdû–ˆ*`€ÅD´C{ì¿Ìú«·Œ¨°XòÜãM™éjTªQ=÷xSôã8dbß tlÛyÁÖÁŠw›¢iÓ§€:CÚM«jc§>Ùþ³èÇq”Sòð‘k”ŸŒœ†ø<÷x‘¤a˜–5Æ©=Þ¤£«B–!þpF_)6"^Xõó.¡zƒJø~å' —.=>K³ð›Â¿Ó7‰Nµ*ò’cßåª0HIJÅù#רrÍ Èiˆ«ÖsäïWMÔ¬Ÿ»WÐ3ÐÅ‚½Ó4Ϻ¦ý.Ý¥çïÐØ™ý³_Ó7ÐEƒÖ.œžš Ï=Þå+—ã~ã;¼”ïÿMdh¿ZhXÇ·ïF#%5[WFyÙ¬£#b÷ª!Ù1F@®°ÈYqqìÔ= êé’ËÀg6B›fÙ‘ A Ô«)0+ #l]:÷ƒãФAyÔp¶Ê÷õ§-0¼\sè羜׫eƒM‹ [û*¹^Ï2ôYèêŠhXÇ6ûùôÏ[¢GgøÝ‰D×vU`^FNT²07„סñ8uábãSѺ©C÷rŸ.Õ`°d\r¬ji]½§.<@ë\F:ë;ܼdª9YæzýÐÆáyêx*ä>ç'#]Ѹ^ù\F¶ý7ç/‡ à~ Ô¶ÍuΪ•-àçñ)<ΡfµrhPûéçïÖÞwÎ~ŽÓƒQÖÌ­›:ÀÈP7ûø¦Åà~6)©™èÚ®J‰0ðvĺ ®æT^gB÷6e?ëÕ¶¬XƤè1&…¼<Klj‹ñ [öÇüuãÒ©í±ááU™ùÀ«<§6fÜ ò.y€hë¬cæû¯òÜ¥Á»*ƒø&ò®ìˆKÊŸÔp¶*r¢–ÂûE‰Mùå#Û„exû¦…"óÒzð?H|ø4<Ø øqzÙ ‚0. šLD[F_¾¼,ßâhfö&"WÛxiwÆû^ç™ù€sDô€~òt"ºy—ìÆÌkU*((((äá•tÖ:ïæ– ìUÌý¾ÂÌDÔ²àÃ"úÀO¯;»™™Sl°™ˆ* `*€ˆhd£|Bq]+(((÷¢Qû»3g2óç>‚·Ý£miYZë eæß˜¹:€ö!ûÔÑoDT½´ÖVLœ––œ™,IŒŒô7¾RxÌ@zj&ÒÓÔÉqñ´ß24 »ûcþçqïA^…¶‚Pd &"* ëÜ®aÛ5äìý_D…W3¯\nÔrܸÔkp˜ù"3Àò¹?"ºHDŸQÉ.¿4èñãàøGÌ¬Ž‹RÚo¿*žÄ§A­fD†%†j@K{=¯’ãÝ0rÒn8€Úí—æ’Õ+E±`.ø„À®þ|ü±ô<®ºûFYïY‘ATxå0³7€†¢ Ç{—ò’ÌœÆÌÛ˜¹{ÈÉ]“ ÷ºÞFD݈¨TsôtÓ§ghÔ ±iþ¾QÅjS¨P4˜;×"‘ž’ù(òqJŒŽ>êÍ'?ÄÐljð8ÿþ§?ƒÇŽ1XówÌ[r®Hó)2ˆãPÁ '¶‚ŸÇ§ð9ú1œ̱yWÞ6¡Š ¢B©ÁÌ‘:@.7Úóªë‹ 3?bæyÌ\ò>ÀF!D4ˆj<ëáìÞ½Oº|Ù3x_BlZâ•Ó¡HNÌ(| B‘HOUãúùPÄD$g\9âpàùýno}ÎÈód+Øš"Ò÷ëì.S§.CW'ヲ"ƒX|D;k´kQ ‡NbÖ¸…ι»o)2ˆ ¥3gøœˆ.ø¯©Þ¸¸hwðÞDô%€^³®¯Ñ5È ^[˜¹xÁµàhB§&dTôöx¸ºNsÛ~ñ1)¦æ(SV:ù\ G­–›†„ØT¤$eDøûDì‰ }rß(Ýäpi¯­¤&ƒ¨«#B­–ðñ´ýؼÛž;Çä™C‘A,¾ b[öøâà‰@˜šèÁ¹²E®cï‚ ¢bˆß˜y-ÝD)ÔfN°À"²0À'Ñ~ÈFù3—<ðRnnn×Þ½·G‡=‰=¹=1ºj=ëêe­  MôlD)-^IÙ)Iቱia·¯…ûª3¥ ]•ÞQ#kÞúD­œ2ˆ_Î> çJe1ÿ‡.ÙÇ“’30h‚¼®†áØÖ‘hÒ Bž9\ªZÁ¥ªUž× bÕ‚>X¼Æ îç‚Ц™B%æ’Aì6|#ÒÒÔ/]qÝ?ý1¼Lûù8Vo¹Š-+ç’At®T#'íÆ÷óܱ毾ΙS±fµrÅ–A€ ÿöGJj&ºØ„Yó<°ú¯>ÙÇêײEýZ¶¹ÞŸSqôàzøq'füvƒ{ËN¸,D׺vÏ•AÜðo=ŒCß±[qÔó.º´­[k\»ù4–ÊöæEþÏC1ÄïÚzã†(ÅzãâÀÌæ˜OD ï’×ÐÑ&È Cn¼ªó_Þ·/ÀþÆ=ú\¨Í€% ~ƒÜûoPî\:¼Ã¿´Wô²È)ƒø8RÎâ1ibnNCTL ºØ„„'i8¿ÿ#Ts²DjZ& ôs‹¬s»1—RadÉ ºÖµÃ  Û1ýó–dÄîÃ7Á¡Bxì˲†HKWC_/÷%=6>5Û­ZŠ*ƒ¨§«B«&ö¹¼3K±gǪÊ f©Ie•s3Ù l ¯‚}ù2HNÍFJNÉÈ%ÌŸ âöý·dÍmQÈ–AÔË¥@DçJeóÈ Îüý$‚Cãaj¢‡Ãî8´qx‘¿ßçQbÑ…7"Ò°ÀgJ¥ÞøEÑ®½'d£Ü €ä]òffŽ~ÕçÕo]Üøy‘äòu†ìº>ÀÌ/T\«Ý`c}ʪô%½—µÎ÷!Sà h’„ÔØÀC‡Š×‡ðuS ;b…‚P ñ{†6n¼À1¼…qãçADµ äátl캾\Ð8puýXG×2®¾$JXCF¦&Æ&:Æ…SxІ™cÒâS“2ÓâIP]¸tpk@i¯+_C¬ð†¡â÷­¤ân¼åqãüÐîü»@6ʽ܅¼KÞÈÌyëºöf©Òd ©èdQé–E[}GAEzºº¢‰V(’ÄÈÌ QKIñQ)7oùDœŽN¾™YÖíyòž¥†bˆÞ0Cüž¢©n‡,Î0‚™÷—ò’^:Dd`(€ÑãäG!å}̜޲OZgxÃ6}œËvt¨Z6Ma`¬“íªPt234HJLÇÃÀ8„?|¢ñõ [ÿVÌq¯¦µvjõÊß C¬ð†¡$k½§äèS½ÀÞ7­OõË€™ã˜y 37PrMòßÑ’ä$M ç:6MœËvlÜÞN5-adª«áDGW„¹¥!ê6+šlÄšl‡›[Ökzñ¶}i¯íU£ÑH89óÿ;ŸÅÓ·Pd‹ÆîÃþ8yö~±Æ(2ˆ o<̬fæ/|`:JYßøUÂÌþÌü älëaÒõ ô­­íMê:V+ ã2ÅËÍJŒKBLx<’Þ<ÅèÇq¤¢{ºâ£áïs?w,#- 1Ož3ª`*T6CY+#]ûªæÕX”*½Ð$o}>ÜŠŽƒ×ã°û]t²ßÍu/Ò8E±hì>,ËUµ PdÞ2˜ydu¤úxCô_̬aæ#Ì<XéZ›šç-+‘4Üw^¢o-§öýC\9g§|çÀÕÓþÔ¾ìXUÛ ª¹Ÿ­z£"ÂC¢1°ÆÕÍKEÚÖ¯øi‡ÐÑjœjlóïÄ>N“Ä«gngÛ¿ÖSø¨å,1#íÅB¼f–01Õ³a ¿AZÔ%#?Døtt#„]ý'ÜFaã¢þXº®hzÄŠ bᤥ«1õÇc°³6yî{D…wmv±+žÖ÷*å%½r˜™ˆ@ ¼?ƒCÏÐÜÏW‰ŽÕ˳…/Ÿí&\8z ~ëì»ZÝkLÛ7'î©eýܽ‚¥­9×jê\è–8,(’VÎÙ)ü}`ºæLÒuÇMyÙÛ²¿ŒNCšI‘a±´oÇ ]'ˆ""üNøúŸ'ƒÝ;8#3SÂâ5^˜»øl¾âŠ bñeà%ç ÑH˜8ºQ¾ÇD…w fŽ$¢ûTï%¢w¢ÞøE8¹ã¢Ðcdkiâœ!²±e†C5»ìïÁÔÜú†zy\ÓQb1{Ì1àúÒ7Ôã&ëðô%ã¤ôÔ ü¯Çobrb*,mÍðÛ¶)šÉ=“Safi‚EGgjn_ ¢Ý+NÒÙƒ>‚½šÖ[jß¿I‘¿ûÌ 5ö¯;%|¹`´”3Ü¿w•»phÃiºsí9V/ÏÍ»Öã ³I6-x噟4µµFÛÌÊÁ³šš£ßøÒ¶ÐÀ‰_ô«|'(LÛN.]þQøzb‹ È»ÉàÛ(5) õ[ÕÈ^gld~ýd…Цo#ž·cª¦b\@ ªDdá]ËO‹¿Ý"Lú}¸&çœ Z×à·Ã(-åÍîÑñªÉ)ƒhYs~þû4Z4ʃֲ±=ü<>Å¡Ã1wñÙ\»I@–AœúIób%®ZÐÝÚWÁ ÿ´iæ€Êöæ¹d[ö]‡¡ /]qÍ_}p~ÿG˜<¾)Vo‘ã¹9eoú u]lðý¼Âcá9eKqÎ_§¡VK8äˆ-{|q/8—®„æzOýZ¶ød”k®×rÊ ]šŒ1CêaÆo'²gÉ x® âMÏÏp`ý0œó~ˆ£žwåqÖ&¸ü´¯xPH|ö¸’ b…|yŸâÆùÑip3Þte®¦u¯†|çjk5KôØíUèTÒHHˆy‚å³· ÓÎ#BbèÁíG "tÙš;iÎ×ÏÝ!ð½Hƒ&v–ÚôiÄî;/Üõ ¡5¿í®žö'}C=ÜpªÈWí{râ]%«lClne ×¶.¼hÆfáßé›C}þjᇹŒí’™[„ß'®¾\0Jê>¢U®› ;Çr`f<~õN¸—_”œ2ˆ/OÁ¨Au1bÒ.h4˜9×n­²ƒ9T*É)¹cëëÜ®aónßb7KqÚ§-à}íF–w 7ü#дÇJ˜ëáÂq¨âX6ߤ¡œnð¢PTÄÎVhÕÄ>ðÄóΙ%ƒÈÌÊ >ëm\¿<&Žn¤Ýµê@% Ð×ÏëÈMNÉÝH/?Ĉ¨äìù³d™¹@ÄÎmœòÈ žõzˆàÐxÄ%¤â°{ ztt.Òw[ŠkZá¹0óemó7Èqãw²ÞøY˜#]gˆ¿oŸ"Ù_€Y£‰§özS»~ ܯþe—àuÒÆÎì/™[™bá´¹nvÇ}7@\{ª¸êçB(ýshºô u!ª´è^Ÿu´.¶¶}±}UÛ"»¦­ìʢǑc9€ÇÁQ4triöºÏøÒq_òÜã%|ÞégqÇí¿5êL ~»DtßåEóv~¥iÛ·QžsE?–w å*X¼wቜøÝŽÄ¼%ç°qç\2ˆ¢(À?0 µÛ/…KU+¤¦fâ^pô¨‘+*-]I߯õÃrÍû¬ âÊMW ”Aœ1IÖ#þwÕ%„…'"áIª4_øÂ2ˆ§ÀW?Ë–A,cªÙ_µE‘›rÉ @—¶Nøu¡¬jý‘K1'Ï“Aœ3­=šõZ‰ò dË æ$3Sƒ ä‘A7¬àûyî¸|ýÔ ~·#sÍŸ byS êUõ;/Ë%ƒ˜%WøÃÔ¶hÜ}Ê7X-ƒ˜Å'£\1`œÈ–AìÕI–zlÑÈý»×€K›ÅÐÑQ§†5š»>•d|Q”† …¢íVµÀçxGúT@Ó®¦v^kjÓN•­,mž&rd¤g¢¹þpUYë2hÕ³¡KÞî~4oçTM«ž yßû…kgü)Ð÷!Ô™jªÑ 2WoX™ÇÏ(Í÷ŸpËû5êP‹ý.Ò äPÕŽ§/'5j_‹`Æ¿ÄãnhȤnÒ× ?”¹„hh½iª²V¦Ü  'Æ%SЭP´ìÙ€'ü0¨HIaɉ©è`9Võó¦/45cØü÷Aaá´B‡M¹l¹2ìãy‹ Mô±òÌOšó÷ µA°w¶åJ5ÊgöÌÌÞ1¯úy§°wµí»¿HóœÓ>—€‘ðñ ñ:wøÞü ‡·ï*îøWÆK–A€¨˜dx] CdL2ªW±DÓ3N¯È ¾˜ bQ1ɸA Ô¯e c£§²„Š ¢Â{†¬oüNô©~ž!€ýk=‰ˆàíî'Ø:Xrç!-¸rÍ göûЭË÷r¹j«Ô¶ç›ò“ød\Z¸q!€*V±a3K$Íãé@ IDAT%¤ Ó fìX£<@ÈÝp:´á4õÿ¸£”µ‹dc¼åŸÃBHàc263âJ5Ê£óæ’…MÑ«fûG ŽÆªss4€\Îä¹Û[òEFZ&šv®Ë-{4`#S\=s›¼NÜÈõ9ˆfˆ?#-½*}¦2©›4öÛ~ÅΗ qIQd B1Ä ÅBëªÞ oyŸê‚ ñÛÊÝi„ë7âš ¿hj4¬\¢÷¾Õ¿Ó7 »þQ›˜^¦ò,Š!VP(JŒX¡XhãÆ !÷©~oâÆo UêØó2ÙšJÚÝwIhÑ£¾äÒȉ_Ä+((%kZ¡Ø0s€ŽÞÉ>Õo3u[Tc}Ã’Ë)[X›¡Jm{Åe¦ ðŠQ ±Â ¡­7þd¹Áév¿õÆ %E1Ä %‚™×h  ÞÃzc…’¢ÄˆJ 3û(qc…ç—‚S‚QÃÙ2W KîmÙíS=ôíZ=WiNXx"€¡úw¯‘«Œ¦0²Êsº¶«’Ý #'O’ÒqâÌ}܎À.Ùí&SR3±û°?DQ@Ÿ.Õ` _ôòœƒ'p3 ZWFýZ¶…€\~äCjѶœ1Ú4s,ÒXIb¸Ÿ Bàý èá’§IÈQÏ»¸v3ZV†k]»\Ç‚Cã±÷è4®WMVÈuìaXv¼…r–FØÓ¥È}¼_Å+¼˜9Šˆ:˜w¨Ourb*R“Ó`ikžçXÈÝp€ŠUlò|ư HÒdjPœ†YÄDÄCWOÏK’JJHAðGä\×uõž^$c#šœNå+•+Ö9“Ÿ¤âþÍPr®c_ìØòƒÛÈÄÌ‹Sbõ¾1tâlÛwÌ ·eƒrbFBË>«‘œ’ •€_žÁÕãCOW…€û1hÕw5Lõ`d¨‹ÏgBÀÙI°¶2.ð|ÒÿÙ;ïÀšÎ6€ÿνÙSIbo±Wì­¨Ò´Õ–R5ªÕ¦K}·F­R:TUµ1Z{S‰ˆ ±’Y"{ïuï=߇hjEÄûû/÷¼ç½ïssÎyÎûL½Œ{¿ï ¾¨TZ ñ}ëE|!,™¾/­ÃÈPMƒºv¨Õ*f¼Ñìœ"êtXJuGK µÌœ³—¨301¾·ªX¸ò~}ˆVMùl¡G¶¼z‹ò»ý"™öñœ®wWªáh‰ßÖ å*Ú×s!á)ÔªaÍôO÷°ã5Ü›+ß¹bÍI¦º—6-ª3g‡6--Kúç®^žò'Û8óÉü¿ñÚ”ïç+}n“si7ðÇÒ¢%ý"Y³xØ=×ò Ó´à¡Q™üÆÛ×Vmë¥îaûªÁ·Ü'‹ÞY«^ošú…F3ÕSû}©Ö_/W\XÂÌ!óÔCëLUjúŽz|‡Ô™©åK·þaî&ÕÈÆï¨û9N4 мå)TR¬å“1ߪ{Ù¿f0±ûgê¯&ýPº®G/Sržl0¢ÁtõÝæ¨‹‹Ê׺ðüñpi`Í7 ^ïú©z ódƒk‰å º ;%=WgªÚ³ù,õ@çÉŒZüDµƒ|Øää‘_PÂÙDôz™ˆèô[Ê*Þ‰)ãÛ¸w"ukÛÞrÌÿT,Wã²ñ}‹Ð#S),Ò²ô§lÞBç¶µ?6àƒoÒÑÝ™•¿Þ2Ç¿Q©$¾ü çM¾íñü‚<ž_Ãóub:ûþx…ot`Ýæ³Ôt²âܡɄy #C5›v\,—œ?üĪyƒ9±ë <‡6å«e~å: U3'¢OÎ úä ü·¿v‹–e™¿ÓŸMÂ÷x4çÿžÌñ¯3rpcVýã÷ùqýi–þ¯?Çw¾Îë£[óùâ#¥Ç–üxœÏÞíŽÏŸãÙýÛh~Ýt–쥞úöý—¨jgÆù¿§à·u¿l.SåQ ±à¡SüÆÚ=žo'»Ô¯~Ëî235糧U+~¢Û»R+ý±t· `Ÿ·¿tùl´ôgØݡԟµ&fFl\¾¯Ü÷Ùˆ7ûÈf–¦·=öî°êÐÀÖŸž§;š·Nû᪉z€3~a’ßÎ éÏKKt»¯­Ò&]Kcïú£åúεó¶ª<žk#ÎX£m׫™¼`Úšr—‘œÅˆI}äýI?jwƬО8pNúg/ãÊÄÚÁXÕ×àØb!-{¯¢N‡¥¸uZFµf né4fê_´éÿC™Ïº¶w¡u3§Û¶,.Ñafjˆ‰±jµ„S5 NÇàõv6~¯t JIË#4<å¶íoÇÀ^õhp½cпñ;ƒ¥…1]ÚÕbÉÇÉȺYúFSƒÛ/òëæ³$$ç–«ÕaDt:1é Ѐƒáw"¦\k O¡iï<öwö¾rËñ>/®£çȵe>³³5C%I¬ú5.t­t矔ÃÙÄ›­4âX`,z½r;Ww´d÷¡pöûF°Æ;s3£Ò Y{þgø€F¨TëW¥¡›=GO>x«Ã»!LÓ‚GÂ?üÆOeêç'öÖÛ|Ë“/æR‚dhlˆ{÷&²J­¢SÿVú£»NK£ßL·!îrÓvnº-MÍMˆ¾_.u£”ås7Ý¢ cS9~àœôËñ/u…ùÅJ¼ë¾»Œ”,@1IÊ×4éÉY÷ü¾’b-'ž—–ïýHgfa°×{êg¿°H­×éQ©ï®;öo)wìßRNŽK',(R*.*A[rkÓÊÀØ‘-XñË)ºÙóòðf ³žs‡&3ùƒ]xo½À»“;•Ž7ª)iùw™­,½º¸bjbȘ©¡Óë9[Z¦R’$ $£Ò0z=5­xgRÇ–ÇÿT,ײ™4{nµmùtÁa®›†CU öª‡™©!/¾¹ ×Z6ôêRçžs&§åaldPZ³º£ei[Á{Ѭ‘]ÚÕ¢7²sŠöª7Q'¦—šªf¼Ñý¿vÄÕ,éÛ½.ÏS:@©T¯½ÔJYOjŽ×[/Vw´$'·ˆ­c#ÆjÉÀ1ëé÷Ò:ÞÜ åšOJÉ£¯ÇM󿓃%iåÿŸþÄŽXðȸžo܇J–oܼS}Ù¦š«¿üKµû7?iׯ¾ªøèd¬í,qm\SÖiuüﵕªÓGB¤×®7ŽxNº ™˜ñF·9ê׺|¢~©Å{ê´$¥±z÷¡måªÕmåaußV¨1É '#O9¥ß=¿3%>]*È-¤nSg”2ù9d¦ÝÚQçvèõ2/µx×à¡óÕÎuå6=š<ÕñwâFïß°07B­–hÖÈcƒ2Íèút«[¦ÁByæöùs¸O}žë[Ö°Ë®ƒálúaa~Séк&}ý÷õõ(ŠôF+ʨ«8V³Àèzýë5‡xñ¹¦DŸÎüû°äÇã$¥ä*ç:XñStÔÕ \j>Ú¡ˆ”Êä7¾$IÌùyŠÎ÷éˉ߫kÔq­mo¾½çç2cð×êÃ[Nª–ìø@wCÑ=èwæeðú'#ô§ÿ¬513.5?Ÿúû‚{%QšøÙHýŒ…cõz½ž}ÜÛ4mfa"d¥çJÙéy’Ú@©yùê!«T{â¾×.ß÷‘.-)SÚôÝþgþy¢ùÖŸcÑ÷uNmç*,ø´/ƒzÕ#är ¯¼Ð€-{Bé5òW÷©ÏîßFcanDqÉ­½7î·ÕaË&ލÕ*ìl•²®f¦†¥þѤ”\š6¬FÛ–5èÐZiŸTöÅL§Ó“•]x‹ Õ,Yÿ—ÒZÐ{Ûöºµ=àíÖKÉu¹ò J¸•N=ײþô’Ý-²'¥äagcª˜áëÚãѱ6q‰Š«À¡ªõ\íøíÏs¥­ô¨WÚˆ#)5þ=Üp­eƒç°¦ètré.ºk{þÜBa‘–c±Ä%æÐ»ë½­Â3ã*†ø+Eã¶=›Ê¿ÿR·)d‘.=9KêЯ… –˜ÉÝæ¨c.'°Æÿ ]«nÊ.ѵQ TjÏMè¡7·4¥Eçrèõ€® 'Â¥&mëÊçŒÔ¿øRi2ÿͪbã7À™ Ì_áOÃùf‡Oð¦JIgüŒ­84_@@P,‡ŽFâì¾Ç ™5w?ß~1~Ýݘû/…%lÙFÕf °ª¯aú'{Ê̽|ÍIì›ÌçrdZég²,ÓØcÕš- ý Ÿpv_DJÚME3 §õ;‹³û"ŽŠeü‹JŸã±#[p*8§– ql±ˆ˜ŒÒƒLýh75Z/*Už7XôY?fq€­±ß7‚‰cÜË?š„]ãy¬þãt™Ï'¿¿‹­ѬçwTiø5ÆF¥¿Á úx®£ós«Ë|Ö£Smj:YáÐ|u;.å›Uǘ9ñ¦éþ›Ïú2g¡5Z/bËž0ÞÛ¦ôØÌ7:0ámÔïü-õ:}Ë€žn¥m+_¹5æfF8»/¢ûˆ_˜8ƽ´Ö£B4}T(’$UEñ·«ßønM6.ß«:qð¼|4Tªêd+תïÄØÙCõMÛ»É×"“¤•{« óŠðßsFjÕµ‘üíÞt†jf ¯öÝ(5ëP_¶­f…N§§Ngyڼѥ¦â3~aÒ¼)?©¾X?MïÖüf Ée³SÅ\Š—ü÷œ‘š´u“œí˜¶`ŒÎÑÙY–™Üëêìô<ê·t‘ÿþë¤êËõÓt]‡¸Ëgý/I“{ýOݱKÙÔÜŸ­§¤O~zS×ï¥Î¥sï\ë+ý±d—ê{ŸÏtÖ7eõ^¶GµòoUç­e¿ÒÌoÆêŸŸÔ§t­9™y¼Ùc®ú噃ôƒÆz”Î÷VŸÏÕÖv–Ø:XËÑañÒißiñŽ÷uíû4/SYš>ä”t.SCê¹Ú|1‘Fõª’œCq±Ž6-ª#IÏû›šNVeøÑ±™DÇf–þml¬¦Móêª).ѱÏç ¹yÅxt¬MõøEÏÆ“›W62Û¡ªy™ô§äÔ<dÔ&¥½v.^J.ã«¶´0*MëÐjõ?}”´<öªWÆä’–‡Ï±h Õxtt)Ó bÁcC–åu'¹ P›¬—Ôþ»6\|µÄ‚§Qµ@ðT!˲˜)IRJ¾qkI’ÈoÜaÈ õŒôªM;UoS³NC#uSsCLL @hâûB[¢#?·m‰¬KOÊ;s>0ޥà%IÚ"”±@p{ÄŽXðÔò0üÆmúp2”Ô/u{®îXÇZÖMê6µÇÉÙªLK4Áý“QHÄÅTb³óöE}Ÿ‘Xp `{]Á“ˆH_<µÈ²|Å_œÀ¬Sm(©Û¶ìVÃá¦U“ö=]¨áb-”ðCÀÊÆ„V]jRËÍÆ¼mÏZ/ë%]«=^>wà6E,xª‘e9è ü l½ß|c½¤¯e[ͬ±K}[ŒM…§æaS¿y5LL m«™Ûæ›ÔzÜëžD„"<õȲ¬•ey&0øØ"I’å½ÎkÑo¬9zÉÌÔÔØÁÚîÖÍZthœ´vÞ6ÕŽ_|¤à£a’^¯¸q¶ÿ|XõB£™ê f¨÷o8öDmŸ£B®I#ÌPkKʬüëümªÑ­f«{ÚM0øüõUªœÌ¼ÒcšÉ?ª~_¼ëžFÆjL- ±w²tP¡µy¹‚ÊŠPÄ‚Jƒ,Ë¿¡ô7n‰bª._cI¾e½F³EõJ;/õÉCç¥þ®~½ë§êèÐ8  C¿ú‰ŸÔK¹™yO”"^òÞ:•SíªÞ»PvZR&{Ö•†¾ÖSž¿y–.ülŒ´é»ý¥Ï„¦íëÉË?ü]•žœõ@k’$ IBÚG ¨¬[œ R!ËòiI’ڠ䟔$é?åÿ¾d—ê«?¦ëºv—3S³â:ÕÀÈD¹]ªÕ°¥ï‹äŸ¿üë–HǸ¨déÏU¤˜Kñ’“‹½ÜkD¹U·Fr\T²´ù»}RI±–æÈMÚ¹qãïÆmÝäcºÊiI™ìûÝ_uáD¸Ô°µ«ü”~z3‹ò»U¯E$JÇöKßø¸Ìvøøþ³’ßÎÓRòµ4©nSg¹çóíåú-kËvUøãìÝõßZõ;Yúb1`tWùû9Ù¸|¯êÍÿ½¨ÿ÷÷ ‚‡ƒØ *ÿð¯æ?øê7w‘—ð»jf‹êZD²´3z…¶f]Ç{¦Ć'zMªUÏQÖ–è˜6H£ÎJÏ¥¤HË¥3ÑÒÆå{UyÙ’®DDZ=ÁÒþ ÇTyÙùRðÑ0iódƒÀÃ$ÇZöò¶Õ‡¥IÝ?Sëuå×güÂ017¦m¯fò?>“Þ¹H-ëõÔ¨ã ÿ¹ê€ÊgÛ©2¿ÅŠÿPu³g°÷÷£Ò¸ÙCK¿ÐÀPM—A­õA>!OÔ®_ ¨lE,¨”\÷¿Œå>üÆ7˜·y–®ûðvòá¿NJ¯wýD=¥ÏêúOïDóN ä~/uÑ'Ç¥K© ’¶X˹c—¤Ú «Ëó6¿£3³4ÅÜÒDvª]UÎÎÌ“^ÿx„~ä”~úu w¨ìlhÐÊU665¢}ŸfrhP¤ä¿ûL¹•`ThœTË­ìË‚‰™EÅ$ĤJf&L›?F÷âÛÊh÷“ûèßY4NïèbÏÆûÊ<jÕw"ær¼PÄÁ#D˜¦•Y–×K’‚’o|R’¤{æ§%f²qÅ>ÕäÏ_ÔOþüE²ÒsÝj¶Á‘íÒ ±wÝ9ñ{uH`ϵ•% þ¹·¬bΨ©ýõ«¿øK•‘'ëuz†½ÞK›•ÀåàèÒñϵ‘¥ûH¥213¢ ¯¨ÌgöNUäu§¾Ö>">*y»G•™š£åÝ!¥ÊØÑÙžáoôÒÛ9XËŸŽ]^ƹ\˜W„¹•©(6 sÝo¼rø/Ÿ‘Vñ§*=)“¶½šÉa§£¤”ø ¶®Àžõ~RFr¶”•–#²#PÝú}õˆ†3 jÕs’[vixÏï<ŽKýê²MU+Zwk$_9S®õD‡ÅKÞËv«f-¯?‚C숂û¤Y‡zr‡¾-ä!µß2Ж谰6㟥b‹ Šùèå¥ê“‡.H‹·ÏÖÕp­öÀŠ8=)KŠ ¹&Mœ3R_«¾“üÙøj¡môN.U‰¹J¥¢f] ÔrÂÕ)'3cS£»Î©RIü³ß±^§§¼Jõ›kTÛÔ•]VG[¬¥¨°ääžeÄŽX ø|±~šnÃùot‹¶ÏÖ•kiÖ¡>™©Ù¼Ùóê‹§"¤ŸŽÌÕ¹woòPü¦µê;ÉnÍjÉ?©ïÿr¹q›º²ßÎÓ*€Ý뎨F¼ÙG?oÓ;º5_èÔjn» [×!ær¼”x5€“‡ÎK-»Þ{']WDÀ¾³Ò¹€ËÒs®SÕW.\•V|ø»*;#÷åžEÄŽX ¸ 6H)qé’,Ëìýtç˜ IDATý¨) ç¡742 *äštáäb.ÅK»~õUµèÜ@<ÎC0Ñã3utXœôÒŒAúÀÃ%ßíR§þ-å&íÜJÜɃç¥ãÎIoý²þF©km‰Žk}T±WøcénUµ¶r¿—:˽FtÑlU­·MebfÄ¥àhéÓÕ“õÎnŽrÀÞ`iûšÃªŒälÒ“²¨Ý ¬Ozׯ¾RvFžôÒô¥[àÚ «ËnMkÉ_OùIÝ®w3ùï¿Nª4Þ3Êø‡ã¢’¥õ‹vJ¯}ô¼Þα  ”Ñ<œ±F °ïÕOŸoVµëÕL¶°2{èÿàY@숂Ûà»í”Êgë)©u·ÆrÈ©ÉgëI)'3€„˜ÖÎÛ¦òÛ$ £·þ›­ïéTjåVrp¶£e—Frh`¤ä³õ”têÐ)âBl™ÈëËg£¥€½ÁRI‘¶ô³¼œ|¶ž’΄K­»5–ývI>[Oªn˜¼ë6u–?þq’Îgë)iÏú£Ò{Ë^Õ×nTC˜6Œ¾m¯fòÏ_m‘þþë„ôÑõ­º5*£ˆƒ†I§¿%|áÖwõ%E%l\±OzÕk˜¾óÀVeÎKŒI!`_°”ŸQæ\Ë*æXX›äsQªU¯ºœ“™OA^ÑÿÁ‚gÑQðÌÒ¢ßXs©à­!ãš~RYÚ >‰ˆ6ˆÁÝ;bÁ3‹¹u¡@–Ñê´â…ôQ¡ÓêÑiõ:I¥?²@p„"<³Û¸±@¥’3òr cÓ“ó÷r*%ù¹Åækåøè¬k’^—ô¸×#<‰E,x¦‘‘bâ¢rc.¥Ë)ù{9• VϹãñd¥ç‡äçeå§Ç=î5 O""jZðL£5(:~.±¶¥µ±^Ï€u¬%§ZV˜[al*nûE[¢'?·˜ÌÔ¢ÂÒHOÍ»z|_Ô&Yfÿáǵ÷žA xöÁZ‚gžNýGÙÊjý¨ªÕ­ÜµrènQŤŽÚ@eù¸×õ´"ë咢’Äk‘™a§‚1PùÛ±ñäã^—@ð¤"±@Œ5J}5Ÿ–ètíeYeajahbQÅÈRxoî’bmIvrA–Œ^«W\6VõÛ¹þÖ. ¡ˆ‚ÑiÔ(SU¦ÚFk\bþ¸×ò´!i K 3ƒ}¶fÉâá"” ¡ˆ@ xŒ»›@ <ëh<« ñt|ÜËxVa¡@ð¬¢ñtÞÚ}ójžY„"‚g §0 H€;^Þ"½ì1!±@ <+h<[/pÓ5ù^ÞߢB ‚ǃÆÓxØ—wèã^N¥FãÙðúþëÈ)`^…¯GO50 x_DM ‚ŠEãi¼ L6ãå=é1¯¨r¢ñ”€¡(;àö·Q´ÆË;¤B×õ¬£ñ4^Þ\±#ƒÆ³0 elÄ <Œ§!ð2ð>Ðè.#?J¸ÑxV¦Ójÿ8òžPÄàÑ¢ñl‚¢^â¦;L&àåóØÖUÙ¸iêŸ8ßcô à›G¾&h<«3IÀ¿KçÀËû{¡ˆÁ£AãÙEF‰Ìý'+ñòþ»âU ÑxÚ¡˜ùßìÊqF!0/oÝ#]׳ޯ³>ðJdºÑmFd¯ÖÅ/9Å/Ù壢PRg‚ÆÓ%ø à~ʱ~Œ—wØ£Y”g”ë8w/šõ^Þ± ±@ xh< €QvÀÍî2ò†I:¯BÖUÑx6By‘ ÞçÙÇ€Å}MÐxöFQÀ½Ê1z/^Þ«oü!±àÙBãÙÁË;ëq/¥R D€N@ ºª]Ž3–ãåíó(—TiÑxv@yÑÊ­¦þòP€b’Ö?Ôu=ËhÏž—÷µ~ ± òò„y â¿üWîz7ôÀ«xyç?’uU6”kw$ʵÛò!ÌxXöæy¶ÑxZ \ûïÕÿà »ðòþåß E,¨|h|hkªŒh<­¹yí:ÜiØÉké,õ¿LtFÓ;×gT³{¥ J-ia’þ/h<]Pâ^Lp¶éxyÇßî€PÄ‚§§Êl2`}›"]æ~ÑxöDñK>hk<=J€PÁƒ/ª¢ô¾qíZÝm豫©ŒÝx‚¥CZ!!¡+_yâcÀÒ_è3Æí‹Ð<Ûñò^w§ƒB ž^4ž®(&¼W“;Œé2åE‰†¢€Û>¤Yãå}ì!ÍUyPÚ¾‹b†6¾Ýbž IYÔ±µ Š‰!>‘Éô«ïH Ç*„§å™ž‹oT ®wŒ•‹FDIßÏN(×ÿíŠÐ—]ÀùÄLÚ;ÛacZ./M:Š_ùŽE,xúÐx¶@y[¨ï1z…H—¹Jè«Aƒ; ‹ÉÌ#,%‡Žµì°2.Wúê%àㇳÈJÂÍ6„#¸Ëµû×Åk¼³+KcRòŠX6¤5žÍkÑo/?FQÛÆWsŽD§°g|7ºÖ.£ŒÏ € ¢Çp9ÑxÞ(BÓõNCdÞÞqšÃ‘I8Zš¢’àÀ„îå™}^Þ‰w ±àéAãÙ åfPÎ3DºÌÝP"@ß@‰­y§aZ½Ìkž$8!kC,LØôr§{Í~Ã$]øðü£ñìr-þ» á-$ç2e[&t§™£5[CâxyCç¦õ'|Ö d¤ë{µ—7ž–{Cæãå½ÿÑ R‰P"Óo¡i~¯á{.'p.1“³Óúc ’(Ñ•Ëа/ïõ÷$±àÉF)™8å!Öñ>Î&é;Q¶ ¡í½†o<•´übμÝ•TîÐ"¼¼?àJŸn”k÷†©¿Ý݆¦óæÖ@6xvâBRFØ›1kw0ÛCã˜Û»)UÍIÈ)à%ï^o[—3ñ\HÌ’—?çþ' ÁËûtEˆõÔ£D¦¿ŠâÖr½Ó°°”lö…'âlmÆðÆ5±33"*#)Û‰LÏ#"=S#6¿Ü‰:¶ÿ>½ø…rZ„D?bÁ“‰ÒÊí%”·ÕÆÿa†exyO¸‹zʹµ á-èô2+O\á`Dn¶|ܳ 'cÓxsk ½Ý¸’–KDz.N–¦lÓ'Ë[I¯¢ä«.ÇË»è‘Êó¤¢\»7Lý ï6ô³WYvì2F*ü¢SØöJWš9XSÑnêØ˜3½s}^uwådl:‹ý/±eLv†Å³/‘Éô]ã˦ ±Œj挙¡]ì)ÖéÉ,,¡q5kPzk/Vÿ+œPÄ‚'§-й´¼­Üàt¥6ñÀsÜ¥6qP\î5l¸š™ÏÞË ¤}<œ¼“²ÙljØ4†5®µ‰!®UÉ)Ò’UXB+€Ã(~ɽ#ÔH9MýG$±#,žæŽUxÕÝ•S×Òi\Í W¥qÒÏ#ÚQ]³ ŸÈdּЎAkPï›Ý¨$èäbŸ=@ÛŸE*X9Q"Ó߯q‡Èt^foxEZ=ã7ŸàÀ„îÔ°2%§HË•´&m ¤H§cû+]¨imFA‰Ž%þ—·é†j‰Mœ^jQë=00N(bÁãEãYÅ\z¿­ÜîÄ2¼¼>„yž^ÊQ›¸X§gïå JtŒß|’3o÷E’$Š´z>=xMçc™ÐÆ•sÓúq0"‰NUÐø„òÚŸ'1P©Þ¤Fðsj¼—w`Å ö„¡˜úo{¸­©@/ËŒÙxœäÜ":¹Ø3s×ìÍiê`ÍÅälJtz Õ*ŒÔ*š;VAãʾW=z«/§ã3.æ—è–ö¨SmµHC*'JdúûÀ Ü#«bøoG1P«èV»*&h|CÙ2º N–¦|rà ´¤Km{Î'fñÂzÂÞÈ–1]¸–•ï[ÓÚìK¼¼<Œ% E,x<”£•›N/£“eŒÔå.èôìVpºÚÄ:½L¿5¾Ô°2¥M [ìÌŒøÚ7”_^hƒ… ‘鹜›Þ3C5ûÃY{:šñ­]Ù÷ªGÁÕ¬|o—*f_âåQ!r=‰Üg±‡Í®‘”[ÄÁ Ý)Ñë ŽÏàÓç šÚ—ÖÕm˜¸%5/´#>»€‚g2 KÉ>ܰªÕÜößð}ÔâT4žÝQ^@û•gxTF§âÒ‰™=#µŠ!ªÓnÅB’³øÀ£!_ÁÈ@E|vKü/1¸au=°˜_óëíÁséB *g{”›åŽ­Ü´z™Ožgíéhr‹´|Ý¿9“Û»Ýkæ&égËlw³6ñ»@Ý; Ë*,ÁÌP¡ZŹÄLâ² 8ôZwT’D¯ºtýáW3óY<¨/o ó©lÍŒY≺7ΖHߺÌÛžRA’=yh<[Ÿq—bG£SÙ}9žöVŒk]SC¦wªÇ•ôÆn<Ásjt<œm!qlÓ…þk|©ñõv‘Ù~ç«›Oä7Z¼'@–e¡„ï…™>å™ÒþNôz™åáüq6†ªæ&¬Ùžüb-Ũ¯ç‚ÕµµÀ£N5¾ö åבÈ,,aȯ~¨$y@}§€…[¾Œ—wÌ£C(bAÅ ñì‡r³t¿×Ð…~a\IË%òÝÁÄçPŤ\Å#–àåíÿ€«|z¸{]íR®¤å2u{‡#“±53ÂR/òŠud£ºþjæhMëê6,ð ãÛ!­ÙbØ…5AQ¨URîÔõ¾Þ¤æœg: L v›‹R„ã®}€gíæDl/4ufó…Xú¸9ÐÇÍ‘ˆô\züx˜ŸG´£·›§ã3˜sèCÕàÈÄž9G£S6ieYÓý‡¿¯Äý,ýdW„hO-Jdúh«Z£» ÕË2½W¦©C¾îß‚¯}CùôàyV<玃… œ»Ê˜–.T57æçÀH¾îß‚O{6Iþ´g“oïðòN”âE,xt”Ó\šW¬åRj ì-172 >»sCþ8ÃåÔb2òéäbÇ[êÝît=ððÑ#‘áIãÞuµKIÉ+¢÷j¾êÎÎqÝxã¯S||àïÓŒ œÎlà4ØøÌF@ÃŽSsPLÐwô”è05TS¬ÓóýÉ2>Ž¡ZÅè–.ä+?ß_¯ÑÙÅžÞnDe䑚_D»šv…ÛBâÖhõú¹žÞËçùQj…Ù»±$|úÐxš£¤ßÍîØñ¢P«ÃX­F’À/:•¬Â–?ךôåtMPx4bë˜. øå©yEju¤ä1¢©söì=g÷¬±C…¤yÄ‚‡O9Í¥+O\AãJSCÒò‹Y?ªªY1cçrŠ´Ô±5§º•) ýÂØ3Þƒ¶5KƒR‹€_…xy_~”â<h<ë D€ŽçÎuµ)Ôê01PâS6eíéhvŽëÊ¡ˆ$¦ï<ÃÕÌA‰¶½c°ÏŠãá,ô»Dz~1Ú¸²xP+º|ˆ„œBÒò‹È)Òb¨–˜Ú¡ãZ»ÒkõaVµ"½ ¸dN¯&KG6uþ솥A’¤†(½²Ïeñ`.‹’6 %5ìŽYI¹…¼½ã4>‘Éèe™ýºSÍܘÄÜBòŠuLÙÈœ^MÙt>;3#V kC`\:ËÂ1R«fwk4»áâÝ&:½ì!Ëò+%žPÄ‚‡G9[¹iõ¨8Ÿ˜ÅÀµG8>¹75¬LYzì2ŸPBfÀÖÔˆÜb-Fju´^¾Ÿu#;à^Ã&X‰TA’=>4žuQve/s¥p53Ÿ©ÛƒˆMÃ@%áóFOª[šbb â_äà•$6¾Ü‰Ù{΢—e6¼Ô ¿è~8!«%)ìãM^«÷Í®N@mY–ß®(ñž(4ž5P,+¯s|ë­!qÌó e×ønÈ2ä•PÛÆœ"­ž øtìÍŒ©mcN`\:¯l›ùShrö’÷ö?»¦þûAãiŠR„ã}àŽÝ-nÐkµÃ×`jÇz'dÒÌÁµJ"&3N«±ïUš:X3{ÏY–»Là[}hîXåЇûÏmÒø„þŽý¾øF–å?­p7¦iÁƒSŽVn² YE%\LÊbêö μÝÀ¸tØ[RP¢ãõ¿Nqìj*ÿëÝS5ç³è»Æ‡çÕÀ?&•A ªg¹×°ù ø /ïÜ ”îñ ¤Æ|‚²¾ç}:ü·£ÌìÒ€íc»r&>ƒúv–H¸’ȶÐ8NN郉ZÙ%„'ràJRqÏ:Õ~^|ôòÁ-!×v­=]¥Ô{V°'ÅÜï…Ò!ç¶Ö†’ëjÛ˜ccjD|v–Ɔ칔@xZ‘éyÔ¶1gNÏ&D¤å’]X‚$ÁÿËô¯ï˜Lw¶6ÛèüõvÝàŠ”íiEãiŒ’Òø!àt§a 9'dÒ®¦vfÊÿ¥D¯ç»ãWOË!5¯ˆñî®ä]©¯mcÎÑèT‚âÓY9Ôýäΰø¥Í—îý]ó¡ôJÖÅ5`^E*a;bÁƒ ìÖn˜Ko›0ŠÍÓ;€´ü"Ì 8p%‘c»badÀÀµG¨ooÉìn y±Y-6žåØÕT¾Òšäl.&eEU³0YìáZuå3á¯Ôx:¡<|&·í±V¤Õ³éB,’²Ѥ&mkÚâ¤ÙÆÜ^M)Òé OÍ!³°„7Û×%)§é;ϰzD[G&“”[Xòf;·5UL ?¯ÿÍî8àK”ÿ_,0W–åÝ%êcGãi‡ì3•»äï ‹gÚŽÓ˜œWÄÂ-ÙÌ™™»Î™ž‡ëõNH«£ø¸gcÜì,x{ûiÌ Õ™V&†ÚÖ´òÙÁ "¸æ¼Ï]eÕ‰+üýzT’ĬÝÁ¿š†ïÄž4[º—þõY<¨zYføoþô«çÈ”n£Tlzbn–GŠRé}à-à–Î7È((¦û‡éäb‡£…)Éy…¬xÎýá‰,:z kCêØZ_¢c[Hѳóµo(qéŶ¦F‡fum8¶Á7»R+L®'Å…2 ÅŠcy·¡Å4Z¼‡]ãºá^Æ}በ]çÇé©ýh\ͪÔ}"Ë0äW?^jQ‹Ñ-]_áåýwEˆS)P;G£¸aêÜkøáÈd>Ø{¿I½0R«J‹¢€%]¤Õcj¨&"=—N«÷ÁsÅ*i-0ïI̦iAùQZ¹½Ï]æe¶‡Æq("‰žuÖ¸i¹T57)M—ùº_sÌ?ûÿ˜T¼=;2tÝQv†ÅS¬ÓÓ×Íñâ”ncŸ™N2Ï*ÜT ·´pù7{.'ÐÄÁŠ•CÛ ÓËÄfå“QPLßzŽô¬ë€N/cl "(.ƒ¡qñx4Z¬z¦SàFÀÏt”ßÛævCÂÓrØGMk3<›×",%µJ¢¦µ)ïï=ËŸ®1§gSœ,MHÉ+¢íŠô¬[Ðäl¬L ¯ hà4 /ï€ •ëiFÉ~%?û޽°9ÅöÐxœ,Mø¸GclMˆÍ*àí§‰LÏ%"- c6½Ü™ÃÉ|p™öÎvøF%ë?ôh´Ã@%½…—w\E‰u¿ˆ±àîÜG+·¼b-Ï­;е‰! ì-Yâ™ã“{“YXÂØMǹ8cFÊ»_£Å{pµ1g÷øn”èòE$íHÎ+š?aó‰gE[ (ßY@•Û ÑË2?œŒdoxµ«˜óIÏ&¤åÑqåAìÌŒ‰ÎÌC-Ièe™¿_ï_t ÏÇÒÌÁߨ”’·:º-¯kC¯g¶ Ò ”€Ÿ·P^"íï4,³°„6Ë÷óZ›:üɧ=›ÐÛÍ—ù;qµ1ç­n¼Ñ¶.ç3™{è"»Çw#>»@÷×Åkþ™…%š÷Ÿ{vëmÿ4žÃQò³›ÝmØôgIÎbR»º¿šÆ†ó±œŸÞŸÀkéMÃ¥Šuí,øþDÖ&†,ÒšWs¯fæÿ™S¤ýlÆÎÓÑ"Ï vÄ‚Ûs­Ün°âø\ª˜ñóˆvd•p("‰O^`ë˜.x¸VÃó–i…oT -ª°÷r‚|)%gAƒª–ó¯=òlt’)§RxuóI2 Kx¥• ‡"’è´ê gÞîGÈÌ\IË¥¶9Õ-M™¹ë ».ÅóUßæ4¨js%-wíù¤¬EïíΪ™žT”€Ÿ‰(>÷Û=É((fܦŠH¢¾½%öhÌwW:Ô²ãå D½7˜~õ‘€7Û»a¤V±ùÂ5\mÍuÀOÕ­LçOÝYR=ýh<ÿÜï4D–A’”t¼ÇÃIþhÆj±Yùäkù;"‰ç›ÔÄØ@Eg;t²LzA1î5l³¯ú¸9~‡—wN…Éô€E,(‹¢€ß@‰"½c+·ô‚bæù†â•B§ZöÌЗ*ftªeO`\:þ<ɤvuyw÷YÎ$d°fD;¦l¢Ë÷‡p¯n›÷ëÈö9|µÕ£áâÝçdY®üJXãiÄM¥pÇ(РäÜB¼Ï]%{Ίt:.§æ’W„L ]ªràJ"¯´ªÍ‘èv_J`Õ0÷àÃáknÇË[~öBŸÿbŇò°¿cÀÏ[ÛƒHË/fX“š|3¨%=ò!§¨€×Û=þÅÃÛ0h­õ¾Ù…Z’äšÖf¡«†µ€—÷ÕŠ¨’ ñì|t¼Ó%þ—Ñø†"¾{âha‚^–Yp$ŒMçcÕÜ™sÓú±ëRÅ:=˽á8j•¤m\Íê@ê–#ŸFŒ0M ”`‰W€O×» -Öéiõí>^uw¥g]ŽD¥0¥ƒ²[¸šÆËØ2¦ -ªÐî»Ø›³{|7€ÀìÂ’o¬L 7In°Ž/Ȳ|á‘Ë÷¸¸ú1wé¯<÷ÐEVÇÔPßÄ^¸ÌßÁG=ã}ö*c[×f‚»+û™à^‡…GÃX{:ššV¦I­ªÛ|¥ñ YV1=áh<Ûß^JÍi·òøG&ãhi·CZSß¾l\Ö<ßPæº@þÜPIëÎD³< œSú°ûRom"|Ö €âÑI›·\Œ[øÝñð3.×ӌƳ Šî~»Ã—RsØG}{K½Äžñ¬=Ͳ€Ë\˜ÞŸŽ+balÈ_£;cmb¨tBúÝŸKï ;Ÿ±¢uu›UOsV…PÄÏ:O7ƒ%êßiØ…¤,’s éZ»*1™ùô^íCÀä^\Ë*àJZ.zYfDÓšÌÚŒ•±!š~Í9ŸÁ‡ûÏc¤–G·¬ýÞ‹Ûø‘@cà²,ÿZbV8Êïz# ô¶ÕÅÎ'f±7<:6üÉöWºðÍÑKl ‰#`roê³›ÆV¬Õs#ŽD¥0{ïYŽOî-; ^ÞÇ+Pª'%Xs&>cüW>¡ÒáÈ$&µs£o=GV¸BHr6g§•1Ì.*ÁeÞvŽëFg{´z™úßìâÇçÛÒ«®RfÀ/Gä<mõp­:óQü¯´h<Û¢(àÛwÆdæq-«€‰[NÑÉÅž_®—ü¼O3ô²LãÅ{ø²os,Lè¹ú0ŸõjBmsVž¸ÂȦÎ1Ó:ÕŸl© í!…iúYE1ß G1ß5¹Ó°ô‚bžÿí(:Y&§HËðÆ5ù¤gc:Ô²ÃeþN-L¨kgÁµ¬|N^Kçù&5ya½?Ç®¦’šW”éÕ½ñ«c6lõ;H’äT.ɲ\\!²V$ÊïúJÊm‹Ñ‡§åp-«€i;NÓ¶¦-ïï=Ë‚-1T«x§K–„s("‰%ƒ[1ì·£|2 –»,iYÛ˜‚—÷ùŠë‰Eq¥¼ ÌÑ˲ÕðßüiçlKÔ{C°4Voõì,pž·^F­ºÙ³ÁÊØ©ë±< œÎ.ö¨$fwkÄW>¡ôªë ›÷Œïö ^Þ—‹lO+J.ü×À+9EZIFÆÊøÖBeKýÃY{:Нu§uujW1Ç/Zi0¹k IDAT$>èÞˆ¯|BšÚ—ï÷`y@8þ1©)mkØ}3mÇéy+Ô£E(âgç ”7ÕV÷ºàHíjÚ1@ ´zµ$!IàíÙ±ÔŸ °åâ5¾?A¯Á­ô¾{îþ;"ù»™»Îì³! ÌÛêupåô +-—e–´;•Œ•±!ÝëT»u˜O(ÛBâ8õVêØZ`kjDà5¥¹‹‘ZŬ® øÊ'”C¯ug׸®üp2RNÏ/ºâlmöá´A›+Xª'g_`é”mA {Ô©ÆÈfμ׭+OD”Fçü™Œ©¡š"3UÙ´÷éêSwá."Ós©ckÁ«î®hugQZj ôý ÄAÌe>þëâ5Ëe—9“ŠJÅÂ-niÚ2«kV§D§<"¦w®ÏbÿKœOÌ¢™£5£[¸0çàö…'Ò¯žãéÎ.öVÖºÂ4ý,¡ñì |Á]úvúF¥—N{g;:»ØóÍÑKüq6†fU”NH™y¸×°eÃKysk ªZandÀ"ÿKòÜ^M÷¾Òªöt¼¼Ã+N¨'¥Ä§æJZî¸yGB¥MçciéT…s‰Y¼ØÜ™•CÛ”~)5‡Æ‹÷:sõí-IÍ+¢îÂ]œÖÚ6æäk©½`'»Æu“ÛÕ´ý˜ƒ—÷•Ç"Û“ˆÒcJZ–ø_æ—ÓQ¿ÝB­×;Y9´ î5lXp$ŒõÁ1üô|[†7¹}ìá;»‚±53âãý€ñò>ZqÂT”—ûÅ¿Ÿ©÷ÅáL Ô|àшak°?<‘á¿%é£aØš–-7qË)Œ Ô|;¤5 ÄJ\JÍæ÷•x®#Ñ)Ñfêÿµ©iû ^Þ•VY Eü, ñ솲îv·aþ<ÉåÔZU·a{hSÚ»ñn׆¬ Š"½ ˜:¶æ8[›1~óI hAsÇ*Ìó ÕÊP¢×ôãÉ¿Šè A1‹N>yý¯SV]¼Æ¤vuy§Kªšs>1‹æËö’7÷Ì ËîÄFýqŒö–|ÞGI¡œ¹ë EZ=ß U2:v„Åû›¨Þï³ÚçÙé±|.Î`á}ö꺨Œ¼!†*•z¼»+®UÉ+Öâ2¿ŽìÀÀN,ð cÁ‘0Jtz&´©Ã¨j~Ç ¬”èΘª?ÄË[äß/Ïz:Y^š_¬`ilÀøÍ'ˆÏ.dÿ2êüï/ŽNêES‡²;#Òsiõí>"ߌ½¹1ÅtXy ·úÆZÌ~ÅË[W=„iº2£˜J?úÜk豫©NåÜô~«Õ8[›ññó¼ÔÂ…ñI¥›kU¢3ò(Òê¨go™S«ŠÙ²CÝ—âåòè…yÂÐx8Ÿ˜µÂÌHíZ×Ö3C5®UÑôk^:¤AU¥ñBbNulËÍú°{cz¯>Ì072àÝ® éû³%:½¯¡Zå5díQéDÏ2nÒ–Àïeã‘ÍœIÎ+däïþ„Έ™Ó:Õç+Ÿ6pbr{7¾ö eõˆv<‡]ðu€OM Õ›+ónë‘ ñ´Dɘñîî`£Üb-?oËh±lÑJ €…~aØ™ÝZ:½®­CÖàÛ€pæönŠ©QrèÌ_©$i%^Þ•/†äE\QjAÿ¸c£—ïŽ_aGXúyò­›6:®OÇÖ¾Ÿ<“µ®Ìç§îFßN­9ö±ÿ1aÖ/¬Þ¡w}ø=‡õïÅÅ;¯—·bàß&óíÄé?ô«§1°´VÛ®ÕyJtÀ³9þåùO3}w¸)ó,dÄ”™|5nm›5aÓU;ЬQCÎzîS>3…çږɳçÒóʧyîÈmÙ²ç"³±x·©ÛëS´U]üu‡u~üËŸ˜<³UË&èÚº¯·íš5fŸ{ÞdõŽ­¸nÏþ|2z Þü}:¶fàvksX¿^4j°ÔŸ£ ³€›ðf “kïnê!Ä+%úàë€ rwÏ[°·GMäð‡ÞegýÂkÇíÀf«väµïÆqðƒï0⬽9ô¡w5e²%s,äð‡ÞåäÍûütH¿žîe`é¼Z¾³âS2@7½3ìüÛÞûöܦÎjÜ´QCútlÅËÇlO‹Æ 9ì¡wiÚ°ÿÜÿ× ›8µ¯{ŽƒÖïÁå»ý*o$œÅüßìވʲpc™ãñ¢Â™Í_Ž›Æ–·½LÃbÒì¹4”èÓ©5SfÏ£Wû¼rìöLž=—Õ®z†·ÿ¸vkÏÕo¥OÇÖüfîS€+—G×¥¢S2`¥a§_¿É-/|ånpÜ&«3kÞ|®}ëkŽÞ¸7ÝÛ4çQÙ韯2òì½éÔ²){Þýë¯ÜŽË³¦jr˜Ü\ÂÀÒѵw3u“âå™’ñ'ü£)£¡lpÓó´h܈ÇÛ’–M±áM/rÁŽërxÿ^lyÛ8¼/Þ '>ù!Ï ý™.­›ÍÙsÍ®7\·gÿÿ[ÌW‰’}>fê -oyÃÛ÷Ý„ƒ7èÁ”9ó¸ñío8aÓ5èܲ)CÆNeã[^dØ{²jÛ\úíš7^ªR:‹±¸È Ѝ,‡’[ãÑÑùvÏœ;ŸOFO¡s˦ôjß’& 0wÁBúÝô·ï»1[÷êÌŸø€ù ì· Àl|á*–N©½YAX¼Fûü[ßý¶íóߌæ©ßo]æáÛÝñ [÷êÌÅ;¯ÏÛ£&°Û¯3âì½s+¥÷á+¾¯Ññ/G„/¸mâIÀß>øiRÛÛÞÎ{?L¤o§ÖÜú›èÒªÙ’‡¿ö·½ÿ-#ÏÞ€{>Á­ï~Ë»'ìÀ³_ÿÌÉO}Ä7gìI£ú·¹,­·,þ€spô¥¯~Ùà› Ó¹ûÀ2W|±Ï=o²Z‡VܰW>3•_ßúÃÏÜ“nm–˜‹¨¬,¼ä­ÀÁ•=uÁBcëŸãþƒ6g£îí™<{.-7¢i£O§0°tDu·^Öh“¾¼2|,¿{àmÎÛ~]¦Î™ÇˆÉ3ùjü4Ú¯'Ú¼/ÃA¼Í¨¿ìCë¦Øþ¯²íj¹pÇõ2W}8¥_ç¦ê. /¼ðÂb!¨ nœþÔëß?üøÇ?hvã;ÃØ½oWŽßdu^üv,/ ÃÁ,ii¼A×v\þúWì¼ÆÊtkÓœu»´¥äõ¯øÕÊmY­C+úvjÍÓfM[­C«¿tjÑô–~ÆÖçþŠIÉ€F¼õÈ)ÀcÀ€&Ìú…›ßù–&0øëÑÜùá÷”¼þ÷|<!úwkÏjZñçg>âø_¯ÎjZò¿'ñíÄìÜgeð9°«ß1°ôe¶> þ¥÷+â­Gáv å2oÁB¶¸íeÚ4mLv-øxôŽyì}º·iÎi[y+ÛæŽlÔ@G0°ôB¶> ¢àÊR2 7o=r'žµY4¹¾Z‡VthÞ”§¾ú‰©sæ±j»ì½V76ìÞó_B¿®íÙ¹ÏÊ<2äfÏ[À–=;±^—¶¬Ò¦=Û·üp0K¯eëêß ‹ˆˆxy¡d@¯‰³æÞоyã}¦ÿ2ŸU.Šc6îÍ»m@ÓF>-üö¨ Rú.#Î^ºXú¼—>çûI3¹ï Íøû{ßòôW?3øÈmÆ%ÀßX:§ï¨náf'7àØKpãÛßðÌÐÑ´oÞ˜Õ;¶bý.í˜9w>g?ÿ)/½-wïÀöÿx•-{vâ’×ç§i³iÛ¬ñÜVM .e`é˜Z¿Ÿå…’»ƒ =üé¡?sÚ3óÝäl°r;ëߋӶìKi.þÀs)KgÕØxWTJ´Ä;ƒ”½è:|âÖ^© §nÑ—G†üÀßÿÀƒoð9ðg–¾Rý^±ˆåKuï_{pö6ƒþÓìÂ×ãÀõWå„M×`ÈØ©‹DàÕáãYNæòçdå÷ý䙬־%Go´Ú´­zu¾ ¸˜¥3jãVê$%V®Á}·órÊ}9e‹¥ûa¼<|,_ŒÆÆÝ;páŽëñùÀº·iþpF¤E+À×¢ÞV™Sö^«{¯Õ9óЬѢ²ˆÿ'…'t)°*Þ ­ÜE×/Cÿní¤|9nù§§ŒÄë­Êë­:/D¼qyîˆT›„×eJ€‹D€c6îMÉë_qàú«rúVk²ÚUÏðñÏ“iÞ¸!¿ò%oŒÏåoõÙ©eSNßjM¾;uîjí[ÞдQÃË׿þ¹Iµx7u7úyr—vÍóÛ¯Ãïû÷bæÜùô¸âiî=h3vïÛ•“žúǾø‘ù “7ïÃi[®¹¨ãLæÿÀ#àz¿\ U„~¬_Ñ¡fpèCïðÉè)ìÚge†OœÁû?Nâö}7æ7ët˜ƒw›¹œ¥¿ÔìÀW¼GíäYï^ ðêêóX:½ZÇUß(p0p¡‡/4ãñ/~⃟&ñ«•Û±ó]èäÑñ×øCèË55Ô™âºDÉ€öO}õóßxøÝƒn·{®Ù•ÏÆLå¾OFòè¡[Ò´QþöŸ/xyøÞ<~GFN™IŸkóî ¾n² b¹@>Jœ›ŸÄÜ yü‹ݬ ›¦´óèx0p2K¿«©¡®x6â% uE‡æð6ÞòÓêT=£d@'à+²Š²ªÀ,| åÕ±¯ê„×¼‰ü±À¥‡<øN§uVjËyÛ/U3xàžW¸cÐV½:qÔ#ïpç¿ÎwøãÀ_c¹@î@ö°´ùmáŒNe`éÕ3¨zˆ7#y(×ù$1ø pg½]VWÝ” ¸8d®ð¾Dld5¨ÞB\lJl ÜlpÌ£ï3gþBn·6?M›Íð‰3øzÂtºµnÆáý{Ñ­MsÎü _Ž›Æà#·áë ÓÙàÆçzÚ‹LÖñHã\–þ¯HwUwñ‡žÿ›Uñ óðyû‹£:·ðjõ§){žÞp¦ ,­ß5 Õ‰ÿÝ©j»Çïq~¦GT¯ !.%ºáK¬Ÿüaê,ö¿ï¿|üódº¶nNŸN­Yg¥6|=~:?LÅg§ìÊø™¿Ðûêgxç;Ñ¿[{Îyá3öY»[ôèôÞOõµbÜÒrAÉ€}€'«xö+xuîÐjQP2`<{“›¡ø8¥ïÕþ VpÜJô~àw•8ëüoVI½ô›¯ABˆk›’M€ÓsVefÆK‘Ì ç•Oóò1ÛÑ·SkNzêCÖïÒŽ?nº:ø<ÏÙñ„Z%„GXÇT⬨έiJì <Œ¯ä˜Š·Øû{øp× îÐ÷0°oG¿€×B «ÙAÕOBˆk“’ý€‡€>úó´ÙKX$~9n;üãU†Ÿ¹'-›,ªŒž‰w¨¹¶^6d¨*žž¾ 8¼‚#£:·6)pÞºóL–Ž-öpV4$5Z˜Ùbß ö,ã´ÓXúHͰþB\[øð“ǹ)—I³ç²öuÏqÜ&½ùõ*øà§ÉÜþþp®Ý£‡öë™9ìqܵfT Žz…@ÒêÀ^fvâžš»8¨ŒÓþ‹Wç~Vó# ‚šEÒéxW«¦ÀààE‚\2 )>O¿sÖ)ó€ë‹êµáO-B¼ HZ8‡Ïµ ήúÿ4³P2àox3…‚øß“¸áíoø~ÒL¶íÝ™Ãûõbí•Ú|‡§ˆ ¶¬ÏHê < 3³}–Øé©¹‡XÒUk<^{WTç+’Ö^ÖÆ—= ¼afW.:ÈüÛ¯ã¡_Öú`ë)!ÄË€¤ ÷q}¸Xø=>¯ø°†]vÐ*ør™üâ ã\àKêµ't$5vÀÍ!Þ5³ iûnø¿É;@ƒ¥„2Æ{ƒðb·zÛ ¼ºHiÐã€ýñ)”ûÌ,Ò›5ˆ¤¦ÀµÀ¶øåÍfö¨¤¶@3û&÷0ð¥™]°ÄÜozW–>V»#ÂârÙ™\nf %½L0³Á’F5mÔpc¼“ɲˆð ¸kÍ·Ë>Ü‹ôÇçÜ`~&0HÒÎfö¾ö´?°#p`Þ ,—¬D× “ˆjå·ÀQx¯ì@©¤Ì,* kŽ?Ý€íñö…OHú™M¦¦Àá|ÜÎòÄ¥Îööœ!ÂE „xÙmf Óû_€ìÅíó±õ~@ÙÍlË' %*¦/Ð8ÆÌfJÚ´ ÆÌ>PY02¸-eˆpÔ Ÿ{\¯Þÿ‡™ÍzšÙ—鸇ð~Ã!Ä5Ç$`=¼ z°™uÌÙ?oq<^qsí/(‹TÀÂòvþwä„¡@e+nçã-ÝÖ®/€71’žÂ-CTk›~ÀËxæá.3»ÖÌžÔØ›øw©QÌìßxÄ»?ð¤çRZ:³ÿ33» 8oyÔBˆk˜ó_ò¾4 P¦7€~ ,=+ª+ÆÌšÙ`]àUüÌ%ÅU½¢7^@ø^±Äw/© ^7Ÿ¯jI;#Íl7  °2°—¤$ý+ëÐ@,«C„/£ñfòÞÄ+t3Ü g`é›xDP^‘Õ8à–nÞÐ…#iIßcÍì:ü}Ï N ª?[?ãÕ¸‹Úé¥"º§ð©‚½³¦p‚ša!p¤C€ýðùâOp?ï½$+é<‹q]ñ†äUÓµIÉ€]q{ŦY[âÍÑÏe`锢Œk9GÒåx:n^÷ð;3û*kÿáÀy«¦ƒeBR?|z >|%ÐÈ–7³0œ©$m…gà”šÙ'i{G|^¸pO¦‚:¨„×6%öÆ—:5þ‡{é~XÜAAÕô$nTs ° p/°Rú}KàŒ¬Ãǧjö ²!.%~‹Ïá b`i¤ë‚åI=³p#ˆóÍì}IÏ-s}©µ«A„AA1‰b­ ‚ ("!ÄAAPDBˆƒ ‚ ˆ„AA !‚ X‘t‹¤-Š=Ž lBˆƒ ¨V$%éÚb#XÄê@›b"(›âjDÒJ’º{õI$õ(ö8ê1MñÖ‡A@qõr4ÞÙ$(.û{A…P­B,é±Ô¨I»HzZÒ ’´q®·£¤Ó <ö I;TòúÇUvLé¼&é^;Uåü ‚ ÈÐ@Òª’VÉÞ!©¤ÎY¿·ÔLRËìíi_WI-^@ƒd<~1p¸™í œ üKR+Im$5’Ô#µHCR'I ²®×$¥[ã¾µe"©µ¤.@çt|f{çœk6”ÔWRûÌyÀù’:TpýÕ²ÓÍ’úã͵·Æ AA•i$éF’ HêhfI€§÷Æ$‘û=žvÝø ØFÒ 3»KÒ]@C¼ WºîÁÀ•f6À̾“´±™ÍM}1;£€$ÝwkÙ^Ò&x“ñAÀ»ÀÀ e ^ÒQÀÀ¼Û;éàN`$Ð3ï%\<_6•ô0Þv° ð;I3Cs.p Þ·³“¤fvÞ-éà‰Â¾â ‚ (›FxEÝñ¾¡™Fôgg³qÞ-mgfç$Á`f¥’JïÍìÒ$Žý¿g›Ù$‹tYœô3³©’OÛöKãxhüoZ¾0¸ ˜ifc%M6³ÛÒØÎïI«â¿¤íŸH*gXAAP €ß?çIjtÖÄ£Ó€ éø¡éçl< îŒ0³ùé:àâµQöIº4«’ulú9 ø>½Ÿ‡·ì„7tó"WÂÆÀô´)s5Viìk3]ðHü.àœœË”f¿Ò÷°'0 (Á£ì ‚ ¨VàýCÀÅ©UÚö<.´Ïâ¶ Œó?Ö“´¹¤½€µÒö›€c$.i]IÅ#×QŒéYàtIkà)ñ¼˜·z8QÒúÀ^i×½xDþ*ž~Þh ܧ“/Ä#o€ù’úšÙf¶oÎk%ŠGé«Hj\Àøƒz@ª—X¥â#Ë<¿U¦°1‚úM#àD<"Ü8Ì̦¤jâÝãÌìƒTøÔ07xÈ̦KÚŸ_}8˜—Ò¾»»‡oãždqôú$‹£èGÌìjI‡¥1]´/güGÇásÉ'?šÙ×’ŽÀçx'§¦{:8¼H矅υ_]ÆõöÚû˜Ù¼¬}ÿ`q* $­\ ŒOcȼ–ø=3‡,?¤Ãáÿ]Ž–Ô8ÓÌÞ¯ä¥♣ǪyˆA,gD?âjDÒ9¸h_lާÙ3¯Îy~o L¥±ÎýÝÌf×Þ-ŸH:ØÎÌŽL¿·Å§)zšÙ0I]Ò´ )³²!þPù#°¥™½”ö­‡?xFz`LÛûÍìhI;±¸pðUüßnOà-3-©!þÀÛØxÇÌ qšÙð¼™Oלt1³g%m€O»ü×̾MÙæ3ñÝ­€9@¼þ£9žzÁÌò>@V’þˆgÀþX“Ÿ†¤çëÍìùb%ÈO,¿©RZ»Ìjï iž»ùÅz<…ž-ÞRz|…‹÷D3+kja¹CRÃ*ÜϪÀÓxVækIwCp±}1‰ÝžÀŸ€ë›ñš‰+$}hf“€[ÀÀ¯36³axA#xfå<Ò} ¸ŸÚxÏÚd2/Ÿ²8‹”ͽxÂ/éÜ]ðú„€×$ígnnOYžm€nY×üŸ†ù>-óp.Æ;'UæË ‚ f !."iž{rz +äœ$ÄÉ/Þë°täÝQÒ/Tigo3³©Õs‡5Â6’Nþ²P>5³+%µ¶Ç—áõš;ábú73{[ÒkÀ|<}˜¤W±fö³¤fø¶|4—í‘tp•™ML…Š ñiŽ5Ò2¾|&ü_Gá Ǥm € SD=ø«™}(éu|š¥Kúg¦;&›Ùué~5³ë%­/îûOÓîŠ/k»XR/<*=ÆÌ¦IlafñSñtöé>ö®1³y¬-ÿ0HÒMYzúâ$ü;ü6];÷žîþ–LpŽÁëîÏýô ÁrBq°)e>)½¾)䜔2ïD~ñ^/϶’æP@Zf[š{ï„w÷h‰WÇï"é3{»Œá}WàgîïtIÝñB¦­S4ÿNZ°9°wú,pÓ˜vYçŽK…^k]€[²²GákìŽÊzÐ84½¿( ø<¼€*·ðî$`]|ÿNf6[ÒñxÑföº¤pß½œ¢«Y,ž·ždЦ¦d½‚ ŽUÓÕH¦jÚÌr C‚<¤”y¾jò²½ž^Í×bo&p¾æû²ª¦ƒÚ%ª¦ëQ5]÷‰ˆ8(Y)ó…ŸRæ/àžç¹,Ä+ˆ×«®ñAÔÑ8XnH)óvY›âk{ßö4³Ì죢 .‚ ŠDD,otÄ‹µ~ÞÎ5³‹;¤ ‚ªq°¼Ñ_z´‰™íVÛ",iI¿Íúý·’n’t¤í«xÍsS°²öŸ¢ÔG»×\¿*c ‚ ö !–767³ÌìË"}~/¼jIãŽ\·ÿþš<Ö+šW& IDATË,öqÏÇž¸jA¤†)×VaAHMËfösÅG-IŠ&±Ø‰êdÜÞò¼cØ\¼ªô IOâý«·Æ—L'©¾†7³PxHªø>X3³LIÒÁ¸“Y7à|þºoúìՀ̀’´ éøääé¹-éwxÓ‘ÙøÚm$uī»àë/M÷t>_Þ_ç|,°njžÒØ$ëÒÌìXIOãv˜ëƒñtÿöÀ#fvwe¿ã ªNQ„XÒÖxÛÂ6ÀËÀcVÉuT’Ž^1³‚L*ª I+ãëLï¨ÍÏ œÔ!wySöëÒ<§mŠ‹èŸp—¬¸Ðv3³#Ò¿éÍxW®­€ ÌìIoJZ8¸ßÌ”é"ö+à»lS3 Œ•Ô÷–^¯â~_ü+à,Iߥ±l@êâ”s-q_êÕñÿGJ»Ž>1³û%íü÷«n†¯î‰;‚ýØÈÌîM뻳£íÌÿgÛà"<õ5q÷°ð–¡AÔµ.Ä)j88ÿpþÄ]%/µð9ºEU’¶Ã›|‡G!Á2 ©å‹jî«=Aæš}d^_“¿5åK¸Ð<†‹Òø¨~’îJÇÌJî`3Íì“´mn²6ÞÖÜârSÜè¤WÎý¬ŒwFú é9’Æ%ééx†¾ÀÇf¶˜(ihÎxW¾Jv¦ó$eæÁw¦g¥¿G¯ã‚úÐh—u­óÒx3LÀ[“N5³ŸÒ¸¿7³é}«<ß_5HAB,iug:¸eÌìòSá÷AíPhDÜ èafýS÷™WqÁð¬¤ðÔÞßÍìyIGàº&ïÞyxsI@ÛŒ¤þºï¦_ËL—%Áü'ÌÁÿØår,>o7 Ø-mÛ¯°!éÜøUIÇHê‹jö_îŒø7Äí{“œœÌìIý üΖ E[ñHçѪ\£¶Hcí@å¢Õø¿Y>›Ê/YZT'fú×û§ÿ†Î3³Q’ÖÄ;,ÍgqS‡w³Îû°\/éŸxûÄŒôïÓ¾ÓïϧÆ.@&ªž|–ÞÏ>K”ΛJ|„?,Ædmyþßû¼%â<åüGyÇx›ÃñÀéÞ„?èþŒ“tf¦or²ïóý2¶AP dq™æ£v1³?§ùÝ»ð5œàb~p?nŽ?=ë¼ðy±yÀ·iŽí¼ÀeV:¦ p@š÷ef™Â”¡f¶Vz?ïû‚™õOÛî®3³²>o3|îm}àQ3»HÒ7fÖ7sMÜzoNÖ9±dDü&ÞöOÀ~¸ Ÿifï¥ãûã­èöËó=嵸”7 ˆ8ÝÌn¬à+¯6’U[Ê·Ì}5ǚʊV—zÕb÷¥ ‘t$aqY4Ââ²n—uŸÊÌgRmïáÁ)øSùµÀxAÊîÀC’®Æ[Ã=œžÎÛ.ýÜ(éOxp>^lr?å¤Ë’Ù~GI«âQ@ö¼WFpÃSs Q’.˹æ x§'%ÝüËÌ~—ûY’6æ¥TæàÈtß•"é¼€¨95®\Ùëä\³•‹TÛâ†|:ŸgÏÕ²š AÕL¡B<Ÿ·"œ„GÁÍÍlФkS$í|žAÞ 44³¹éü‹%ŒWf6Åçd3•®¥ËöÇ‹¥†áæ¢èÛÌLÒ³¸Ð7ãØù’²¯sEãþx±Ì'äçc–Leþ5kß <]X.éáA<:o™µk•¬cšQ9Qí€/g)+2žgÛ”ÊV¤AµGt_ªF2©i|þúfü ÷agžöí„Gë§©zßzE¤¦‹K¤¦ë‘š®û„¡GõÓ_BÒ€üßïh¼lb&KAÔ_Ââ²ú™lfëâæŸ‘•>O´4³Ñ!ÂA„×föœ™mìÏqÏÀד¶+÷Ä ‚ ^B\ØÙëf¶9îö ¹ò8NAõ“âZÂÌþgf;ãUÔåuÚ ‚ êQ¬U˘Y>G° ‚ žqA‘â ‚ ("Õ.Ä’ŽKÑ‘Ô^Ò©’î”tfêÓZÙëu“Tf·#I­%QÉk¶NÍ(*;–í%],é·É#;‚ –‰š˜#ÞxGÒ,ÜúfàÿðVtOJÚª’^ÆÒ5ËêÿÛØ¸¦Œýù8øQè ’vNLçþ } IßãZSÓ+ßû2÷×bW¢ ‚ È4HÆOÆ}Œ0³3$õþŒ¯{‰÷Þ7ªøØ ¸ÖÌOþÒN篚®½ðº™Ý›~-i4Ð(Yà­tZ·{+ám Á[ÀµÅÛ¿-áÙÚôÝŠû6OÍÚ¾Þ ¡ð†™ ’´=ÞxbÞ'ù"¼—òôÔpý’œïäQ¼ùûõéœx«ÄQx_âÑ’Þdq3‹\fãK•Ú¤ñg~f¿_¥œ}m$Íe„oúžiÕAÔa2ñÀC@)°Ÿ¤FxŸÕ;Ìì}I§¿Á]¢¶7³õSj÷IO·}ð¾®ÃÓ57ÞÉþ03û@RG`¾™ é¼Åâ$]쬋÷í½$5˜Ø.g܇sÍlÏÔjñä´½ØÓ̦Iº7õI>ïuI€Óïß›ÙÔ])›ùÀñxÄ|Þ~±•™}›ÆÞ¸ï}œ3³ïËØW’Z#Î,-ØÝñ¨¼,1o–îµBÑ.oDçA5KFˆÿ’^§ã¶ŒÏ;M$ˆ7ÿÃþßtÎX¼{Rw`TVáL·£¯Õ²?LÒ6xÓrð¦ëãðˆ›tývxßÞûÓ¶7YZˆ×fq[·€“%­ ¬…·X¢ûçç¤ã¿ŸKj‰GÀÙ<Ü œ¼„ ÓIéøMðHü(3ûœ"}—³p_ê*‘²mXZÌs…»{žm‹~JšGÅB^®¨GtAP6!>¸ÑÌN–4O?¼”RÏX왜Û3øG G©x Þ¦ðyIOšÙ§’ÖÄçy·.ã:Ù|‚÷6~‘¥EàS`OI÷eö›ÙIà Ìl¤¤óñ¨ö`àv3;UÒãÀFÀ\ qˆÝr/žRퟙÙE’Îö•ô>½—™-gìu‚Ô¥irzUIÍ)LÌצl1o†[|FtACFˆŸÎN)Ñ÷Ókp‚¤}ñxdÙø&³øÀÌJ:‡3ÍìI§kt~7³q’F²X FâÑ5¸pŽ.O½Œßfqô €™Ý/i 5³o%=î­yºÎ[øCÅ’>̤Ësx (‘´>ç{pfºçSÄ=´>´y3³ÙøwPå‡I ðZ€ŠÒí]ólËN·/ 0Ñ^è™j":‚ Nýˆ«‘L?b3;§ØcY‘Ô”ŠÅ¼ ° Ð ï »¯…GçeŠ~}ŒÎ%mô6³Ç+8.ú×!¢qÝ',.ƒå3û¯)WÞq’޶3³#óìW$æ+—³¯­¤…T¢Š=ß¶å0:ï <–²LÇ˜Ùøb(VBˆƒz…y hZzýXÕë$C—BļoûÚ¦© ™TriZî¶ZŒÎÛàÙ„]a’N4³û+8§ÆÔS•ÞÞ鬽™Mªþ‘Aå!‚*þøOH¯*#©å‹y[|}}ysçP9!_êØ£ó6xç°&éu»¤cðÚŸ—ák¨’öþ ºHaf'WpZ.mgÍ«{|APYBˆƒ ˆ$—¹,^ÖWiÒºÿŠæÎWÂ×úç=.Eç³(?úî/YÌÐ _1TÒéförƸmºÆföH*¨ÜxËÌ~JÇ´Á]ôfà“ÃÌl|ºÇðbÑ+€-ÌlZ:ï&IýñÐæ¸Á,3,is 1𦙙¤ÕÓ˜óvA“´e:ÿ 3ûoŠº;à€/âC™"À×ÓçõʹÌ0|Ád`‹ôYâ¦Eß›ÙA!ÄA°œ“RÓӫʤuõå‰y+–ö§oŒGɃ$QŽŸ“Æ÷I—áîu%ÉèiÀéÚA„AP8ÒÏŒ¿ œefC «àø²¸¸ ¾wÆç‰Ÿ—4OÒÿáb×÷¿ÿ;ð1©Œ™}$éYà9IOà&C«§â¾òåñ0ðˆ¤Ié¼\:áÍfnÁÛŒÌÞif_Hj*é/xº¾±™=ˆÛù.A*  ‚‚Cj$ =êå­#ªŽ¤³ñÔï'·”¡‡¤õñ«9é÷FÀVÀwf6*ë¸_áÑï¬m›ï›Ù¬mðÆ2ߘو´­ ÐÜÌF¤÷MÍl”¤®¸hŽJçõãùõrõ$µÇ»Ì4³¯$­”®92ë˜~xÓ™2Sñ’úãÍlrz?Ö̦JZ mfSË:·º CºODÄA„™]¹ ç~žóû|–ž[%_”mfïæÙ6 /æÊÞ6¶Œ÷£sÎ{%ýº”½­™M&«1Œ™-eSуH:æ›2Þ­èÜ þ‘[AA-BAE¤Ú…XÒ6i="’Hê/éÈô³Ò ’:HÚ´œý-“Y@e¯Û¬ŠcÙ-Í9AÁ2Sñ_pÛ¹FÀ}ÀñxâÀ£U¸ÞÀ‰åìLªzÜ»’çôÃÛE®<œ~‚ ‚e¢€¤>¸£Lcàq3{6Eµà•‰šÙkÉF®în³.pµ™ “´6pnM×:]û ¼/ñ é÷$ý)U3n†[åí |‡/8 ¯,¼>éOÀFiÿR$íϳ¶­Œ»ÝtîNU½q·¦¸¾/…'éc|Ý`6ï_çá®:ïã½–ŽL=Ž×Á+++,Ú‚ ‚òÈDÄW÷g»¥FîãÖuçÇ&[ºÞ¸¥ÛS¸Ý5éØ'p ¸‡páØx)ûÃÌìæTÍøk`?Þ!d"ðnÙv1p±¤iÀ*À‘,Ýî‹äsð‚¤!¸¸afoIú'ð¤¤ÉxT}•™}Y@ñôÕ@©¤MÈoð~ð¤»pXÌlž¤k€§1·Ä»ü‚w\—^C€Õñ¯Ïó-ÎO&ñ×£Ò½_Š?,Ü$éP¼Í_Ë|2œßþ'¥Ÿ“Íì—Šn<‚ ¨_„Åe5’±¸ÄÓáWâQx{ü!¤=>'?…Åâ¼H¤+Ø6ÅÌÊë8d—Å%ŸÅeP<Ââ²î—5€™ öÏÝž–te söûxJ<ß¾v)5^‘`/µ-»okAP÷!®E’¿îøôª©¼,oÏ•/%ò’ZáíØ ¾³S鳫x«AA„/'$Qü)½ &¹™µ¡ìH|e¼Ú|©}’³d*½2">¿êwAP!^ÁI•ìSÓkDeÎM©ôv”‰÷.c_{Ió©X°óí›jQ¸A="„8(“ÕNH¯J‘¼¼ËK¥¯Bþ(½MZ£^•ùð™U¼Õ ‚¢BÔ©üÏéU)$•—Jï ôÍ·/‰ÿ|xCIÏP¸ˆg¯k‚ ¨5Bˆƒ:GªôžŒ¬Ìy’â©ôcñuÜ7±d$ÞèOž(]ÒB*·¤,{iÙªßmõâ`…!­µž(i,0ÁÌ^(ôÜäüV^*}2öµ•4‹*ˆ¸™M_Æ[‚` „8€äz6:½*…¤Ö”¿>|õ|û$µÀ‹è*]Ô–RÿA¬„Á2’"Ûé¸%jÁ¤ÎeåU¥¯ü*ß¾´,­RKÊÒû)±´,ê!ÄAP$ÒÜò¤ôª’šP~*}Í2öµ“4‡ª‰ø´XZÕOq,‡¤*ï1éU)’ÛZy©ôžeìk•ªT$Ü]%­ÏâTz¦k[9„A=ÃÌf3ðNk“Rém)[À»ëâ-Uïeq*½K w¡Em¹mRƒ`…"„8‚‚H©ôŒX–I¾îKÉ.µ¼Túeí“4ÂRè¹û¦®èKË$­gfCŠ<†5€Í€·«²&_Òf6¸Ú·œBA“¢Ú±éU)$µ¤lϵåù6’¦S…ùð”5Xø{²“=ÄÌ*]ñ¿¬H: ØxظVÒ6Uøþ®Bˆƒ ê"ɺt&qLª,//•Þ™2ŠÚÒºòÉTMÄ©úÝVš.¸çû7’N4³Wt‚¤cÒÛ Sp!ÝøxÈÌfKê œÌþ…7†ùÑÌÞOßÍY@)p0ðëL%¾¤¯5S£n #à2à p•™M•´OúÜwóŒQÀiéüU-ñèûfààÀàŸÀÜt½lžÂW4v†§û9 cf7Tô}Õ!ÄA¬¤ ï)éU)R*½=eGâ½XìÒ–›JÏTÃWv>|J2¥© m€†@+<:>8ÜÌÊË<ìˆg&n¶ŽÃí·À¹’.žÀî.®ÇÅ÷wx¯õƸ€¿–½ÎÌžH"{vú¬S€W€Ý£C$½—®w 0 Ï·Â÷`à`“ô™gà‚þ3ð:°.Èç§}Cs®3Ô{'÷ãÿn'wIzÝÌ>)绪Bˆƒ rH©ôqéU)’QKy©ôuÉ¥g\Ú*#âm²>º%°ÿÁÌJËæƒfö…¤›ñ(õÆ´}MàQà3ûOgÈ­| ìšÞwgi-êÜŽ§­O.Ç£Ù̸¿z¤ñ ¸¸O—g“ù7Èž.¨s ^Bˆƒ ê9½Ã¿¯èø}Žšå욌 wàà:IwÄ/Íß#é\@ß7³’Jñ¹ÚÓx'H:¸CÒDüÁà#3»?¥¦Ëãqà€ô£ð—lF×á±Wgï4³é’’t?9¿™šÅ,ݺN×mF9Õ‡¤s€vfvN±ÇRŸ‘t$°™Yä¡ÔKò-_ ªI½€OYœžžŽ ØÙxÚ×ÒqÏ×›Ùóå\«in‘YZ7Þ KTIͪâ.©IyË$5æ”åè–:®e¾,·DDA°|ÒŸkŽ›³œUÕµ¸ù*½Óì‚ÖaWµ IEkŽÍlvû—kÎBA°|Òø 8ÏÌ^,ö`‚ªBA°|ò¦™ýºØƒ–Å@APyªb%ÔMBˆƒ V0R¡U°œ©é ‚Z&U·ÀM8ZTóûé3Ö5³ —A-ã}lìdf§Ù—ám0ççW¶ JÒ}ÀIf–×MÒÀ f6¼Àë5ƽ¸ï®Ì8j“â ‚,’—ru‹cöû†Àl|íì,ÜG»¢÷ñÊ肎/k¹OÖ=6ÃûNÿ˜¼¼3Û;â]«2ÞÑ’#Uw`\¦%ejkÙ=ÝW—tú½Àkxõ¶Iº8+‚´Å=¡;™ÙÉ„££™MÈúì•Ó=ôLßQî˜;á¿ îÙÞ%ÛÒ3ýûõ~JÍ'z'Kz<­5Î÷}tÀí“t4Þ¹è<àïø¿e3¼u!À)}mföMÚ6OwÂÿÝÀ­8×Áý©{Hjž1á´ð`‹ûPÏbqêw>èzi{¦QC†,ÙÇzDú¹&Þ33æ—Ìl¨¤ÃðQçwd{ °uÖï3ñ¦óÌlr÷Ĭ”t­øc†A ýÑ«I‘‹ç%˽ÜßÇ—±=ßûÙY–ŠaqY‡‘´>ð7<‚œ€w5ºRßÂÓ¸'™Ùmeø4? œ*é¼ÓX3›/éRàQIÃÛ2^Š7‹¨ˆ!@oIÛàY~Ù;Sצƒö³[¥]÷â] nÂMIkâ "NÄ#ë_ã‘qI«˜Ù5¸‡u"„8* UƒÖdñN#¼¥2Å;“ñÎ2…F“Y+>fö¹¤[ðóàL3›#i_¼7ð,àøtxv÷q¼ê}ISð† —’:5™Ù-’>vǤV‹Íñ¨˜tí¥÷Ó€™Ù/’vÇ>Â;Få6Ø OEO?Ç›ÙW’~L¿‹7¬˜#éZ¼cÓxà”ÔÄâj`'\”ó‘}Ÿ7—±½Æ¨õ¦ɤûoø„û<—?°²‹Ó%Ý\jf?Tÿ(ó~ž€ ñ6dÓ³s+ð iú é  ¯™]\ƒÃ­7¤ÿžrî@üIøf–](›âmÓ*3'Y©÷…˜ê/ODD\·(¤éCP\ ŠˆSÚ¬Ð/w7I­©÷f渦@çTžÞ hlfÓÓ¾¶øŸ[ðy…]Íl¡¤ÀEÀ9©„|&ÐÅÌF¥ó:›Ùø¬ÏèŒÿa\¢„=kÿêÀ´Ì9éš ð?x³Ò;™Y¹½BS@w`Tš›Ú6ÝÏ¡’þœŽ sAHêÜÏOñÿ†¤~À©x:bÛM¾ ì ì–©~K\4L‘ÍgÀ€¶i"(ž÷ÿ,-¿ ¯Ž[l”;ÈTfÿÐWÒ3»ïOùð²¤oñaoIZŸWxŠ%Æãeö7ãÕx{I:ÂÌ^^“´ÞÐúï…|qIŒþ \Œ?84Ä…¢è¨L¨|„8¶ÐãËêÌ¢hƒÁrDeæˆß3³?Hê lœ‘¶ß éV|®`_3)é×x~~Fš<'GG3³/š <æ'!nçû[âÍ¢Áÿ ¿*é&`?3[@Ò°¡ÿ~<‡»«Ì0³ÏÓùçOH:Êþ–®Qßá)ë]q1Ù"{gìGðj¹7‡ò\ãYà4Ià™Ù~¹IÚøp>!¯ƒ/ ÿ³¤Sðê¾ÇÊ몸ßjYá,|Á|Yó’õÁT ‚  p!þŒ§ïv¸PµÆKÔg'+±Ýðu]gšÙ’.ÂEŒtþ½)­¼múüÓÌì“4œä…x4 µÞš ÄvÁ p¾N?'e]wž¤ãñTs3`÷´+»ýÿ€=p!=ÕÌ–ppɺÖK’Zàs»Ãpg™iŒ'áÑðse}Yf6 ·ƒÛ_ã¶ífXhfÿ)ëü ‚ þPëË—VdÊZ¾” nÄçÊ[“̬Â9æ jD±Vq‰åKu‹X¾T÷‰ž•µ€™½¬¢ýH©A‰âZœGðÎ%'y8AA!"³Z&U+ß_ìqAuƒˆˆƒ ‚ ˆ„AA !‚ ‚"BAE$„8‚ ŠHqA‘â ‚ ("!ÄAAPDBˆƒ ‚ ˆ„AA !‚ ‚"BAE$„8‚ ŠHqA‘â ‚ ("!ÄAAPDBˆƒ ‚ ˆ„AA !‚ ‚"BAE$„8‚ ŠHqA‘â ‚ ("!ÄAAPDBˆƒ ‚ ˆ„AA !‚ ‚"BAE$„8‚ ŠHqA‘â ‚ ("!ÄAAPDBˆƒ ‚ ˆ„AA !‚ ‚"BAE$„8‚ ŠHqA‘â ‚ ("!ÄÕË*€{AÁòC£b`EARs 7p|±ÇAÅKÀOÅDP62‹.X±ÔèefO{,õI+›Ù3ÅK,ü?`ê~öY°IEND®B`‚sipp-3.6.1/docs/controlling.rst0000664000175000017500000001303513730472040016072 0ustar walterwalterControlling SIPp ================ SIPp can be controlled interactively through the keyboard or via a UDP socket. SIPp supports both 'hot' keys that can be entered at any time and also a simple command mode. The hot keys are: ===== ====== Key Action ===== ====== \+ Increase the call rate by 1 * rate_scale \* Increase the call rate by 10 * rate_scale \- Decrease the call rate by 1 * rate_scale / Decrease the call rate by 10 * rate_scale c Enter command mode q Quit SIPp (after all calls complete, enter a second time to quit immediately) Q Quit SIPp immediately s Dump screens to the log file (if -trace_screen is passed) p Pause traffic 1 Display the scenario screen 2 Display the statistics screen 3 Display the repartition screen 4 Display the variable screen 5 Display the TDM screen 6-9 Display the second through fifth repartition screen. ===== ====== In command mode, you can type a single line command that instructs SIPp to take some action. Command mode is more versatile than the hot keys, but takes more time to input some common actions. The following commands are available: List of Interactive Commands ```````````````````````````` - ``dump tasks`` Prints a list of active tasks (most tasks are calls) to the error log. dump tasks - ``set rate X`` Sets the call rate. set rate 10 - ``set rate-scale X`` Sets the rate scale, which adjusts the speed of '+', '-', '*', and '/'. set rate-scale 10 - ``set users X`` Sets the number of users (only valid when -users is specified). set rate 10 - ``set limit X`` Sets the open call limit (equivalent to -l option) set limit 100 - ``set hide `` Should the hide XML attribute be respected? set hide false - ``set index `` Display message indexes in the scenario screen. set index true - ``set display `` Changes the scenario that is displayed to either the main or the out-of-call scenario. set display main set display ooc - ``trace `` Turns log on or off at run time. Valid values for log are "error", "logs", "messages", and "shortmessages". trace error on Traffic control ``````````````` SIPp generates SIP traffic according to the scenario specified. You can control the number of calls (scenario) that are started per second. If you pass the -users option, then you need to control the number of instantiated users. You can control the rate through: + Interactive hot keys (described in the previous section) + Interactive Commands + Startup Parameters There are two commands that control rates: set rate X sets the current call rate to X. Additionally, set rate-scale X sets the rate_scale parameter to X. This enables you to use the '+', '-', '*', and '/' keys to set the rate more quickly. For example, if you do set rate- scale 100, then each time you press '+', the call rate is increased by 100 calls and each time you press '*', the call rate is increased by 1000 calls. Similarly, for a user based benchmark you can run set users X. At starting time, you can control the rate by specifying parameters on the command line: + "-r" to specify the call rate in number of calls per seconds + "-rp" to specify the " r ate p eriod" in milliseconds for the call rate (default is 1000ms/1sec). This allows you to have n calls every m milliseconds (by using -r n -rp m). .. note:: Example: run SIPp at 7 calls every 2 seconds (3.5 calls per second) :: ./sipp -sn uac -r 7 -rp 2000 127.0.0.1 You can also pause the traffic by pressing the 'p' key. SIPp will stop placing new calls and wait until all current calls go to their end. You can resume the traffic by pressing 'p' again. To quit SIPp, press the 'q' key. SIPp will stop placing new calls and wait until all current calls go to their end. SIPp will then exit. You can also force SIPp to quit immediatly by pressing the 'Q' key. Current calls will be terminated by sending a BYE or CANCEL message (depending if the calls have been established or not). The same behaviour is obtained by pressing 'q' twice. .. tip:: You can place a defined number of calls and have SIPp exit when this is done. Use the -m option on the command line. Remote control `````````````` SIPp can be "remote-controlled" through a UDP socket. This allows for example + To automate a series of actions, like increasing the call rate smoothly, wait for 10 seconds, increase more, wait for 1 minute and loop + Have a feedback loop so that an application under test can remote control SIPp to lower the load, pause the traffic, ... Each SIPp instance is listening to a UDP socket. It starts to listen to port 8888 and each following SIPp instance (up to 60) will listen to base_port + 1 (8889, 8890, ...). It is then possible to control SIPp like this: :: echo p >/dev/udp/x.y.z.t/8888 -> put SIPp in pause state (p key) echo q >/dev/udp/x.y.z.t/8888 -> quit SIPp (q key) .. note:: All keys available through keyboard are also available in the remote control interface You could also have a small shell script to automate a serie of action. For example, this script will increase the call rate by 10 more new calls/s every 5 seconds, wait at this call rate for one minute and exit SIPp: :: #!/bin/sh echo "*" >/dev/udp/127.0.0.1/8889 sleep 5 echo "*" >/dev/udp/127.0.0.1/8889 sleep 5 echo "*" >/dev/udp/127.0.0.1/8889 sleep 5 echo "*" >/dev/udp/127.0.0.1/8889 sleep 60 echo "q" >/dev/udp/127.0.0.1/8889 To send a command to SIPp, preface it with 'c'. For example: ``echo "cset rate 100" >/dev/udp/127.0.0.1/8888 sets the call rate to 100.`` sipp-3.6.1/docs/3pcc-C-B.xml0000664000175000017500000001144413730472040014721 0ustar walterwalter ;tag=[pid]SIPpTag04[call_number] To: [service] Call-ID: [call_id] CSeq: 1 INVITE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test [$1] ]]> ;tag=[pid]SIPpTag04[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 1 ACK Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> ;tag=[pid]SIPpTag04[call_number] To: [service] [peer_tag_param] Call-ID: [call_id] CSeq: 2 BYE Contact: sip:sipp@[local_ip]:[local_port] Max-Forwards: 70 Subject: Performance Test Content-Length: 0 ]]> sipp-3.6.1/docs/sipp-04.jpg0000664000175000017500000015664213730472040014720 0ustar walterwalterÿØÿàJFIFHHÿÛC  !"$"$ÿÛCÿÀmK"ÿÄ ÿÄP !"1AÑ#2RU”5BQTaqsu“³78d‘²´Ò$3±4r%C&6DÁEFS„ÃÿÄÿÄ= !1S‘4AQaq¡²5Rr±Á3"2ðBÑ#Cb’ñÿÚ ?â› OimÅirTuh $›ýׇêü÷ÕÅ™(i!J‹ ûޏÛ(CcÐUoRB{\ÝCõV鎷†äÇ¢Û —.HJnmþZ¸ÑÚ XMNKu.‘°Òa4ÎH[á䌔—BKˆ ¥?”’Q¯aË™FÇ7®*&ª£¦šiÆgˆìÃÎuéÖà.÷+L£{š«Ÿí‰Ó385äÕPѧ>ܶu—Xm‡CjJT.›ØÝcX—Wïÿ£‡÷VþÉUn"<Š“"ˆËt(ó4¤9æYó­k+RÂOuv õîoÙžÍ赆vd9ÏÔDýØŠšÚÓÈCPD@rh¡Eì•ÜùÛ°6õ9îYRlîœ}ö‰§û¦#pœ"8Xáá»Cã'Y×y⮕EQ„Oùc׆¿7¹×‹ÿƒ‡÷Vþx¿ø8uoá«m[jQ Ñ`6Í?rÔªS6œ}Ä$ ¸Ó<ËU›q‚RÚP’ï/bAÀújßOö1L~]Ÿ>¡&AÚÀ¥Õ£´ð“Æ¿ëN*-¶¬RSaÌ‘šNj)Ru±i—.6tEuÎ8õkï·¯1Ãc§#Þ*ªhˆÓuö¹‹ÿƒ‡÷Vþx¿ø8uoᮦ6ÖÖ¯û0Úõ¦i’iQãíºÝmô6ëNI’#Jf܉ÚÜJN>B¤öPI ž§{#Û°7ÜH1êUÜÑž/±ð°ªašÒÒ‡[RJHAÉ+ÉC!‡`5§9.TÓW0ª8Z&>™Ã«¶~B¼LÆbpÓoý8w‹ÿƒ‡÷Vþx¿ø8uoá«ñØ{m[|•WDæýŸµ»œ”ä¦ÖÙYî¦!¤›•Y|®<¦ÚÃVÙð£VP¨,Åf“*|F§8eÌr#2].à\-ð¬Ã”€ Ù×J]N¶hË—*£×Wo‡†žæ*²5â™Ã^¾Ï÷GzâÿàáýÕ¿†ž/þÝ[øk³Q½œlö7Ý )u¢š½IÔRÝ“§Q"žšR$.HKÑËd‡žC~aú€É$Œ~Ŷ>ØrVÁ¯*+{Še§˜ì‰qF›Ó:鎸Š`•Ù)¸W ¹NxˆVµ¦qÜ颪¢™œ#X}Z4èñ˜×¯F)Èvó11¦{|4ú¸ï‹ÿƒ‡÷Vþx¿ø8uoá«–ÕØp*Þͪ¦oSNZéU Å%j_[±â³JÐJIùÌJùr †ÊA:‰©íxkDEBqÚ1T«ÃpHn)œû‰un.16@Ž”´H[ŠMý.IH2yRéiTÓªp¼gÓºbqÁ«^M¶¢"f5é:ü?>?ÿî­ü4ñðpþêßÃ]kmͬÆÍ¹X¥1ò‡uøXªñ¥ÑÑDBÖãЖ JÝHeOy°Hí©ú'²š|‰1või©¥[¹Úk¬Æ•K±J\¤¯©1’áÈ¥$#Ê•‹§.úÒ¶Î ”ÕÂ׆‰ˆÃŽÜ;7ë†ÍžD·®)Ã\õc¦5ëÜã^/þÝ[øiâÿàáýÕ¿†º%cm:†Ï‡¼Ýi˜rv½V´)ÂsJq+„ëiÇŸ„ V—Gn+¤ƒÝ^š¯o—áÕJ{tÕLËÛ±«¯4SΨmº´%!IJ|åP¸÷»¾Vº^-xªtN˜ÓqŽ1ã¢{»ÚöÙ.ÚÆÏŒª4xõvú¹âÿàáýÕ¿†ž/þÝ[øk²ìX»j³»6^ܯmí¾ºÌ™‹¨ª%66-B¦L5%Yã®:Ú³ŠqE*VX…bd¶ý‚äjuuÊs¦ÑvƒÒ  ¶¦T¹Ó‹2”–HâAZHºR-rSbI1vùÍaaTÓ]”ã冚¦˜Â{4NØ·¬²¥¬p©®0óìÇý‡ñðpþêßÃOÿî­ü5Õ6öÇ 9#Ú")NL‡¾ŠL7jŒ1Ü,²ó¨qyw*UœrÉGJ7ZÇìn…·Ä:Ìä¹HÜN3R¡EnA‚ãŒ%©U´ò™-$(©Ù„›yT :Ù¯8.±g]¥4Ìðx=_Táå†:qòņœ‹m5ÓES†8õÏWýÿúåþ/þÝ[øiâÿàáýÕ¿†º.ân6×Ú;¸»M¤ž¶·;oíxSc­ÆÐ܇òË‹H}Î&ÀBVPŸKn{ Pš[”¥RkïÇ©Ðâ!õÀul¡¹5´ú$´T¤ flåP$êê²Ý•7zï퉈Žüp݆1žà¤dzæÖ›.™ÇË 8hý9w‹ÿƒ‡÷VþþøÁýÝ[øk«TaÒfRwnئíñBù7O­ÏN†+êšÛÞm-f–á i‡’ê W²±ã¦2þ©ÿ-näëí7îppíìSæÕ¾\¿¤àãV8övƸònxÁýÝ[øiãô8uoá­>™U_å§L¿ª¯òÔŸÜÒÆžÖçŒÐáýÕ¿†ž0C‡÷VþÓé—õUþZtËúªÿ-8®ã{[ž0C‡÷VþxÁýÝ[økO¦_ÕWùiÓ/ê«ü´â»ŒiínxÁýÝ[øiãô8uoá­>™U_å§L¿ª¯òÓŠî1§µ¹ãô8uoá§ŒÐáýÕ¿†´úeýU–2þª¿ËN+¸ÆžÖçŒÐáýÕ¿†ž0C‡÷VþÓé—õUþZtËúªÿ-8®ã{[ž0C‡÷VþxÁýÝ[økO¦_ÕWùiÓ/ê«ü´â»ŒiínxÁýÝ[øiãô8uoá­>™U_å§L¿ª¯òÓŠî1§µ¹ãô8uoá§ŒÐáýÕ¿†´úeýU–2þª¿ËN+¸ÆžÖçŒÐáýÕ¿†ž0C‡÷VþÓé—õUþZtËúªÿ-8®ã{[ž0C‡÷Vþüø¹ý'Ý[ÿ޵zeýU–¿:þ©Ó‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µ·âçôH¿uoþ:x¹ý/Ý[ÿ޵8õtà_ÕÓ‰î1§µºÝQÇKmÁŒµ¨„¥)ˆÙ$Ÿ@<º¶/hnÆÒ ôÚu{ÐýBžÒÓú”•8 Oê ¨Ÿę{LÚÆÞ•˜ŸÖFºƒ”­×[Øn8Ä9Lj2Ý2 Í#yœJ²íнÞíCåÝwkZhŽ D᦮ÙÇ1Ø’¹\ìí誩ÆpìÿgµËëPkT€Â¥S)Î"BËm9Ɣږ?#&Š’ÜyI¿qÛ¾¥—´wchô½¿GÕ*ö–ŸÔ¤©ÀRQJû3q2*1›'wÐI·»ÿWð×D‡[®+dÐdQ6->¾‡á©n>õ%rNaçQŽIýHOo^úнå;Í\ i§œ&gDi‰˜ëÑ«¶qmXd뽤MSTá†?î‡Ük´qRé”×!e¶œ‰ÒÊB–-äÉ¢¤…wRoÜvÔ«›|õ„Á#ºOiiýJJœ'õÕÚ;QÑí.¥+M´Ôoh´”!¶Ò–ÒœvÀz~a®©±…¯´è²«±)ˆ}×–ŠHuü:õAC $ùBÀ7ïanà/žËùÛ{ÉÓeE•ãTLã18Dzt—lâèr&m]/´ZZZUVÌF‰Œtð»~ß(ÓÔùZ¾ÞíÙÏÆ©-¦éî(©1çR岬”ôuU‰î›ƒcéc®Ë³¿ ­µl‹¼©•éUÖS-øØ ¸BŽ*< Êq*ìYXaª'´ä¼îÙÝ-JˆÄIMî(Aèì€e|s  ‹ Ø |ñSd ΂;‚?ñ¬×:èÎ|“e|¼ÑV34j™Ž½8wv¶/w 2&Q´ºXÕ3LDNžø‰ðëÖ¹À©R Ó¢µYiÃ}6q•Éàço°ZBû‘Ø‘qßözêÍPöµ\—"Ñ( ©öÔ\<ì¶ã®_$•8ÓhU‚ÂTA½ÊEïé®Qí#ñ>ÞýÓßêN©:μ©¥]•tLðb#ES0ÇTx°d»¤Å„ÕEXp¦qÑׇéÜéûªŸ3Õ˜òi¤¡N­ääá£æ=ϯ®·U¿ãŠ[ô–7L¨ôéaø‘ê‹e—‚Ò ´!`*éƒÛ¶¾ÓTœøµªˆ³ªÂ™ˆêœgW‹^œÙ²¢¾6•D÷hü;Üó E7w#掀)¦¡Áy€Y Àù¼ÝÓë­‡½¢‡•½»ç8Š{ˆvK«¸ZŒ´$¥ mx ¥$€@¿m|ù¦­ç¥Xãý5ñóíñ_ÍêpÃŽ«Vú»<@Mö…×íéU—ËÈ‹ê+¯9v\).7æpùU‚2‡{ë!öŽ£,Ì;Î¥Ô*—Ê+Nå̦xK—äú\^@}B{ |÷¦¬ŒñÂ0‹­—ÎB™×o^÷zùmÆFå’ržŠb‡Š/ÿF‘ayû5Üùcspo©:¿µ(õ*´ggSy.f-3$!RœÍ*RÜmËPJARÚQQº”J‰V¾rÓJóÂký:4èÆ5N=]úpÔS¸8ákVžÝ=Xu÷hÇ[è?þ"޲$Á»çá2¦!¬UÜ+jJR¤³çù R”ƒ…»­q¾asS^ä}ÅÒ–é¼µ5,BH „)d7Ùî›(×ÓWóÒ¬qþšŒßûë9½NqÕaâïêö€…S¥SŽð¨ˆ“ꥲ+N„É. 8]³Ÿ8T;¯qÛXeoJd¹ ‘.¸$¸†ŽÙ~o'H¾  )D%##d‹ç\MVŒ÷´¢®7z"Øü)VnQ]<­j˜ñóüé}Ï´~Ñ÷D–‚Û­C,ÔÔŽ™.›¹ÅŠÇW¼¦Äûõ½´}©·¶+qªð+lÉz4·ç%©³Vë.Jy¥´·ÝO .8Rµ ‰ËõÛ_8é«-sÊm©šk»Q8èŸ=kìòYLMÕFï£(^Õ¥ÑߨÔZÝfmj}t•TçÏq÷ØiD(©¥B «©66ÞÇQ«ÞÐVõI÷7,—$UàŸ!uE©éMbÆã…y)¤ Iµ…­® ¦¬£;¢Î©ª›µÌaä¾¼‰5ÄEVÕkÇ__û«³©ô<¯iËËó7½VRØ|Hd?[uĶ轖”©Â¡sbovµâû@j-J]R6íœÕJbR™3‘WpIu) *{<È)½» p 5’3Îb0‹µxvëÞÇ9&q›j·öjÜî°·m =ú| Â!Æ‘Q_Dy¼\¬«é6¢•¤ªÝÁì}÷ÖÕ?E¦¸·i{‘Ês®#N¨)¸8ä…kÚþíp 5’sæÖbbl)ÂtO|1ÆmYÄÄñ•c|û]Õ;¶Œ˜°¢xÂkj# ›“L%j ^*Å%J’Éï­ª~þ‹MqnÒ÷#”ç\G…P,8SpqÉ ×µýÚàiÏ›^ÑÄS„õu›Vs\W6•c}nìßInò}ºâ›£•‡˜î¨¥¶ŠÍГuÜ í¡ý£ùÉøë…iªÑŸvÖxÍÆ>*W›6V˜pí*œ;]×åmíŸÎOÇO•´?´b9?p­5“âëeNùYÍK¿×.ëò¶‡öŒOç'ã§ÊÚÚ1?œŸŽ¸Vš|@½l©ß'5.ÿ\»¯ÊÚÚ1?œŸŽŸ+hhÄþr~:áZiñõ²§|œÔ»ýrî¿+hhÄþr~:|­¡ý£ùÉøë…i§Ä ÖÊòsRïõ˺ü­¡ý£ùÉøéò¶‡öŒOç'㮦Ÿ/[*wÉÍK¿×.ëò¶‡öŒOç'ã§ÊÚÚ1?œŸŽ¸Vš|@½l©ß'5.ÿ\»¯ÊÚÚ1?œŸŽŸ+hhÄþr~:áZiñõ²§|œÔ»ýrî¿+hhÄþr~:|­¡ý£ùÉøë…i§Ä ÖÊòsRïõ˺ü­¡ý£ùÉøéò¶‡öŒOç'㮦Ÿ/[*wÉÍK¿×.ëò¶‡öŒOç'ã§ÊÚÚ1?œŸŽ¸Vš|@½l©ß'5.ÿ\»¯ÊÚÚ1?œŸŽ¿Ÿ+hŸhEþr>:áziñõ²§|œÔ»ýrîŸ+¨ŸhEþr>:|®¢}¡ùÈøë…é§Ä ÖÊòsVíõKº|®¢}¡ùÈøéòº‰ö„_ç#㮦Ÿ/[*wÉÍ[·Õ.éòº‰ö„_ç#ã§Êê'ÚœŽ¸^š|@½l©ß'5nßT»§Êê'ÚœŽŸ+¨ŸhEþr>:áziñõ²§|œÕ»}RîŸ+¨ŸhEþr>:|®¢}¡ùÈøë…é§Ä ÖÊòsVíõKº|®¢}¡ùÈøéòº‰ö„_ç#㮦Ÿ/[*wÉÍ[·Õ.éòº‰ö„_ç#ã§Êê'ÚœŽ¸^š|@½l©ß'5nßT»§Êê'ÚœŽŸ+¨ŸhEþr>:áziñõ²§|œÕ»}RîŸ+¨ŸhEþr>:|®¢}¡ùÈøë…é§Ä ÖÊòsVíõK¾Ò·Ý2™T‰R‰Rˆ™Cí(º‚Р mÎ¥Nøöh¬IXc3rÜ}Ã-¤ŸP€¨êPOæJ?¬úëæÝ5¯mž¶¶ÓV1uUGâa±c›Öv11Es„÷Dþaô”_i[fŒ–Q¶WÑ„Of ·&Ô’êÞg..éCi Nkìs‘¹6þ+|{5QQ*ìçܶÆâŽ–ÒO¨@Te('ó¥{Ï®¾nÓX§<*Ÿý1ÿÚ¼gÆqÆ|õ2FC¦1ÿÉ;£ñ†'Ó4 çÚ”*G³êtǧ. ÕA-*R'Ê}èéYoÛhò )Ã`›÷$’µÌÓªJÈÿð—ÚCY÷Sl:¤¶“ù’ J üÀ©GõŸ]q_À·ûÍmýòÿÚ=¯µ7Îõ¦íöã!èë›5ôçÓ°§CºÝ?˜…þ¤{€Qל¥uËEîëM\S3V:uéÆ&uu·nq{ÉS3u·ªžá†Z0íóÎë§ntmÙá{9Ý´ªg*fÍ‘PŽãË%¤,$•†[J”¸áôü¢I6àÕsz“Ä×ÿø×ÞÛ’§w²ÍÑP§>‡cH¢à¬UÛºF±Æ¤SÝ£ÎnšDy P€ãR‹ä¦öMÒÉnê°·žÝÅÈï`‡ÓSiö¨Âs{¦‘"AB`6Ô ø*µÓu2ºnoç·cb{_$ú27Oühsy_KKàf`áI½ÜVl&éðœ•ß²N‚MNH¡Ó:7 öŸÏ’KlÌ ÇÄ\fÀQËÐ`•wõ°ï§Óà¬UÛºF‚MLF¤SÝ£ÎnšDy P€ãR‹ä¦öMÒÉnê°·žÝÅÈïdšE=ª0œÞé¤HP… µ(> ­tÝL†î››ùíØØž×}59>‡LÓðïÞWÒÒø˜8Row› ºG¼'%w쓤Š1©Ñc£xÐßiüù$¶ÌÀÜ|EÆaL½ W[ú=59àtÏèþXÐø89zΜ9en;pg•»ßmùWí¤z1ÙÒ£¯xÐØiŒ8ä¸ÌÂÜŒ…Î, }iOKŽú=59‡L“Ôso.'ÔÒ9Ù˜y’-gƒ ²O¸+vq©÷hÆs›¦‘@BÔ 8Ô¢ù)½“t²[º¬-ç·qr;Ø!ôÓ]e­­´jušm%˜.ÅŽõz52T˜î>&CJùClÈaô¥C„+‘Æ”¦ÐY8 z8›MY¡íØ:ìÔ‰Õ5Ó$òN™NK©¤æêRêÐqAA*RДä Ëhºõ`{amúpª»VÜ•ªCU8/@AR ÄÆ_ D‚ÝÂîPáìr*IIl‡9ÓV­»A¦ÉªÕaJzsNµN•2žÜš}„†Ñ×Ò·,òKD¡(ZmÈ’T/’~“dízu~,‡$ÖbCkjV˜vC·±Cϲ\*=’–¹H ¥7FaUÓVúÎÝ 3¶¨²©³«’ªóéjœ¨¾…3äyä:sK¹%(K+?AW È”…£FµK¦ÇÙ”Zµ>K¯®T©,I/EâZm¸Ê(:´­±ËåV(Wu\(HW´Õ«qíÊ"Sô£¹]ñˆukOÓÔˆè±Z]u¡kRÛBvÒ¥p@Ǿvä*F¨ 5¤f Ên7)ÇÊ—#Èy*PÈ$””… ÚÊ Κ°ìXô·ªÜ«0ӌNJ\B¤¡óµ—œ¤ÿ:²ˆýE4”«Rõ­·En£SŸ!Rh´H ,¦(ñž2#—wÎYò¸†–á¾%%iN¸HQôÕ¾&ÚÛNÒëWwT‘ ›TD4­ªYR¥´â]-8ÚTâl£Â¢P²€”÷ÉJ²X{—vSuùUø1$HŠô¸±~:Ûmn ¤äð{‘Jia! -$” …Õ€RôÕ⣱aSväj¥CrFiõ1SñP¸Ëp0þ Mó‡–èC©QJÛm6J컕komâɃ\ñ5Qê‚›4˜…”‡Ì[-’¢UÙ……Ü$%V .$ç ¨i­ª\ffNn<Š„jsK¾Rd¥Å6‹{†Ðµwµ»$÷"ö:’ð:gŠt,h|½g β·¸3ÊÝïŽ6ü«öÐAé©Èô:c³¥G^ñ¡°ÓqÉq™…¹ œX*úÒžþ—ôC¦Iê9·êiìÌ<ɳ‰Á…Y'ÜŠ»wHÐA驈Ԋ{´c9ÍÓH !jjQ|”ÞɺY-ÝVóÛ¸¹ì“H§µF›Ý4‰ £¶¥ÁU®›©ÝÓs=»Úᦦ+TŠ|©z&é¤U–¥„–b5)+H±9VP›v·c~ã·­¡ô 5м*‡ÿâäï‚Fðo”>Ñs=7Yòg•¼÷ÊÙ{±òê¡C¦Â¨óu{‚™GãDZ¹ å½ï NzX^öõ¿{fšè^Ï6].°ÝUé5dõ6Äöa´š›Q~q¨«qT—Š-e† $ËS… l¥uè‘`»ìâ¥9PšâÕâ4‰AkÌ´ë2J›)Ë ÊH8åÜ÷"À{MmHDÁЏòd¹-Yõ-9(m»& %wÍÒ›Ã/]olš\zÞó¡Ñe­ÔGŸQÕ4@XCŽ%$¤Eì{\>šè[aÇ-¨ò6u2ŒÃ°ªŸüÁÍ,‰Ì…€\YÉ¥&ê RèÄ;.Ê´@ÍiÔÒ%S•PnSÑ›iÔ¶Tr•ºlò Ø­à’’›µ%²½5}¨ìJE&=RE_s:Ê)Ò¢6Dh(_fTu<Ê›){å’rNARãžP¥'ÙÚ¯µD¬×š§Ê‘^v‡mFSí)æVØt¨Ý*H<ȲNJ¸Wóh(Zjð6,%íEWFäŒÏ;ʀ̥ÆeO0ÓŽ f’ÿ*]Qia)m·rŸuQô 5Òë[b‘Réé[j›™oVãṘdImèEÎT¶Ô¶]E¹ÖB‹Šd© ,Y)Mî¸:NÝÚÕÇA¤R÷=M~)4Ãx¿HKnF*Á-/ú’´©K ùÁHA6= †š·íý§K®V¤Äƒ¸r…S6c¬´ÂoÊ–¬Ï3ÈJ’Tãd)Å4H'ÊU½Lö}MN°‡÷,N¦®;bS"+‘o¡N!7rClÝ)BÂÂ]]”,œÅÔ…¦¯°v 6Kp©¥ÏŸW‘EŽÜxœ¬9%µ4â] ÇPy¹`±q‹k"… i©‰4Š{Ta9½ÓH‘ ¡ 0jP|Z麙 Ý77óÛ±±=®­R)ð"¥è›¦‘VZ–YˆÔ¤­"ÄäyYBmÚÝûŽÞ¶“ø(#ð–ÚKUì•K&ÂÿþÍÿv¾ßj*%QêŽtdTi,ºøšÈRÒŸAÝ_³õ›&ÿD[áÿÀÏûÊm?Û3ý›úôª?»Yì/3a3„c‹¥—Öã{†•NìÓwE¤&œÛk¥Èpµö׿ìµ(„ad‘ÞÀyR=Ã_Ôÿ?ûïN}¬ÿe[«ø<¯é+^cTÿHýáÿλüÙ·›k­¥XaýÑøDÞlø»hŽïÛõí#ñ>ÞýÓßêN©:ºûGüO·¿t÷ú†©Zç3·çÞ^Øm䮋O^é4Õ÷ÙCîÇ‹VSk‚–Â\B«Q©ÒEº2’•6ìsÿÕhƒ‘Ḡ©9ÒhÕÚ4ô!V£Qzƒ“k•\‚ûŒ»ÆZ”–ƒ©+$‡O æ8¤qÝg“œH9~šêCkíJ–ÄÊtºC91dºÓÎÕXmÖÞG eµrIoˆ)H@ °íÒ¼¹—ÍFnô*fÐÛµÔ ½*\˜­¥<´º—œ) ¿›em%„Ý#0P7 š¼{Wj’õREJ m@§¿5åAM2ZœzK QRã\‹ bŸ!KFîX âB+[^¡V¦WcÊ¡Îèj Ɇdr¥®>T–ÔsQUŸ9#[‹\fšìRê«­þ;Z¨R*”È•ó$V’Â"Â2ùRã/¶ò2q t”§%÷ùLnÆÛEPêÑ«ímW*1¥çwH.×xÁD€…6§S² .΀ S“aËô×^¢íog¬N®S¥ÏPáª:ËNxŒ\„$°ë.ª[ T»ù^ (NHGÑ_!Ð4ÓMM4Ð4ÓMM4Ð4ÓMMY½”j;OøÜ?ë£UMt/gŽUâíi’)ušcjyõ°)ÏÕ£CK„¡7vJ]Z †€UÑÉE«'$=³D¨¶Þ΄ثFjˆÝ{5HjÍPY•Ó­QвyC8–t!A8'Ì8Î!Í5'\ÜúïŽW*uN ¸zÉk{+eŽDÚö·­†¯º‹nlé­š´ghŽQ 3K€&¡\5½BÓ+&Tp—wJš¼Çe—}/h;ìûn;F”ë°#UäÚžzV&tëC ©Ò—]_"‹ ùÅ7bUÙ(BZB‚ŽöéÜÏVY­=¸ªîTã ¶ÌÅÍp¾Ú^T¬œ€ó+°?”>²HÞ;ºLè³änšãÒáçÓ>åAÕ8ÆbËÁEWNC±·¨õÔÆØŸ¶[Ý1¥CJX‹1 =P©6ðL¥GZb­+ ´)x æM’qUÓ‰:’¨×k±÷, ØÔ:üÊr)Õš²j)_s´©Éh*¸KB=Ö•%8å)ÐTiÛ§sSeK—NÜUxr&¯’[¬Mqµ¾»“’È ¨ÝJ77õ?ŸF÷Næj,Øî*º#Ï[ŽLi3\’·–§첡ؓ{ûõ/¸7¹ºêuZÔåI§8™†âj(Žùi¦ÃI[åM;›ªBTA,¡ol˜®ËÛ›Ê< „0*1PÌh“ë‘™[Ž¢\wqMäR×%œÄ'ébqÐT¬UÙ£=Ef«9ºd…‡†‰ 8±™H|©îGäÍ­©;§sJ£ ,ÅWz˜†Ä7&¸¦lS8Øb,-ÚÃójÕ´§<ÆÈžÔÊÔo T)D1Pm ¡ò—y¡X9%Õ(µƒÉ¸hñ¨©A¢„sÝû§s;G7]qà-·!´©®F[bÈSböAHìµ½ÚÕ­V*õ¹I—ZªÎ©HBiv\…¼°€I D›\“oÖutÞNUé6UeºÍ2EB ±V5Æn\‡Z!k%)lµcb€,ŸÕ­]Õ^ŸVù/G¨U~Q?ÒŸv£PRÚS²xÔX/¤¡¤¶†R JJ/ù­b«E¬Uè’•.‹UM´ÔìI ee‚RJH6¸ߨk, Á_§Õ$U`W*q*rç”ĵ¡çrPR²X7UÔ7=Ⱦ¬;¹HÞu%Ä©Á¡.:Yéjl°¢‰1e:²”p9Éãä?9r…Ù}O¥7í{Ä7\ˆ1ªæ£æd2äi0@9º¥¼§Ây1 Ò§Wrá!Ob ù½Ó¹š‹6#{Š®ˆóÖã“L×$­Áe©Á{,¨v$Þþýb¸+ôú\ŠT åN%>N\ñX–´2îI VHʺ@ã¸Ö˰›¾Ã,S&5ô6‰oÌB$  e´â÷k{m{EÝ9Õ “SäÊ©Nã+™&t’àS`„(„º”»k+©q6ZăPÓA'pWéô¹¨ÊœJ|œ¹â±-heÜ’¬ •t€ Çp-¨Í4ÐI×7~»Ãã•ÊSƒ.²ZÞãÊÙc‘6½…íëa­™ÇwI|Ó\z\<úgܨ:§ÌYx(ªéÈv6õºƒÓA'ò‚¿ã¾=ã•?ý?«_Qôpÿ¹|¾—×Ó·¦²³ºw35—«Lî*»u9 ½1\¸•K"<©ìOäͨ}4ˆÞ;¹Xnšâx_\†­Pt`êóÍÄù»)\Ž]C¹ÍWõ:ˆ—"D¹OK–û²$<µ8뮬©n-FåJ'¹$›’u‹MM4ÐvÀÏûÊm?Û3ý›úôª?»^jþÞSiþÙŸìß×¥QýÚ¶uª‚ö³ý•n¯àò¿¤­ySüa#÷‡ÿ:ôçÚÏöUº¿ƒÊþ’µæ5Oñ„Þzhô;Oº?{ïóLJíýöøŸo~éïõ RµuöøŸo~éïõ Rµ¿8¶òöÃ>Jè´øÕî”…Q«A©M„˜ÅŠcùEÙm4¤¶N „­@¯ÌBlMԑ꤃«6ÊŸ@‰NÜ+’êq¼JqT8H‘¤4ò” §[ÿü! ß2{ccsØÛãdmùU$ˆÕv©’jî¼ &1qÁºxÙp¦SYœsIKÁôwì<ÎgÎ$›YdÇ‘ÀÜ–eep%Ä’…¤)*±÷¨}àƒïÕöï¤Ó}ŸÏÛê•Å©îd6†XT4­*Q .)¹EµÝ6* aÅKAa! N®öÞ_*(¬šwrË––#6)ï»”6i¤¶©•¨¸¥„­Eà¨È5{ÔVÊ™¨)×Ý–„6•$·tJeÀÚŸ^!¤$òÍBb·-L:˜î-M¡Òƒ‚–’¤ƒèH I#ÝüãXµÑŽü&6ŠõcpÓi‰¨Í’Ì:JC K#úe ºS±‚¬xeùM¼üçAf±7<-¹ò‚L(ɧ†|©3ã©ÀÚøŠIh,¬vÁ#€ê µÆ«Ï0¶›eÅ©¢AZJˆ)>` )7Iì«XÚÄ9>¡HwÙõ*ËÓN-FT§¸È `òE’àp¨‘À“Ýù‘ù7UÍ;ހ߳·»›wΗCÅ©r‚€ô–ƒÏºF}R˜:–JI¶7?@‡+Ó]f£¾¶wŠÐ*Œ"¯Q—O[‰}ɰPÚ”ŠÛeà´H+.&B\”R qõâuª¡í;pAÜ•ö'ÁS®ÅKN:ôu¶ë« YºË’[„”…)E)H$\*ºi¦¦šhi¦ƒj“]V©—®isC 7Nn-A)$rGrm­]N{>Ÿ•¿võR{¼1!Õ#>û˜•`ÚJ”l&ÀÀ_Pz Š.Ú¬Ö"ªM>3K@Yi°ä–Ú\‡¶ÊV §œó'ÈØR¼èó&èÛj³&ŒjÍFhÇÁn¥Ki}ÆÑ|ÜC%\‹m8®ëJJF ¹*Ò[ZnÙ‹@”ÔÚ…^U’µ´ä˜”æäÿҔƂ§Û-•°³bTœR R\Jö©»šÅ ¯ §N¤L¤³  °ú$u?:§JÂSÕ+ÈUøÇ˜gå9;j³Œ*ÎÆhGÁ©Kj}¶×l[!\ˆmY"ËRBNh±9¦ú²©(”h5‰0Ýjõºˆ,X ³LÝ̚´h(Ž"ˈ³N¥ÆŒ´¢K eK³H@p¤/ •{ZéÈc«Ô¨j[z •Sv‰Ja1|4†¤¼Ò¤ºóŠKy)(PæRR ” ›_õÚLê%Mtê‹m"BÛŸ4úACˆKˆRV‚R R¤A>ºA¤ÎL¨Ôc6ÒãÓ‡%]ô%hBÖJ‚ PÉI¤d/kI¯wÕ f¡SLjFsVZ~–Ä–ØBnÛAô/´¦É I씤~H¶öתmÄÓ·N©Æ—Xc§ÆŸJel¶:†ÌfÀîÑN HA펂"6ڬɣ³Qš1ð[©A’Ú_q´_7ÉW"ÛN+ºÒ’‘‚îF ´>­[n³D¤Ð'úãQ•ØËc¥h¡ehZâ$Ü:ÀÏ3iJƒ˜©X¸RŠ®‚cäÕdÔ(Ч$VÐÒéénKk-§ÌBNiRHQ$¥{mÕ(‘bËЮ<¥¸Û.Ĩ1)m„¤–V  ØÛé Io9»f]‡‹P«È‘LЍŠéͰ‡_}â°RúÈ ¼½ÄÜzkZ]j:¥PcÓäΧ@¥¡¥™HçL…­÷À HS~T¤–Úd o Œ¢ÒgV%*<Ú%.8ãÏ¡–šEÀÉn8R„ ”¤u) w ¦vÕeÚËÔ‘¦ä0€ë«vKm°†Î8¸^RƒaµdŒW–*Í“’o½@Ÿ@Né¨TkòêsP®G"Jr$­ÇÊÁK¯²·@_”­E%Å ñË4ä•KÐwU*žåvÕ)ÒãÔ¥G™âShq§>·K¡AqßqHEõ@áW“ÓÎq <¶SÑœSJ[KRZu. lqRIJ‡æ }AÔœmµY“F5f£4cà·Rƒ%´¾ãh¾n!’®E¶œWu¥%#ÜŒlŽÔâ*=}¸ªtx“ŸBؤ°¢ìw%8V’xÁ’…’~¯®¦)»šÅ ¯ §N¤L¤³  °ú$u?:§JÂSÕ+ÈUøÇ˜gå7¶ÝQ¼Íyî…¸R\d. À}ÄÖId¯”ŒÒ¡pŸÉ'Ð_Pú·ÏÜy=!BL©,0–¢´üÑ+”¸·):êTTí™ZBSÍ{¨¶•*¡ i¦šši i¦šši i¦šši i¦šši i¦šši i¦šši i¦šši ìŸ÷”Ú¶gû7õéTv¼Õü ÿ¼¦Óý³?Ù¿¯J£ûµlëUígû*Ý_ÁåIZó§øÂGï½9ö³ý•n¯àò¿¤­ySüa#÷‡^ƒš=ÓîÂûüñáû~½¤~'Ûߺ{ýIÕ'W_hÿ‰ö÷îžÿPÕ+PyÛó‹o/l3䮋O^éNmê< t¹µz½FL*|GÙŒLX‰òu.©E8ØÇ\¹Êàâ7$m›Q—T…†ìj”J¤Ù)RL†˜ê‹JÌ…®í)AH! ±9¤ Ü_Fƒ]4¸² È¦AªÀ’¶Ýr,²êQÊØXCM- ¸8-–'3pHI[s|Ô¨²˜”Ý:‘-èµQˆ_‰dFyE…  ¥,6„ؤáˆ-ñ«Í®q Ñci×Û‰ÜHb0¥+” +šÊ›v*h‚°C¤¥²3X¹HPé?i× P£×%±ª|–ügŒÖO:JŠm!wZ’¡e¡ ©»ŒÂn5–~æj^ØMmÊDv›”ô¦iryY[ªFA9A)eï?̺qP ¹Š‰J’ ’Ÿ°7$º˜§­¸0Ý1dIÊTæ[Bx›)EVCÉÉ¥YHÈ„¦êý­n³©sª1#O“Nš‰è2fM[n>€¬VZê8Óe++6”@Ý&>õz->$tDf£J–øÀÈQq[ây•déùµ4‹‹,T¢ú¤©³œ…!q–ëvÉQ¤·!³pgR’¯_q67¸:œØ´ -ySšªWdÓ_a‡e¦)ýBžm¦yÓrâl–€ä’àô‘Z¨"£).³MƒMe´7"‘rMÔµ)k$’nµ(úBB@ÚÚuÓ·ªnÎM2 D¹襹eÜAmdq­å R}{dO¨ÊöÐrz§NžÔúe-e->…´‡G3m¥Å0·¨mAÄœ‚Ue“ï)=²÷#Tfjâ OB~)–…±-—OÊä¥ *`áÄ€ xÚÍ8RÛ;™ª%2|mÊEHOG‡%®HYh-·c‰äÚJ¯l½EíÛS”¯i²©ôÔTmm¼ôv"ô¹;Õæ´HJŠŠ_äL“{ÿsµ±N!Z“¶«1¨Â¬ìf„|ê$¶§ÛmvÁŲȆՒ,µ$$拚o«}KÚv¥³‘¶&üìd0Ó s¬”›6ÑOá;$_ŠæÙ«Íª†¦šhi¦¦šhi¦‚OjR¼wtÒh|ý?ˆÍf'6ñò,#,n/kÞ×Ôf¤ö¥WÀ·M&¹ÁÔxtÖeðç‡'ÂñÊÆ×µ¯cmFh,;Z…M«Å”¹u—a<Òà ‡ÈÓ(H;!¤ñ¶T¤¥8¥]!%{06œy4hή¦ëu9´é58‘ÄP¦ xüÙæîaIpôÏY!µ了Gj.æjŸ@UNܤU#ªQ”U%rP²¼@%—› c8ò9kdn‡»&F£70`®C^…zÜñã½ÉÈÒ@Xl…s=ÝHR‡!²…“ˆlÏÚqãQ¤ºŠ›®TáS£TåÇ1BXäpჹ•)ÁÔ³t–ÒžÊ8Œ±Ö6túm.’é_QS¨Í~©¬6¥½ÖÒÁ ªÞ®žqt ”Ÿ)óä”â™»&I£9P`¢CñY…&zœò#³ÇÆÒYlð³Ý(JºÕ–Îâß»ƒpí({v±!Ù¨‹)R„¹ä:û‹P)²³p¢ÀX $ZÆÖÉy8»&¾Ý^<*í.§CiÖ$I.K„´)MGiN»Æ•ãš‚l.% ”ƒq‘í¿·bHfDýÅ9šTÊq›ÔSä¼D…0[S\¡7mÕ_ŒR?)Xˆ:RE¦‰ÑÐÓ„!ÆœmÐJiÄ)·Uˆ ) RII ¸ Øé{‘ɪ\ÿ ¦"5/ŧÖä`Ú]S¥µ©KZTµ¬«%ç ,oUvE\î´½µ¯_D40§ÖÅ9|Œ-ÆÂËn¶‚¾7¬ÐRTl¦Ô=Ä }š¸×5Ù0ªFò¢9éÿÔ6ÈY*µ%N¤Û{(mÞ"\‰å=.[îÈòÔ㮺²¥¸µ•(žä’nIÔæÙÜÍQ)“à«nR*Bz8¤9-rBËAm¸O ÒU{eê/nÚ ´Mµ©·&Ô©Ib0·Þ 0ã‘KoH*:æ HB‚”¦Ó%ANSw#”úZâÇ¥S²Ã±‘QãX†]J’â,]Ò·’Ð¥€« QŒ‚Ã2…M‹#l­êË­@¬ÅnD‰.Cï u2ç‘*Qp$´¥*ìme®mê[0hÒ¨µy2|Q÷ZCu(­AR —oÌ´ñ)JZs% œ’m‹snf«tÈS·)ÑQ܈¹%a¢·-žW–+uJ½²ô·mhά9:t ¢FqˆL2Ãqid¶ØJ«%¬¤¤•¸µ  ËE¤G“_U.¡1ÖŠVZJ`0&»!ÜÂÛ)B‚$›ß0’H*8¥S”]‡"­Y«5OzuR™KZôŠT1õ)̰JZB±'ʼ”ã+vù!è…4z¥BkTJcíMaÆ gTøC-­@ÚÐâ\O”_2J¤ªáFùbîf£¹9¡·)S&-§ULZäð!ÖÒ¤¡Ä¨<¸9ظSó‡·dâ‹€n¡. †œ…`Hu¦q Q !’²¥z‚3ÇÞmßS6œy4hή¦ëu9´é58‘ÄP¦ xüÙæîaIpôÏY!µ了G‰µ‡*UeÔ¢F›P©?ιŽf—p¬­e B’1$¤¨ôqÖô=Ù25¸)ƒrŠô(ÓÖçîNF’Ãd+™îêB”9 ”,œCf±³¤Qv”jÍYº»MCNĘU hp *IX…^(Jì, Ì"«« ÍÙ2MÈ*ƒŠÌ)3Ð瑞>6” Ë`'…žéBTxÅÔn¬«Úši i¦šši i¦šši i¦šši i¦šši i¦šši i¦šši i¦šÁøÿyM§ûf³^•G÷kÍ_ÀÏûÊm?Û3ý›úôª?»VεP^Ö²­ÕüWô•¯1ªŒ$~ðÿç^œûYþÊ·Wðy_ÒV¼Æ©þ0‘ûïA͇i÷Gá}þxðý¶÷¤ffEÛQ¤T#Sš[Oå&J\Sh±¸m W{[²Or/asªçÓà¬UÛºF ô×8LF¤SÝ£ÎnšDy P€ãR‹ä¦öMÒÉnê°·žÝÅÈïdšE=ª0œÞé¤HP… µ(> ­tÝL†î››ùíØØž×‡ÓA9>‡LÓðïÞWÒÒø˜8Row› ºG¼'%w쓤Š1©Ñc£xÐßiüù$¶ÌÀÜ|EÆaL½ W[úƒÓA9àtÏèþXÐø89zΜ9en;pg•»ßmùWí¤z1ÙÒ£¯xÐØiŒ8ä¸ÌÂÜŒ…Î, }iOKŽúƒÓA9‡L“Ôso.'ÔÒ9Ù˜y’-gƒ ²O¸+vq©÷hÆs›¦‘@BÔ 8Ô¢ù)½“t²[º¬-ç·qr;ÚMÄšE=ª0œÞé¤HP… µ(> ­tÝL†î››ùíØØž×É>‡LÓðïÞWÒÒø˜8Row› ºG¼'%w쓨=4’(tƧEŽãC}§óä’Û3qñ…0rô%]ýl;éàtÏèþXÐø89zΜ9en;pg•»ßmùWí¨=4‘ètÇgJ޽ãCa¦0ã’ã3 r28°T1ô9¥=ý.;é‡L“Ôso.'ÔÒ9Ù˜y’-gƒ ²O¸+v=4©÷hÆs›¦‘@BÔ 8Ô¢ù)½“t²[º¬-ç·qr;Ù&‘OjŒ'7ºi$!FmJ‚«]7S!»¦æþ{v6'µáôÐNO¡Ó#tü;Ƈ7•ô´¾f›ÝÅfÂn‘ï É]û$é"‡LjtXèÞ47Ú>I-³07q˜SG/A‚UßÖþ ôÐ]6NÙ¢NöC¢ËÜt‰ð$JŽSH–„?“ÉIŽ’YJʱ!)ú`ê=˜ìéQ×¼hl4Ær\fanFBç– †>‡4§¿¥Ç}DD‘"$¦eÄ}ØòZ\iÖ–R¶Ö“p¤‘ÜEÁÅ œC¦Iê9·êiìÌ<ɳ‰Á…Y'ÜŠ»wHÖ8ÔŠ{´c9ÍÓH !jjQ|”ÞɺY-ÝVóÛ¸¹í¦‚bM"žÕNotÒ$H(BŒÚ”Vºn¦CwMÍüöìlOkäŸC¦Féøwo+éi| Ì)7»ŠÍ„Ý#Þ’»öIÔš É:cS¢ÇFñ¡¾ÓùòIm™¸ø‹ŒÂ˜ 9z ®þ¶ôð:gŠt,h|½g β·¸3ÊÝïŽ6ü«öÔš Èô:c³¥G^ñ¡°ÓqÉq™…¹ œX*úÒžþ—ôC¦Iê9·êiìÌ<ɳ‰Á…Y'ÜŠ»wHÔš ˆÔŠ{´c9ÍÓH !jjQ|”ÞɺY-ÝVóÛ¸¹ì“H§µF›Ý4‰ £¶¥ÁU®›©ÝÓs=»Úðúh''Ð鑺~ãC›ÊúZ_3 Mîâ³a7H÷„ä®ý’t‘C¦5:,toí?Ÿ$–Ù˜ˆ¸Ì)€£— Á*ïëaßPzh'<™âË/YÃ3‡,­Çn ò·{ã¿*ý´C¦;:Tuï 1‡—˜[‘¹À%‚¡¡Í)ïéqßPzh' Pé’zŽmãC…ÄúšG;32E¬âpaVI÷b®ÝÒ5Ž5"žíÎstÒ#ÈZ„”_%7²n–KwU…¼öî.G{Cé ˜“H§µF›Ý4‰ £¶¥ÁU®›©ÝÓs=»Úù'Ð鑺~ãC›ÊúZ_3 Mîâ³a7H÷„ä®ý’u¦‚rE˜Ôè±Ñ¼ho´þ|’[f`n>"ã0¦Ž^ƒ«¿­‡}<™âË/YÃ3‡,­Çn ò·{ã¿*ýµ¦‚r=˜ìéQ×¼hl4Ær\fanFBç– †>‡4§¿¥Ç} Pé’zŽmãC…ÄúšG;32E¬âpaVI÷b®ÝÒ5¦‚b5"žíÎstÒ#ÈZ„”_%7²n–KwU…¼öî.G{KÕ6õ"?³J]qšÝ1ÚƒÓd¡Æ™<ŽÜR6ÙqjQ¸,b¥Ú¡¬ª‘!Q[ˆ§ÝTvÖ§ÑYÁ+PHR€ô„$ïÄ~a —ŸC¦Féøwo+éi| Ì)7»ŠÍ„Ý#Þ’»öIÒE˜Ôè±Ñ¼ho´þ|’[f`n>"ã0¦Ž^ƒ«¿­‡}Aé œð:gŠt,h|½g β·¸3ÊÝïŽ6ü«öÒ=˜ìéQ×¼hl4Ær\fanFBç– †>‡4§¿¥Ç}Aé œC¦Iê9·êiìÌ<ɳ‰Á…Y'ÜŠ»wHÖ8ÔŠ{´c9ÍÓH !jjQ|”ÞɺY-ÝVóÛ¸¹í¦‚bM"žÕNotÒ$H(BŒÚ”Vºn¦CwMÍüöìlOkäŸC¦Féøwo+éi| Ì)7»ŠÍ„Ý#Þ’»öIÔš É:cS¢ÇFñ¡¾ÓùòIm™¸ø‹ŒÂ˜ 9z ®þ¶ôð:gŠt,h|½g β·¸3ÊÝïŽ6ü«öÔš Èô:c³¥G^ñ¡°ÓqÉq™…¹ œX*úÒžþ—ôC¦Iê9·êiìÌ<ɳ‰Á…Y'ÜŠ»wHÔš ˆÔŠ{´c9ÍÓH !jjQ|”ÞɺY-ÝVóÛ¸¹ì“H§µF›Ý4‰ £¶¥ÁU®›©ÝÓs=»Úðúh''Ð鑺~ãC›ÊúZ_3 Mîâ³a7H÷„ä®ý’t‘C¦5:,toí?Ÿ$–Ù˜ˆ¸Ì)€£— Á*ïëaßPzh'<™âË/YÃ3‡,­Çn ò·{ã¿*ý´C¦;:Tuï 1‡—˜[‘¹À%‚¡¡Í)ïéqßPzh' Pé’zŽmãC…ÄúšG;32E¬âpaVI÷b®ÝÒ5Ž5"žíÎstÒ#ÈZ„”_%7²n–KwU…¼öî.G{Cé ˜“H§µF›Ý4‰ £¶¥ÁU®›©ÝÓs=»Úù'Ð鑺~ãC›ÊúZ_3 Mîâ³a7H÷„ä®ý’u¦ƒ¹~ ôØTïÂSfô›‚™Xä3rèÛž+CzÙs4ß­Í­C{v¿£ýÚóWð3þò›OöÌÿfþ½*îÕ³­TµŸì«u•ý%kÌjŸã ¼?ù×§>Ö²­ÕüWô•¯1ªŒ$~ðÿç^ƒš=ÓîÂûüñáû~½¤~'Ûߺ{ýIÕ'Woi‰ö÷îžÿRuIÔvüâÛËÛ ù+¢ÓãWºWM.s4 » ÃTšû’¢­©¢ˆ 1R‡ÃÈ-h+TrQ•Õˆ68$¨lЫ2¦Tkò©9ªïªµ=ùŠŠ¶"¨¡]TFŠš.8¢© ÃÂ0lqŒ¬¾s¦¹Äƒ£mÊF׫Ņ!IÛÑè2£HDªšØ_Š$>XZBÝ•%,¥Y2Ò,»'[Û†‹µ‘â*…LÛL*.I!½Ä—“&QâÅ@ç$´pI.䄿+B+ÓAÑ·´m¯3yǘZÛÔ}¹*®lå"RÝì%¹ròšÍÀÁB-óe ›®Á³‰“™CÙHß{Q”F¤t¥-º¢Te¸‰Ž0ºòDçÖ‡S†êq!E( BˆPW&ÓA1»‘šÊ¢F¦Á€ˆèKa1g‰ÏxZÞJÔ…¸AE¼SqÙ î5|öSSyg5êC•øÔæ*UHH`®¨ÛÅu!Å&JV”¥ñ‘Ĥž0²;¢éåzh;ƒtí£º÷¾ç©n:½2CSw ˆÈ˜õU®Fb¥@!ÆÂä³d„+ʼd$„HEÑofíI[J*$;µ`ÖNR][™‚W E*9HR.K0“ØÚKÝ -qÝ4¹HÙHöjÜêk;QLXîD–Rù¢€ò…J+Si*t˜È>T+5$)kç:i i¦šši i¦šši ³{(þÔvŸñ¸×F«:Ú¤À—UªD¥Àkš\ÇÐà 䛋PJEÉ\‘Ü›kWAÔ=—×EÙõbLÚÓ¨„Í^HjcW¨!(u/2¶TêUÂæl¥jrà"ÂŽ­‘²¤{5r[Î4íXEãäÉe§Ù’¾$ 9)¼›) ÒsXJò°oœé ¼n¸tX[…*&†gM„9ök<ï2ðuÂ’#¥â¤)M%°¤ ”¥¥öì¤Ò;h?Q~‘N¥7ªÍ&¶‰Ýå¾¥–ÑÌòÀâ)_k§%Ê$kœé èR©»kÅé ¨Å¡Óå¹ÕòÂ§Õ ð—‹@Ää{™Ì9͵üêqBB¾nù›PL7÷¶ÄvqÛÊ‹œÛÛ©µxhnrÔTÓÍÈJCn ´’¥žÞrÚ”8žš ¦Ø£CŒåR,Øûz§Xal#M¬¶Ü52¤¬ºà}·ÛBœIáHHpöZü¤¤”VwŽ+õ#Pã‰NñGjH†Q™²è$8ì Ê×÷ëGMB§»³Qµ!×$P¨n»Ñ‚äÙ!ù“¹Æî $´VyT‡BJš#zf×ÚŠöjä–%Ò¬3NfckEU‚ãîÙq¥e&ä„-ÃÆ#6 ¤…¸EÝåúh:…QˆlNØ7lÊEZ™+tê¢YnRÚo«’mhî—,†T‚’Ÿ(²Sßèë3míf!í´×fÐÒúfÍb§ÑTÒú–xPäDº9‚ T¼’¥´´ $€·XQO4ÓAjö‰¾Â(Ii ¹+y ÈiÖ’ækN91„’ê•‘Q°IH]4Ð4ÓMM4Ð4ÓMM4Ð5f¨ÿeÔ/ãu/èAÕg[N@–Ý-Š¢Ú´IºÃNd<Î6–Ô±kÜX:ßr,ríèlÛ%úl]çC“ZKJ¦5Q޹ֹY$¯$Øä1½Åý-«s³*SfƤoíÁº‹8Aæ¬uF‡— †Ü(HS­¶œV²”€µ)( <çMgÛ›OeÈj55퇩l32K{„6¸òÄâÛËBVøº„b\¾*iKm%$ÙpuZÚaOÄòhKCð*!ÅÖ Œ&:˜Q™©·NImà”¥¼…$’’箹¦š¨š2#JöI¢ÕèlѦ|Å9•n©mü&°ë$f÷›s²Ï¡Í7ÊãXö†×Ú•-‰”ét†*rbÉu§ª°Û­¼Ž@Ëjä’ßR€AaÛ¥yr'/šåúh/»ÿÁª–*tzVÕ¥Åébb¸“2Ýx0„<ÉŽ]YH ä<‹m9ÁäQXäÞÜqè´ê–Ñ«Ui´94†˜ U)tÚïV&L…(6”ÉS‰ù²•‚š»÷Qši éj…²cî:VÝ©±CÌ?%ùÕdÇž`“ËÓFJ–ö hÑ.fÙÅÄ¥KiHqf½í:}„P’Òr*VòÓ­%ÌÖ<œr$b1 $-Õ+"£`’*ºh.žÏÙÛu dÈ5hôˆòÙ•Y¨O}äÚV†Ò„<€ë–(RP‘š“Ìrãµ—bÒvär\êŒ:EʉŽÛ «$¹8J<éæ’Ò¯çQä)¤›4€œ\äÚh/±©µ{)¬Óž«RE]‰ŒÄñȤ٦¥6úÐß-É$´"î Jr:Ù™HÚóhÕúœíè¡êDI4¸þ&´¾Ô”p ˆBt›\¾]º×d–²s4Úu#eB 1#l8Ó¤JKl<Ü–JߎP³›ÍõN¸8 ß…€.°¤R”ó4Ð4ÓMM4Ð4ÓM`ü ÿ¼¦Óý³?Ù¿¯J£ûµæ¯àgýå6Ÿí™þÍýzUÝ«gZ¨/k?ÙVêþ+úJטÕ?Æ?xuéϵŸì«u•ý%kÌjŸã ¼:ôÑèvŸt~÷ßçÛozNz›mMŽˆËu¶ŸÅ2c7!³rvÜJ’¯_x66#¸®|©©ø§‰t´>~ |9e~.,2¿åã•»^ݵ5í#ñ>ÞýÓßêN©:ƒÎßœ[y{aŸ%tZ|j÷Jr>驱:TÔE¡—eaÈ—(pÖÚq6¦Š[ýx‘întºjpºŽ´5õ©õóÐá½e*× Í£‚{vBl‘îR~̶s{ÒeBdÔÑ.3 ¼Ë "Jž y +é:ØN%Ô(’mˆY$cßEª6K¥ØU—d"Œ€üG©Yñ;k/±ä9“ͬ¶œB—’† !J²H:ÊšŸŠx—KCçààÇÀáðã–WââÃ+þ^9[µíÛHû¦¦ÄéSQ†]•‡"\¡Ã[iÄX`Úš)oõàG¹¹Ö]í¶W¶UHCºâêäÌRVÒS.¸Ù RV¤¸ÙâÉe¥IPú˱hZò§5T®É¦¾Ã>ËLSú…<ÛL<ó¦åÄÙ-É%Áè 5`nšœ.£†- }Cê}|ô8oYJµÂshàžÝ›${€Ö8ÛŠ¡Œi-Ǥ*9BÐVå"*ß²¯žSeË÷69\v±Ù^ÚB¯TéÓÚŸL¥¬¥§Ð¶ãÈæm´¸¦àu ¨8“J¬¢}å'¶^äjŒÍ\AièOÅ2ж%²éâ\”¡E@Œ8/Y§ CZNâ¨H£ K‘é Ž„·HЇì›[ç’Ørý…ÎW=îMβOÝ59½?4Z:wÒú8(pÙº“{`ÐÍ=û¡WI÷ƒ¬rvÕf5UŒÐ‚R–Ôûm®Ø8¶B¹Ú²E–¤„œÑbsMáô’7MMùÑf®- ;>4·C††Õ±Í´´çêÌOqc§ÊšŸŠx—KCçààÇÀáðã–WââÃ+þ^9[µíÛPzh'#îš›¥MDZvV‰r‡ m§aƒjh¥¿×€æçH¦§ ¨á‹C_PúŸ_=ÖR­pœÚ8'·d&Éà5¦‚b6â¨G£Kqé ŽP´¹HŠ·ì«ßç”ÙrýÍŽW¬E†’wBER\HLp„ -ºDT?dÚß<–×ì.r¹ïrnu¦‚r~é©Íéù¢ÐÑÓ¾—ÑÁC†ÍÔ›Ø+†iïÝ ºO¼$nš›ó¢Í\Zv.|in‡ «!c›ih%ÏÕ˜8žâÇPzh-ûS{H¦ïÚN㟘ZŠû!ôF£ÄEÙK¡j(@BR—{8,±Ød£#îš›¥MDZvV‰r‡ m§aƒjh¥¿×€æçZÛR•㻦“CçéüFk19°Ï‘acq{^ö¸¾£47MNQƾ¡õ>¾z7¬¥Zá9´pOnÈM’=ÀkmÅPF4–ãÒ¡h+r‘oÙW¿Ï)²åû›®;X‹ ImM§³ž©57bH«ÔUL¦!¸¡Ô.BCWæQZKmÝö¼É§åì6œy4hή¦ëu9´é58‘ÄP¦ xüÙæîaIpôÏY!µ了G“¸ª(Â’äzBc„!mÒ"¡û&Öùä¶¿as•Ï{“s¬“÷MNoO͆Žô¾Ž 6n¤ÞÁX43O~èUÒ}àê6D˜ƒj×µ+>4·%µ¸œMŽm¥EMþ¬ÀÈw·Kö•SÂ)•n¢¡¶Å p‘…–å:§’ÚÒ¥—É—.¥% ¶'rr7MMùÑf®- ;>4·C††Õ±Í´´çêÌOqc§ÊšŸŠx—KCçààÇÀáðã–WââÃ+þ^9[µíÛYjÔb-2u¤ì¸U NÃBç²Ü5¡æÃEY|êЖìûvYXü«€'‚Ü©•QRÓD¤0^˜ìD¢Rˆæm‘ÄÒ‡.·Qß01¹Øû¦¦ÄéSQ†]•‡"\¡Ã[iÄX`Úš)oõàG¹¹Òé©Âê8bÐ×Ô>§×ÏC†õ”«\'6Ž íÙ ²G¸ I½´`ATùujÄ–©Qú.'¢ÁKϹհ© dÒœBSóhVvZ±U€Ì„EcoN¥Ô+pŸz —F”¨²m) +Xp·v²àºOÑI°î«h·B=Ò[HTr… ­ÊDU¿e^ÿ<¦Ë—îlr¸íb,4“¸ª(Â’äzBc„!mÒ"¡û&Öùä¶¿as•Ï{“s©ÊÑVÚNV£=9Õ¦,‰eL@.Ca ©M¿ +æž)l”£\-žã?*fÃÎÊsq²ôçÄVe¼÷@D!ŶŽ6¤…G’§R• ¡8”:.pó<ýÓS›ÓóE¡£§}/£‚‡ ›©7°V ÓߺtŸx:HÝ57çEš¸´0ì\øÒÝVBÇ6ÒÐKŸ«0q=ÅŽ¦7–Ñ·è®—§=›ˆ·ß€Y%kCŠK‘ÉBC$4£‘ÙM›yŽ4½çÊšŸŠx—KCçààÇÀáðã–WââÃ+þ^9[µíÛHû¦¦ÄéSQ†]•‡"\¡Ã[iÄX`Úš)oõàG¹¹Ôš Ȧ§ ¨á‹C_PúŸ_=ÖR­pœÚ8'·d&Éà5Ž6â¨G£Kqé ŽP´¹HŠ·ì«ßç”ÙrýÍŽW¬E†¡ôÐLIÜU aIr=!1€¶éPý“k|ò[_°¹Êç½É¹ÖIû¦§7§æ‹CGNú_G7Ro`¬§¿t*é>ðu¦‚rFé©¿:,ÕÅ¡‡bçÆ–èpÐÚ²9¶–‚\ýYƒ‰î,tùSSñOéh|üø>rÊü\XeËÇ+v½»jMä}ÓSbt©¨‹C.ÊÑ.Pá­´â,0mM·úð#ÜÜétÔáu1hkêSëç¡ÃzÊU®›Göì„Ù#Ü ôÐLFÜUôcIn=!QÊ‚·)Vý•{üò›._¹±Ê㵈°Ô•Kw»3Ùõ?l˜ÐR¸òž[Ž&—´Q7‹‰FaË´¼ÕÙJ!JW ªêNM+‡k@®såÕÍ“‡ a†–Wï~{Zݱ÷ß°lÏÝ59½?4Z:wÒú8(pÙº“{`ÐÍ=û¡WI÷ƒ¤ÓS~tY«‹CÅÏ-Ðᡵd,sm-¹ú³ÜXëGoÒäVëôê,E´‰å5¥:H@[‹ DmsÞÀêÂÎÑ9P%Òk]¥Hëy^•,¾ßHÂd?‹IqiWÍ­8]iÉW2!ò¦§âž%ÒÐùø81ð8|8啸¸°Êÿ—ŽVí{vÒ>驱:TÔE¡—eaÈ—(pÖÚq6¦Š[ýx‘înu–¥¶‚jÆ©3Ú‘«R¡½=mB! qÖÔÍÂÚl®ß8n1÷œF-½G.—6¯W¨É…Oˆû1‰‹2S®¥Õ#ȧâË—9\@ä€@Ý58]G Zú‡ÔúùèpÞ²•k„æÑÁ=»!6H÷¬q·B=Ò[HTr… ­ÊDU¿e^ÿ<¦Ë—îlr¸íb,5¼öÕé\ÝÐæÏ¡¶þ“l³›21”ˆîYeI)²œISq{ãïƒè^ð¿Î7?=K|Ùc•ø²Ï~^8ßµïÛA½'qT$Q…%Èô„ÇBÛ¤ECöM­óÉl9~Âç+ž÷&çY'ޟš- ;é}8lÝI½‚°hfžýЫ¤ûÁÖõKjǰi›™çêê]I S8RÁ†•‡ÝoRK½œÅ¥/ƒØÚ2ïM—òv–ÔÞ¶K·|1”ˆ| ˺Ty¡¹šº–=ܲ;8Ñ·Ÿ°FHÝ57çEš¸´0ì\øÒÝVBÇ6ÒÐKŸ«0q=ÅŽŸ*j~)â]-Ÿƒƒ‡ÃŽY_‹‹ ¯ùxån×·m)»qé•tiRã7Âòd=æå$4Ó*}x)µ-X!V•)îDœ£fïÛt¦«E7páÒKTó#'œ`r3ɈùÖÕèáò¯_&‚2>驱:TÔE¡—eaÈ—(pÖÚq6¦Š[ýx‘întºjpºŽ´5õ©õóÐá½e*× Í£‚{vBl‘î[[k|¤ëMΛÓR©ýlµå—œ3š>i8ÙK˲–رÊâpRäQ+õ,µ´¹%;Õ4IA[k)%$€mqÚàh6£n*„z1¤·¨å A[”ˆ«~ʽþyM—/ÜØåqÚÄXi'qT$Q…%Èô„ÇBÛ¤ECöM­óÉl9~Âç+ž÷&çPúh''ޟš- ;é}8lÝI½‚°hfžýЫ¤ûÁÒFé©¿:,ÕÅ¡‡bçÆ–èpÐÚ²9¶–‚\ýYƒ‰î,u¦‚såMOÅJè´øÕfn—¶º¦®=. ÕËCh.>ä„- ‡RèÁLºÙIͶÕ{Ü`,EÍöiÛōЭm-½ V³! [Cm)Ô:m-<€–Ò¶ÒGl»ZävÖ=´Í:.Öªî ´¨ÕWcM‰ ¸òœu,€ò$-Kù¥¡Yæµ”«‚lDåa·º*•5ü›uú£©ôÙ*@âs!fV·ÜmJÃ4$ñ%×r¤PÎ$»gÚUJƒ·AG¦=°û¥oÌm2RîaeÖÚ} ­X¯²œ€J,|©³|îÚL¶D-µ ž¥Ã6t˜êjL„°†“O;„•0Ò]‚nFEq"ÁwÙÅJr¡4'Å«Äi‚×™iÖd•6S–”q˹îE€TâÁϨU¡4Ì×*3¢¾ò²_ChŒ´(¤Ì±å ¸µî{Þ«{A¬Lðn–ÛðxÝ~G ›¶K®à‘aäF)U“V)Ç-oÚ5j­S:Dv¯ ´[vlÉ(y§ÑÆëjç}¤]$¶P¯5ïp’*C2™yÈíICkJ”Ë¥AnR¬HU¡±ó®ŸíeRS¼Y¥S¢Æ£¡UJ„cÒ>©Lˆ±Â‡µ8 —ñ.f•¸ÚPjp2’VBºw k‘é‘ÑD¦RÚ¦°¶L5>nÚœS–W+‹½–·"ÇÎA¸ {NºvõMÙɦA¨—"½·,»€C¨-¬Ž5 Ü¡JO¯l‰õ‹sÛ oÓ…UÚ¶àœ¨­R©Ázxò f&2ù$îp‡c‘RJKf7ll†jÎU$K©»L§B[ mÉ(ŽÃïÒµ²¬}¦ÀSm•vqGºq ¨fÙÜÍQ)“à«nR*Bz8¤9-rBËAm¸O ÒU{eê/nÚœ¥{M•O F¢£km磱¥ÉÞ¯5 ¢BTTRø"d›Øû­Šq¦U¢x}R\ª4¾™õ³ÏÌÙwšùI6¸>ðFµtú—´*íKg#lMùØÈa¦çY)6m¢ž1 vH¿Ͳ7W›U 4Ð4ÓMM4Ð4ÓMM4ÐIíJ¯nšMsƒ¨ðé¬ËáÏN5…㕯k^ÆÚŒÔç³è*»÷oRçµÍeR3·‘Nm­Ô¥Bà‚. î õ °íÍÙ2‰†ZƒRáÊ3iÏ>΂ ¨ÁiJÍ4làZ~ly{«$=Ù25¸)ƒrŠô(ÓÖçîNF’Ãd+™îêB”9 ”,œkÚh6¤IeØ1c¢Ÿ‡XÏ’Kjp¹##q˜RÊF>ƒ§·­Ï}X&ﺻқ4|ÿn©&SZ—.ceE-.)H8¡(AÌÝ$€¬7F‡´£0ý¨•WÓ±Ö™*IA©×î®4¶»ž6ÒÚWŽ+Rñ /ÉûHÛ”ú YTúJiÑI¨ˆ3j,®P–³–C©s€YmÒIV!° Õôœ õSp¦oAJd:l'Ôúiì)òËŽ/áR–âœó%¶Ò@X'ËbI8éUÓN¨Nyªd!OBš‘Op»À¦‹‰q-ä+Cd¼¼€AP964•ùjk\Ì1 dÂÑQJ],Fuä¡DqR›XlMˆ6"še-ÉÔz«T):¹Òc½)ô@…Œ·c—B‹Å¼Z$9’ZBrºZPD á%ÉMB“L¨Â•Óÿп̖ZéÛ-1Š›q.yR2Y¸7VJ²„dÚÕª²êQ#M¨TŸç\Ç3K8VV²„¡IG˜’RTú8êÕA¥íYÎWw\tÈr£Ç‰ªe·’ê®®œ-Ðee-åÛ$äêð<µ ÁDýF ¸M@‘S­;¥•¡…¥dÒ¢¥E*Uíê}tQ«¦%ÁƒLƒSˆ[OT[.™.´»äß™eIB¢›¤’°¤šéUS Ó ÓP≎Æ.—&cb9 ‹U†C"”$«RpF;ÌÐ(¬P¢Ì¬×d—>Ó ´Õ?™•„)ÖЇäJ¥¸Ê“Ù )$žä'{qìÚl³\¦ng½¨¶ƒÁŒi€£š¾x*B.„äŒ{òetƒ­WLø©ƒ™,:¸°‹¥ºũխj *Å7V ¬ªV­ÓµéÔŠZ”õRêЇËLèÉBëCêqØâ—ZiJ Y)IB’*ºši i¦šši i¦šši jNMW›k@¡ðcÒM“/›;ç̆Ž6ín Þýò÷[¼f§&ÀˆÞ¤ÕեȪNa×2>fÛj"-{ \îÎ]ý‚6“>]*©©ÞpßCì9ˆV!AI6 ƒbb-©Ã¼$·"§Òi”èQzú9”˽Ca§òSŽ)Ï;iJ+éÅWQ¬é œ™¸S2© Lš%1p`°XLÉñ •-ddå?8â×rá76ú 'Xè5ÓK‹* Šd¬ +m×"Ë.¥­…„8Òл€ã‚Ùbs7„‘“eÆfEQÕI¦ÆšÃ,TÇÜj,tä]x·e”€l…)j@]«ÙÖÛÛ;¯Ú_4ÍTc2ŠkÕã?!.®Ä£‘yqŒTJ§ó6ØRŠ‹À éÛÕæUZv£A¤V%VÖµO‘,ÈBÜ u€Ë­¥#‘´«²Aõ·mWú–|/£ðøÜüü½fNsc¸ížß½ñÊÿ•nÚÅ-…Å”ôgÒ–ÒÔ…Kˆ$T’R¡ùˆ$Pu}£Ðholèüñ#*¡.—2zsqá5e’ý–Æ'§ 8wçN/b Ú¸VW¹ð'iŒR©‘]Â#K˜ÃkKÒB´¡IËŒy›l•% ZŠ.¥¥å—qîÉ•¸¯²ì1W2P›Qy€æsd°^kRR~uÓfÂó‡ËÙ8Øk;:GHɨD¥Ãž¬xÍAx±u¿‘éËŸÈùÑ“9gm‹ÚFܧÐbʧÒSHžŠMDA›Qer„°õœ²K˜´ËnJ± €V¯¤àV£WL=ÂjôúdHRÒ¡6]S im–nëZœ²Ð¥‚BòJl-%OÞ¯BÜ´êÓtBÅ)M6ÌŽ….—‚’C¡ÅE-^u¨yȵ‚@ˆÛ4¯«¦ŸéÚKÉyÀŒÔ–™in¹Šn2V «HØ‘ÜX`í¥Pb£&ïœ#À§"sÅú> õ!•µd¼¡Ée´¤X”«;)MØñëУΕ-­«CÉÜ vÜên"’-’·H]Í”Rï"I¶7"\‰å=.[îÈòÔ㮺²¥¸µ•(žä’nIÕ¾Ÿ³)ÎnTÙÛ‰¨Œ¢,yq–¤°Û4ûhu‡Þi¤…§$‡T £d…¤)iªÕ¢ô5Ip±’ŽõµŒ–8^¨‹-»œÛºnln.tºi¦¦šhi¦¦šhi¦ƒ°~ÞSiþÙŸìß×¥QýÚóWð3þò›OöÌÿfþ½*îÕ³­TµŸì«u•ý%kÌjŸã ¼:ôçÚÏöUº¿ƒÊþ’µæ5Oñ„Þzhô;Oº?{ïóLJíúö‘øŸo~éïõ'T_÷¢à7m.©L˜§ù!,¸®âÖZ°;ØýqqÚ÷Ϋhø§'שÒD­¢©ÑW‡\n"sêZr°ÒÜrãÉ‚Ä`cÜÝ*¸ì1õÐ$oÝ&tYò7MqépóéŸr êœc1eࢫ§!ØÛÔzëZà¯Óê’*°+•8• 9sÊbZÐó¹()Y,ªê›žä_[=VÑñNO®x<0×7._K“¦¶8öÇ ß¾^í#ÊÚ))r(uÇ"+™¦ë !Æì<ù¬Æ!w=Å’›Ç/]D¹%Êz\·Ý‘!å©Ç]ueKqj7*Q=É$Ü“¬Zœ+h£¨ëèuÇò}Jc‚°ÓX5Ûªñ•’‡{¨bÕÇFÙMµ&‘Wr§‚ÀÝQ´0o‰â,Xv¸äïcÝ7ìújbL²ª0j5"®ÝO!Ê£k`¬[#Ä ±ïaÉÚ㺭ß$ù[E}?AC®1‹éSüõ†Í®ù%6ŒœT{YG >©ÐAé©É¶Š§E\zq¸‰Ï©iÊÃKqË& €Estªã°Ç×N«hø§'×¥1ÁXi¬íŠUxÊÉC½Ô1Ꚙ#l¦ŒZ“H«¹SÁ`Hn¨Ú 7Äñ ¬;\rw±î›öI‘¶UF F¤UÛ©à€d9Tml‹dxƒV=ì9;\wU»„>šœŸ+h¯§è(uÆ1}*ž°Ó¹µß$¦Ñ“Šk(äÕ:H•´U:*ãÐëÄN}KNV[Ž\y0XŒ,{›¥W†>ºÚLùtª¤J¤xeÃ}°æ!X8…$Ø‚ ˆˆ¶µuxö}PÚ1½¨íé¾S…Ojlee&¬Ñát>“Ì·8 A>¨²Obsƒ+h¦t¥È¡×ˆ¬:f›¬4‡°óæ³…Ü÷Jl;½tzjr­¢Ž£¯¡×Éõ)Ž ÃM`×lR«ÆVJ?Tkie4bÔšE]Êž CuFÐÀY¾'ˆ°UaÚ㓽tß°$îÍ*Œ(²w]ê`BÜšâ˜E±Nãaˆ°·kÍ­Zb¯R‹%F«:dxHãˆÓòâE€Å’,”‹ zÍ­©26ʨÁ¨ÔŠ»u< ‡*­‚±l`*ǽ‡'kŽê·|“åmôý¸Æ/¥OóÖw6»ä”Ú2qQíe€ú§A£&±W•Y©5YÏTÂÐà˜ä…©ð´[fNW‹ö°üÚÚgtîfk/V™ÜUvêrzb&¸q*–DySØŸÉ›Y$JÚ*qèuÆâ'>¥§+ -Ç.<˜,F=ÍÒ«ŽÃ]:­£âœž\ðþ x9\©Õ82áë%­î<­–9kØ^Þ¶ŒÔäù[E}?AC®1‹éSüõ†Í®ù%6ŒœT{YG >©ÒD­¢©ÑW‡\n"sêZr°ÒÜrãÉ‚Ä`cÜÝ*¸ì1õÐAé©Î«hø§'×¥1ÁXi¬íŠUxÊÉC½Ô1êc#l¦ŒZ“H«¹SÁ`Hn¨Ú 7Äñ ¬;\rw±î›ö}51&FÙU5‘Wn§‚åQµ°V-‘â X÷°äíqÝVï’|­¢¾Ÿ ¡×Åô©þzÃNæ×|’›FN*=¬£Tè ôÔä‰[ES¢®=¸ÜDçÔ´åa¥¸åÇ“ˆÀ"ǹºUqØcë§U´|S“ÀëžÁŒ5ÍË—Òäé­Ž=±Â÷ï—»A¦§#ÊÚ))r(uÇ"+™¦ë !Æì<ù¬Æ!w=Å’›Ç/] JÚ(ê:úqüŸR˜à¬4Ö vÅ*¼ed¡ÞêƒõF‚[NO–å-ŠZݼHïºûMâ<®8–Ò³{\Ü4ßbl1íêo½FÙMµ&‘Wr§‚ÀÝQ´0o‰â,Xv¸äïcÝ7í9VŸ¶\öSHƒäÔÛ¨ËQʤÚÂZ†â› …q¯œ†% ó/Ð/MNO•´WÓô:㾕?ÏXiÜÚï’ShÉÅGµ”rê$JÚ*qèuÆâ'>¥§+ -Ç.<˜,F=ÍÒ«ŽÃ]±W¢JTº-Vu6BÐ[S±$-•” I) Úà~¡¬¿(+þ;ãÞ9SñÓúµõGû—Ëèù}};zkgªÚ>)ÉàuÏàǃÆæåËértÖÇØá{÷Ëݤy[E3¥.E¸äEaÓ4Ýa¤8݇Ÿ5˜Ä.縲SaØåë Ñ]b®¶ê ª«9H©,9=&BÈ”°¢ §Eüä(“u_¹¾ŒÖ*ìÑž¢³UœÝ2BÃCD…†XÇ̤‰>T÷#òGæÖô [EG_C®?“êS†šÁ®Ø¥WŒ¬”;ÝC~¨Ö8Ò6ÊhÅ©4Š»•<†ê¡€³|O`ªÃµÇ'{é¿`Õz±WzŒÍê¬ç)‘Ö\fä,°ÚÎ^d œAó+¸”>•Å^¥$JVtÈð‘ǧä-Ä0‹Š$$Y)ô›[Rdm•QƒQ©vêx U[bÙ ÀU{N×Õnù'ÊÚ+éú qŒ_JŸç¬4îmwÉ)´dâ£ÚÊ9õN‚"$‰%3.#îÇÊÒãN´²•¶´›…$Žà‚.ÕªítSÞ©Kv|š…Jt!5S¤™1Z ÎÍ-.§8J»Üvµ¬¥FH•´U:*ãÐëÄN}KNV[Ž\y0XŒ,{›¥W†>ºu[GÅ9<¹áüðxÃ\ܹ}.NšØãÛ/~ù{´éÛ§sSeK—NÜUxr&¯’[¬Mqµ¾»“’È ¨ÝJ77õ?ŸPúœ+h¦t¥È¡×ˆ¬:f›¬4‡°óæ³…Ü÷Jl;½t+h£¨ëèuÇò}Jc‚°ÓX5Ûªñ•’‡{¨bÕ=51FÙMµ&‘Wr§‚ÀÝQ´0o‰â,Xv¸äïcÝ7ì“#lªŒH«·SÁÈr¨ÚØ+Èñ¬{Ørv¸î«w}59>VÑ_OÐPëŒbúTÿ=a§sk¾IM£'ÖQȪt‘+hªtUǡ׈œú–œ¬4·¸ò`±X÷7J®; }tzjsªÚ>)ÉàuÏàǃÆæåËértÖÇØá{÷Ëݤy[E3¥.E¸äEaÓ4Ýa¤8݇Ÿ5˜Ä.縲SaØåë ƒÓS%mu}¸þO©LpVk»b•^2²Pïu Aú£XãHÛ)£¤Ò*îTðXª6†ÍñÖ²­ÕüWô•¯1ªŒ$~ðëÐsG¡Ú}ÑøCßžÞýÓßêN©:ƒÎßœ[y{aŸ%tZ|j÷Jcmmº¦âqöé} – Ž&EAˆÇ•)JÕ§ ”¡ED_.m¨Él.,§£8¦”¶–¤(´ê\A Øâ¤’•ÌA úƒ«³éô eF\ºäº›9“”ćòçŽë*RŠF8ò/•ˆòúêÕ¶wÅ‘F Qܬîà@«È~k>Õ¤AsÓ ¿~5–q¯ KÊ'2Úrç_¦¯±7d8[atš^ãÜ4´5TCˆ-ª=L8§lì„󀇇Ùò¸RAJ€N_– <4Tk‘lÄ6†ÓI1én²¦ŠçF<¢ï¯‰Ã`–Íä9wr°çºË%…Çp6âšQ(BÁmÔ¬YI ÒH½ˆ¸õà€Aë>ÐwæÑÜiT˜uË¥âÎfR©ír1óï©E 2 ;Ö¸†È¸#ìÆ&,DÈyNº—T"œlc‹.\åpq’7¦l ɾŠÁ~DšŒšle"s)C²#¬%Ä]jIA¹N!a%Y¦Àä5A®š\YPdS Õ`I[n¹Yu(ål,!À¦–…ÜË™¸$$‰z?´*Ô  ª¿™SœÝQuV^™..)å%))OŸÜq¼æ×8hÕv…R›· ×_“HTY±S)´7TaO„ØYæMÒoˆ8÷ ±J‚rÕhZe-i—]’šØ…cq>ñÝ¥§„½ÉPiУvÀºTO”œR7Az™Ñ.…H%¤:Ì'”‡V¸L8µ¬´ØS…ã–S‰[ƒ;…Ý))Ù;ò¶ššTn(‹›˜S¦0ã©v\vÛKN|VJSt¡*)×hÏÚuÈ?VÄf¹ŸLu^k'¦uW³r,¿úuvW•ÜÁQVÚ^Ê®;¹]ÛÔØ½mA¨H˜YCì•:’Ê<88 ÷•WHAR”‘|Gpá&«~¦“LDÔO©áÌÒªT¸š z=1è…‡Ø}+~ci’—s .¶ÓèmjÅx…”äQcåM‚¿>‘¥J®³1×W6T¨¯0¶CKd2«¥AG0¤¼ŸP›G]7"=2\iŽÉERœ%ã²ÒÃβ¤vR²L’ÚàŽÃÓ[3w3R¶”mº6å!”FZn[k“ÎXi.9æx¢ë  Ž#¾!7Ósnf«tÈS·)ÑQ܈¹%a¢·-žW–+uJ½²ô·mE.ºœæáBk•÷.@* J@JR”H JR ”¥$€ Ôâ6&çv¨Õ.4(Ó%¿ s£·|wú†P¥¥E¢…âm~D’¿)íÛPô*¤Š=M£¡§C8Û ”:ÓˆSn6«@R¤’’/pA±Ý×›]]RÔ¡´•Âv¢òKS$:•!ÅܾW‘BÔ¥` À ï Õµjò'JˆÑ¦“ô…Uc&0+JCåÎ%(€¢O•}¼ª²ÒÜz„µOãu‡Õ³!æØyÇÓl™m·•:èºAmJIº“}í£¾ª»^UEt–b-Ai[‘•%” ¤« VÓ¨vÉ XK Þê !$IíojU­¹*T¸4Ø*‘&¢ºƒŽ®LÄ­kQ!‡Ò^m$¹ý5Üœ•pƒªí ¥7oA®¿&¨³b¦Shn¨ÂŸ.)°8³Ì›¤ßqîb•ëIÛU˜ÔaVv3B>uH[Sí¶»`âÙ äCjÉZ’sE‰Í7Ù‘º ÔΉt*A-!Öa<¤:µÂaÅ­e¦Âœ( ²œJÜÜ.éINõKÚv¥³‘¶&üìd0Ó s¬”›6ÑOá;$_ŠæÙ«Í ÅVÙS©;1ÚõEN±!ª‹P•-6ÜY q+% ´BÙZR¤f‚~•µUÕª·½^«Qª0dPi‘S”ÜÙ³Û2ïHF;bém$ò»p”ùÍ’,›Ut{R•㻦“CçéüFk19°Ï‘acq{^ö¸¾£5'µ*¾ºi5Σæ³/‡<98ÖŽV6½­{j3@ÓM4 4Ó@ÔžÚ§Ä©Õ\©,%vKh‰ÉûŠPJ[i»¤)D¨êH°6%X¥Qš˜ÚÕÓA•)ñLƒPb®*Ñ(ºœP²2)SKBÒHÊî•­&áDh'(»EZ³Vjžôê¥2–´!é¨cêS™`”´…bO•y(9Æ0V..íòT%¦:e<˜Žºìpµ–ëa R/Ø© ¨$‘ê6üç×S‘w3QÜœÐÛ”‡)“Óª¦-rxëiRPâTÜì\)ùÃÛ²qˆ«O—UªKªOwš\ÇÖûîb›‹QR€\“Ø h5tÓMM4Ð4ÓMM4Ð4ÓMM4Ð4ÓMRri\;ZsŸ.®l˜œ8[0¼²¿{óÚÖí¾ý£5'&«Íµ Pø1é&É—ÍóæCÇv·ï~ù{­Ü#4ÓMM4ÐXvµ ›W‹)rë.Ây¤-À‘¦P>vC…Iãl©IJp-JºB2(Jäè{E[i9ZŒôçV˜²%•1¹ „0¥6ü€¯šx¥²RŒp¶{Œü±”]ÌÕ>€ª,¹HªGT£(ªJä¡ex€K/62Ç qärÖÈßZ5tÄ£0ibJq iê‹eÓ%Ö—|›ó,¡ƒ‰(BTSt’B–ËÛQ³¯E©IqQð— –¢)Å]†Ÿ*»¤8•)$b‡¤¤dÜ{nÄ ?V¡î)Õ$5QdÓTI-Jq“ʵ('ò))ænö¸Ñ^äsÀ¦1J¦EvC.c ­/He BÒ…'.1æm²T”%j(º”J—–Œê¤‰tÊu9Hi¨ð´¡-‚9µ•)ŋظAJ2íåm±ù#A£¦šhi¦¦šhi¦¦šhi¦¦šh;àgýå6Ÿí™þÍýzUݯ5?ï)´ÿlÏöoëÒ¨þí[:ÕA{YþÊ·Wðy_ÒV¼Æ©þ0‘ûïN}¬ÿe[«ø<¯é+^cTÿHýá× æC´û£ð‡¾ÿJè´øÕî•¿ÙŸ½.¡ ¯·cU0¥KaÇä¼ÚYéâHwZZ2ÉinäžÁ$ê¸ËIÛ4ÝÅ)©L¾íÊ»´ú$&ÚêPÛ€¶BuKJ’ØêNa.(Ùd§° ¬Ñkz$¥K¢ÕgSd-µ;BÙYA ”’’ ®·êS«zlYq)ÕYÐãÍG¶˜¶Ðú,F+€¡e(XßÔþ}s‰†³·h íª,ªlê䪼úZ§*/‡!LùyœÒîIJÊÏÐUÂr%!E(Ë3b†vS•æêí.Tx¬Ì“ |H)eÕ¶””§—œ›ºÙºÙB$¥ju–kvhÏQYªÎn™!aÇ¡¢BÃ,cæRÄŸ*{‘ù#ók{åŽîð¿ ùS\ðþŸ¥ñxx±Ç ­Ž=±µ­ÛA—wE‚Í+kˇ ¨‹›H.É ­j:™Rä󍨩-$,›ÞÀzkÆ£_-Mk™†!L˜Z*)K¥ˆÎ¼”(‚*S`+ ‰±Äc­nÍ[Š˜•­ÅW©GBÉj\×@X¢EìH¿ë:Œ‰"DILˈû±ä2´¸Ó­,¥m­&áI#¸ ‹‚4ø4˜;ª­¢’ÖÕ†šrŸ}ÆTµ´ýŸSaÔ*K©BÊ[9¼”äÚ€%jKg$=³•½ª»~¤Üj£I¢K—àéR˜q%¥”• “ˆî  …•ªû;§s3Yz´Îâ«·S€ÛÓ5ÀûˆùT°r#ÊžÄþHüÚÙNùÞ©”ä´ï ™!-­ÑR{5!%E)'+ Ô@÷d9Ð6ÄX2öÞëT˜M;"%9™Qd¬-•õl4 PI Kʾ@ú [½ëÚœ¼wu?¨è7Mr'Rú¤?ÁPu®ªÙ8«+Ì£auæÚƒÐYª´ -2–´Ë®ÉMlB1¸‚Ÿxî‡ÒÓ‰B^äÈ(4èQ»`]*H'ÊNõWfÓ[Ü4¿GÜΨ՗ÇKðxi¹M¡håPZˆpt -8s½Ð+ß(+þà>9SðÐ:µôÿK?ûwÇéy½={úé\ÜúïŽW*uN ¸zÉk{+eŽDÚö·­†‚ËQذš®ÒàÁÜ‘¥±7˜º¤®3G $-jRX}ÖÒ’“åRÜ@ºVV[BJõ¼öÂÛôáUv­¸'*+T†ªp^<‚¤‰Œ¾@‰»…Ü¡ÃØäT’’ÙªÈÞ;ºLè³änšãÒáçÓ>åAÕ8ÆbËÁEWNC±·¨õÒFñÝÒgEŸ#t×—>™÷*©Æ3^ *ºr½G®‚cll†jÎU$K©»L§B[ mÉ(ŽÃïÒµ²¬}¦ÀSm•vqGºq ¨Il F;¶–Îá¨A›]yt†d8äie²Ðu\­©+@!ô” ¥[<ri7~ŸT‘U\©Ä¨IËžSÖ‡ÉAJÉ`ÝWPÜ÷"úËNÝ;š›*\ºvâ«Ã‘5|’Ýbk­õÜœ–AFêQ¹¿©üú}5–\‰å=.[îÈòÔ㮺²¥¸µ•(žä’nIÖ-M4ÐN{>«¿võ.{\Ñ&U#0ûyæÚÝJT."àžàßPzڤϗJªDª@w†\7ÐûbƒˆPRMˆ ØØ‹kW@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 NM½…Iª!«K‘TœÃ®d|ͶÔE Zö.¹Ü œ»ú AëiÉòÜ¥±K[·‰÷_i¼G•ÇÚVok›†›ìM†=½MÃWM4Ð4ÓM«k7Fb*}rŠÓñTµ´™.Hq.¸¼-EJ”‡Y-LjBK~K”¡é=©KÚµM¥1.® SbÓ¥J–ìƒ(JCÈ ,†JŒR¸<¾rµ¸”÷SZ­QwNæ¢ETJ.â«Óc­eÅ5kŒ ¬€ ˆIö_õ j½X«½FfŠõVs”Èë.3 rXmg/2PN ù•ÜÊ?ŸAfðê\•QQ£5 £NŠÄ€yë•­Îîvc§PÈ—-_$…­MÖíFÒDø›V DÚ‰4äG•!×:FÂÃ…ÞWP+SiJÐ ™xyq¶«/V*ïQ™¢½Uœå2:ËŒÃ\…–YËÌ”ˆ>ewòçÖ³Ò$<Û-¼û®": l¥k$6‚¥+ƒè2R‡½Dûô´ÓMM4Ð4ÓMM4Ð4ÓMM4Ð4ÓM`ü ÿ¼¦Óý³?Ù¿¯J£ûµæ¯àgýå6Ÿí™þÍýzUÝ«gZ¨/k?ÙVêþ+úJטÕ?Æ?xuéϵŸì«u•ý%kÌjŸã¼?ù× æC´û£ð‡¾ÿJè´øÕî•Ó`KœÍ®Å°Õ&¾ä¨«jA¨¢ÌT¡ðòËZŠÕ”eub Ž‰Í¼ÎÛÜuúdýÁ+o>è¯I†d™FDØï-²ÓÉFHUÊ”ý¸Ð‹ º›“Ëô×8_dS)=š·1ÊVÞ•ZnT¸²My ZY»imà弆 .© ’°¤¬bÝpè°¶- T*M Λ sìÖyÞeàë…$GKÅHRšJ …aHA)KJ>šÎößön+»}0E2U5Ê£lºìš›1Òì%en¿iªq.„¥*OÍÇ]*EÔ–ÄeÖ‚¨u94´yüJ—ÐȬ¦JBÒÅ¢>áCÀ…-ÞD)áƒ@­!«¡zåzh&7tdF¬©(j‘­ RãRå*KϦ!µ…YÅk;\R™Í.s4 » ÃTšû’¢­©¢ˆ 1R‡ÃÈ-h+TrQ•Õˆ68)zh:ÎÆ¤mÉ*¥QÝõzD™ »­Èž©­°´ ”©RiÇã–Á.)AE·ï¸‘AƦÔ즳Nz­H!v&3Ç"“fš”ÛëC|·$’Ћ¸1)ÈXê…¦‚ñ\‰5^Éh)~©L¡›1ôƈî<Ë¢/K!±u‡I@MÒr*÷ÕM4 4Ó@ÓM4 4Ó@ÓM4oeÚŽÓþ7úèÕg[T˜êµH”¸ sK˜úa¼‚sqj H¹ ’;“mjèi¦¦šhi¦‚é°%ÎfWbXj“_rTUµ ÔQf*Pøyå­ÅjŽJ2º±ÇDö~›+y×$ÑRÒiŽÔd.i®4KŠ(Å6ŒmaaoKjMM4Ð4ÓMM4Ð4ÓMM4Ð4ÓMM4Ð5f¨ÿeÔ/ãu/èAÕg[N@–Ý-Š¢Ú´IºÃNd<Î6–Ô±kÜX:ßr,ríèlºi¦¦šhi¦¦šhi¦¦šhi¦¦šhi¦¦šhi¦ƒ°~ÞSiþÙŸìß×¥QýÚóWð3þò›OöÌÿfþ½*îÕ³­TµŸì«u•ý%kÌjŸã¼?ù×§>Ö²­ÕüWô•¯1ªŒ$~ðëÐsG¡Ú}ÑøCßž§×ÏC†õ”«\'6Ž íÙ ²G¸ ml}¦æèëËÒyXÁ(f$%Ëx•ågÚ<Á„ãe¸²’¶ÀBŠ´;6£.© = ØÕ(•I²"R¤™ 1Õ”™ ]ÚR‚BbsH¸¿8hÆÜUôcIn=!QÊ‚·)Vý•{üò›._¹±Ê㵈°ÒNâ¨H£ K‘é Ž„·HЇì›[ç’Ørý…ÎW=îMβSvr£K]J3¸ò—f²ÓδÒT§m¥¬-Ä€…ù’’.…T ÚuÄP¬­ˆÉŒËê ÖD„4µ!(qLgÊ¢ãvQMˆZHìAÐ'ޟš- ;é}8lÝI½‚°hfžýЫ¤ûÁÒFé©¿:,ÕÅ¡‡bçÆ–èpÐÚ²9¶–‚\ýYƒ‰î,tŸ´·..Óù}ôÆ Çy·Þmõ_\mµ)M:l XJ‰J…®•Y#jÕÙM2Kò³(*±¤¥%©Å6âƒi ºŠ–R 7²IùSSñOéh|üø>rÊü\XeËÇ+v½»itÔØ*j"Ð˲°äK”8km8‹ SE-þ¼È÷7:ËOÚI›¶¶[“Hëj BqFa9N6µ$ÇËÝG° %I-ohÈ‹!÷)²£L¦·5˜"S“b&Ï:Þi ¾âPžËóæQäUÈ!@¬ ÓS…ÔpÅ¡¯¨}O¯ž‡ ë)V¸NmÛ²dpÇqT#Ñ%¸ô…G(Z ܤE[öUïóÊl¹~æÇ+ŽÖ"Ã[-m•¶Æéf¢û°ê{}NE %Ä,‰-Çq%À¿)JœI ±î;^½ ˜“¸ª(Â’äzBc„!mÒ"¡û&Öùä¶¿as•Ï{“s¬“÷MNoO͆Žô¾Ž 6n¤ÞÁX43O~èUÒ}àè½§\E ÚÊØŒ˜Ì°‰ ÍdHCKR‡Æ|¡*.7eØ…¤ŽÄc­mªÍ*dÔ#4„†œ ÉmÕÇp‚Co% *eÏ*¼Ž«È±o*¬$nš›ó¢Í\Zv.|in‡ «!c›ih%ÏÕ˜8žâÇO•5?ñ.–‡ÏÁÁÃáÇ,¯ÅņWü¼r·kÛ¶›–¹Z§øŒ—éî1OSÒ%/Ÿe·¿ír’Rå­‰¢úÖÝt¯Ý5j?QáÓ^‰Í†œk(Ë›^׵ʹ1÷MM‰Ò¦¢- »+D¹C†¶Óˆ°Áµ4RßëÀ ss¤ ÓS…ÔpÅ¡¯¨}O¯ž‡ ë)V¸NmÛ²dpƒÓA1qT#Ñ%¸ô…G(Z ܤE[öUïóÊl¹~æÇ+ŽÖ"ÃI;Š¡"Œ).G¤&8BÝ"*²možKaËö9\÷¹7:‡ÓA9?tÔæôüÑhhéßKèà¡ÃfêMìƒC4÷î…]'Þ’7MMùÑf®- ;>4·C††Õ±Í´´çêÌOqc¨=4ý©½¤Swí'qωL-E}ú#Qâ"ì¥Ðµ !)K½œXì2Q‘÷MM‰Ò¦¢- »+D¹C†¶Óˆ°Áµ4RßëÀ ss­m©JñÝÓI¡óôþ#5˜œØgÇȰŒ±¸½¯{\_Qš Ȧ§ ¨á‹C_PúŸ_=ÖR­pœÚ8'·d&Éà5Ž6â¨G£Kqé ŽP´¹HŠ·ì«ßç”ÙrýÍŽW¬E†¡ôÐLIÜU aIr=!1€¶éPý“k|ò[_°¹Êç½É¹ÖIû¦§7§æ‹CGNú_G7Ro`¬§¿t*é>ðu¦‚rFé©¿:,ÕÅ¡‡bçÆ–èpÐÚ²9¶–‚\ýYƒ‰î,tùSSñOéh|üø>rÊü\XeËÇ+v½»jR{jŸ§TDIr¤°•Ù-¢$C&Cî)A)m¦î¥ {©"ÀØ•b•Ì}ÓSbt©¨‹C.ÊÑ.Pá­´â,0mM·úð#ÜÜétÔáu1hkêSëç¡ÃzÊU®›Göì„Ù#ܦ(»EZ³Vjžôê¥2–´!é¨cêS™`”´…bO•y(9Æ0V..íòT%¦:e<˜Žºìpµ–ëa R/Ø© ¨$‘ê6üç×A'qT#Ñ%¸ô…G(Z ܤE[öUïóÊl¹~æÇ+ŽÖ"ÃI;Š¡"Œ).G¤&8BÝ"*²možKaËö9\÷¹7:‡ÓA9?tÔæôüÑhhéßKèà¡ÃfêMìƒC4÷î…]'Þ’7MMùÑf®- ;>4·C††Õ±Í´´çêÌOqc¨=4Ÿ*j~)â]-Ÿƒƒ‡ÃŽY_‹‹ ¯ùxån×·m#îš›¥MDZvV‰r‡ m§aƒjh¥¿×€æçPzh' nšœ.£†- }Cê}|ô8oYJµÂshàžÝ›${€Ö8ÛŠ¡Œi-Ǥ*9BÐVå"*ß²¯žSeË÷69\v±‡ÓA1'qT$Q…%Èô„ÇBÛ¤ECöM­óÉl9~Âç+ž÷&çY'ޟš- ;é}8lÝI½‚°hfžýЫ¤ûÁÔš ɦ¦üè³W†‹Ÿ[¡ÃCjÈXæÚZ sõf'¸±ÓåMOÅ4·C††Õ±Í´´çêÌOqc¨=4Ÿ*j~)â]-Ÿƒƒ‡ÃŽY_‹‹ ¯ùxån×·m#îš›¥MDZvV‰r‡ m§aƒjh¥¿×€æçPzh' nšœ.£†- }Cê}|ô8oYJµÂshàžÝ›${€Ö8ÛŠ¡Œi-Ǥ*9BÐVå"*ß²¯žSeË÷69\v±ÙÚÔ*m^,¥Ë¬» æ·L>F™B@ùÙ'²¥%)À8µ*éÈ¡+“¡ì9m¤åj3ÓZbÈ–TÄä6ÀZ”Ûò¾iâ–ÉJ0UÂÙî3ò„ÅP‘F—#Ò! n‘Ù6·Ï%°åû œ®{Ü›dŸºjsz~h´4tï¥ôpPá³u&ö Á¡š{÷B®“ï[KÛQ³¯E©IqQð— –¢)Å]†Ÿ*»¤8•)$b‡¤¤dÜ{nÄ ?V¡î)Õ$5QdÓTI-Jq“ʵ('ò))ænö¸ ¦¦üè³W†‹Ÿ[¡ÃCjÈXæÚZ sõf'¸±ÓåMOÅ4·C††Õ±Í´´çêÌOqc¨=4Ÿ*j~)â]-Ÿƒƒ‡ÃŽY_‹‹ ¯ùxån×·m#îš›¥MDZvV‰r‡ m§aƒjh¥¿×€æçPzh' nšœ.£†- }Cê}|ô8oYJµÂshàžÝ›${€Ö8ÛŠ¡Œi-Ǥ*9BÐVå"*ß²¯žSeË÷69\v±‡ÓA1'qT$Q…%Èô„ÇBÛ¤ECöM­óÉl9~Âç+ž÷&çY'ޟš- ;é}8lÝI½‚°hfžýЫ¤ûÁÔšåø'×&Ö M›Õ³Lk„ÍÇ£¦G‰{Ãzùp¡9z ^öïk\ëшþíy«øÿyM§ûf³^•G÷jÙÖª ÚÏöUº¿ƒÊþ’µæ5Oñ„Þzsígû*Ý_ÁåIZó§øÂGï½4z§Ý„=÷ùãÃöý{HüO·¿t÷ú“ªN®ÞÒ?íïÝ=þ¤ê“¨<íùÅ·—¶òWE§Æ¯t¦(5˜´È²£KÛ´ŠÂ$-µƒ4<Ñ@Xò)§ fà’)6ì5/Gö…ZTWâÓ*s›ª.ªËÓ#åÃ%Å ¼¤¥%)óñ ƒŽ ·‚¼ÚÞö7L U§Uãn) e¨¬¼ÜÊ“’Ôbe°Ê‰ ¸‹‚—Ò dJœŽ›[eÃÞr¥Jƒ)ª&ÔW• ×[R¢AKjuçRÂyIâKÎ{Ô’ùÄ‚FæjM3¡•·)†ëP\Rä…Âik[¶ñx­Å¨Õæ±*Š¥¸SR¥¢4Ú%1ÙÈa¦SÉôÈ ´”¡±ˆp5Ù´%ã¹æêók*itÕû>‘YbK®Of£<†Ü‹ˆh8‰*OÒh68âm–JœX#Ùõ ¢Ô&™šåFtWÞBÖKèm–‚ ¥‚9–<¡7½Ïr{§Ú5ksÅ‹¹©¡™H”ï,ÙŠD…€A l¿Æ€¬ø’ݯdâ;kcTª»Ž›[›3®Ó®YBåLZ²õ /©òøÄÙIJ\ I€ —”Ùš©õJ}.â¦IªÈª7Jz:¤1ä}j)É!§\s‰*I SˆiC$y.HLfí¦íøû"Q¡¹Ô-ù³˜zJÙq—œ ¦:’l­hJuv-¨‚‚‹ù²1;¾*.ï¸[Áø_™ m¸ËO)÷VŽèRÖ§ ΫV⽟ ­tŽNLj!¥-§“huÖ}¤©-º·¥›žW Û9œ@kmšDz¢§È1Ø)Ñz©N2ÀyÜ ­´„ (æê/u&ÉÈ÷ 9DÚ4JµNK¹]ð˜¨c nÅj:Öã¨È¡A÷Ûi%%.$ŽR¥ct%i R½ü¨W§IÚ;zCµå•MÍS!N6ê’œd ëažä‹ãdŠ^­ìÐY§5¾éH‘¤T(Œ)M¸çÍ:ÜæX^ÀRT—ô’Od‘{èÄ‹ßg)ʄП¯¤J ^e§Y’TÙNXXRAÇ.ç¹1T· jT´F›D¦;9 4Âjy>™¶’”61»6„¢üw \Ý^mXw´éõÚ7ƒÏÛô…@™’ˆâDâÛE¼€CH2 Zl¥kF( 'µŠRSBÓAf­î¸õz¼*Œ©COLÂS ¹,6ûhi-4wʆ BlR¤’G›-hï éÜ•ù5§)iò%-NÈLB©Jp‡² *ô'°°ï¦¦šhi¦¦šh$ö¥WÀ·M&¹ÁÔxtÖeðç‡'ÂñÊÆ×µ¯cmFjsÙô•]û·©sÚæ‰2©‡Ûȧ6ÖêR¡pA÷úƒÐ4ÓMM4Ð51µ«¦ƒ*Sâ™ $Å\U¢Qu8¡ddR¦–…¤ •Ý+ZMˆÔ>š ]ÌÔw'46å!ÊdÅ´ê©‹\ž:ÚT”8•ƒ·Ç; ~pöìœb*ÓåÕj’ê“Ýæ—1õ¾û˜„æâÔT£`$öÚœÛLÓ¢íj®à›JUv4ØÛ)ÇRÈ"BÔ¿šZàHkYJ¸&Ähïj\z&ó®Qb-ÕÇQ‘¥:AYCn) ¨€ì;Ø >ši i¦šši i¦šši i¦šši jNMW›k@¡ðcÒM“/›;ç̆Ž6ín Þýò÷[¼f§&ÀˆÞ¤ÕեȪNa×2>fÛj"-{ \îÎ]ý‚M4Ð4ÓM†‹¹š§ÐE“·)Hê”eI\”,¯ eæÂ‚FXä<ŽZÙëF®˜”c 2 IN!m=QlºdºÒï“~e” q%JŠn’HR¡ôÐN/r9àNÓ¥S"»!„F—1†Ö—¤2…!iB“—ó6Ù*Jµ]J%KËFuRDºe:œ¤4ÔxZP–Á‹ZÊ”âÅì\ ¥vò¶Øü‘­_wí.âfŸLjöõyºKo6ëŠ\¶Ô%ãÙ©Iäÿ¦Iù°„ù×åú8… M4Ð4ÓMM4Ð4ÓMM4Ð4ÓMM4ÐvÀÏûÊm?Û3ý›úôª?»^jþÞSiþÙŸìß×¥QýÚ¶uª‚ö³ý•n¯àò¿¤­ySüa#÷‡ÿ:ôçÚÏöUº¿ƒÊþ’µæ5Oñ„Þzhô;Oº?{ïóLJíúö‘øŸo~éïõ'T_÷¢à7m.©L˜§ù!,¸®âÖZ°;ØýqqÚ÷Ϋhø§'ך˜“#lªŒH«·SÁÈr¨ÚØ+Èñ¬{Ørv¸î«wÉ>VÑ_OÐPëŒbúTÿ=a§sk¾IM£'ÖQȪtzjrD­¢©ÑW‡\n"sêZr°ÒÜrãÉ‚Ä`cÜÝ*¸ì1õÓªÚ>)ÉàuÏàǃÆæåËértÖÇØá{÷ËÝ ƒÓS‘åmΔ¹:㑇LÓu†ãv|Öc»žâÉM‡c—®%mu}¸þO©LpVk»b•^2²Pïu Aú£A¦¦#HÛ)£¤Ò*îTðXª6†Íñ]*©©ÞpßCì9ˆV!AI6 ƒbb-­]^=ŸT6Œoj;zo‡ÔáSÚ›YI«4x]¤ó-΂ÐOª,“؜ƠãÊÚ))r(uÇ"+™¦ë !Æì<ù¬Æ!w=Å’›Ç/]šœ+h£¨ëèuÇò}Jc‚°ÓX5Ûªñ•’‡{¨bÕÇFÙMµ&‘Wr§‚ÀÝQ´0o‰â,Xv¸äïcÝ7ìújbL²ª0j5"®ÝO!Ê£k`¬[#Ä ±ïaÉÚ㺭ß$ù[E}?AC®1‹éSüõ†Í®ù%6ŒœT{YG >©ÐAé©É¶Š§E\zq¸‰Ï©iÊÃKqË& €Estªã°Ç×N«hø§'×¥§+ -Ç.<˜,F=ÍÒ«ŽÃ]šœê¶ŠrxsÃø1àñ†¹¹rú\5±Ç¶8^ýò÷iVÑLéK‘C®9XtÍ7Xi7açÍf1 ¹î,”Øv9zè ôÔä [EG_C®?“êS†šÁ®Ø¥WŒ¬”;ÝC~¨Ö8Ò6ÊhÅ©4Š»•<†ê¡€³|O`ªÃµÇ'{é¿`‡ÓSdm•QƒQ©vêx U[bÙ ÀU{N×Õnù'ÊÚ+éú qŒ_JŸç¬4îmwÉ)´dâ£ÚÊ9õN‚MNH•´U:*ãÐëÄN}KNV[Ž\y0XŒ,{›¥W†>ºu[GÅ9<¹áüðxÃ\ܹ}.NšØãÛ/~ù{´zjr<­¢™Ò—"‡\r"°éšn°ÒnÃÏšÌbsÜY)°ìrõÒ­¢Ž£¯¡×Éõ)Ž ÃM`×lR«ÆVJ?Th õ´äùnRØ¥­ÛÄŽû¯´Þ#Êã‰m+7µÍÃMö&ÃÞ¦ûѤm”Ñ‹Riw*x, ÕCføž"ÁU‡kŽNö=Ó~Ó•iûeÏe4ˆ1¡ÎMMºŒµªM¬!e¨aN)°ÈWñ! ÈbP¯2ýRôÔäù[E}?AC®1‹éSüõ†Í®ù%6ŒœT{YG >©ÒD­¢©ÑW‡\n"sêZr°ÒÜrãÉ‚Ä`cÜÝ*¸ì1õÐAé©Î«hø§'×¥1ÁXi¬íŠUxÊÉC½Ô1êc#l¦ŒZ“H«¹SÁ`Hn¨Ú 7Äñ ¬;\rw±î›ö}oTkz”X‘*5YÓ#ÂGFŸ·Â,(‘d¤X[Ð~mmI‘¶UF F¤UÛ©à€d9Tml‹dxƒV=ì9;\wU»äŸ+h¯§è(uÆ1}*ž°Ó¹µß$¦Ñ“Šk(äÕ:=59"VÑTè«C®79õ-9Xin9qäÁb0±în•\vúéÕmäð:ç‡ðcÁã sråô¹:kclp½ûåîÐAé©Èò¶ŠgJ\ŠqȊæiºÃHq»>k1ˆ]Ïqd¦Ã±Ë×H¶Š:޾‡\'Ô¦8+ 5ƒ]±J¯Y(wº† ýQ ƒÓS¤m”Ñ‹Riw*x, ÕCføž"ÁU‡kŽNö=Ó~É26ʨÁ¨ÔŠ»u< ‡*­‚±l`*ǽ‡'kŽê·p‡ÓS“åmôý¸Æ/¥OóÖw6»ä”Ú2qQíe€ú§I¶Š§E\zq¸‰Ï©iÊÃKqË& €Estªã°Ç×A¦§:­£âœž\ðþ xë 9ó8Ú[Růq`ë}ȱ˷¡¶®§&ψæÂ¤ÒÐíåǪN}Öñ>VÜj"Pok–œì Æ=ýEÂ"$yå3#ÈòÒÛM4‚¥¸µ¤ä’lÔ¼§\ftXaˆÏªV|NÅšËìyNdók-§¥ä¡‚HR¬’µ¶¥WÀ·M&¹ÁÔxtÖeðç‡'ÂñÊÆ×µ¯cmYanJ%ºu.•9´ö¼KžSñPÃÉëb¢2±h8°®4 ,]iÌœ|–È…VµIG”˜ó›h 8ÛŒ¾‡šu#$8ÙR.’RM”•Ü2ÐèUÏ2¡&2gãÒ¥µ”•_ò:¤§#ŠˆMîBT@²M§¯ÀnµAM.³\¦D£Â\Fêq£¥›¯ºTKÀ'»å»r›¤{ñ;r¡Hð ´ôè‘åÊ-2bFD…¥l¡äÔãbÊ“–]°Ç+¤1@Ú[‚oP–©ün°ú£d<Û8úm“-¶â’§]H- )@©"×Ro®}Ò$×Ñ^¨ÆDË'pÄŒÂò$-å²¾¸T‚Ø„Ž@•ß2qÙTηÿÊÞâõ?ýo?‡aÿIô1æ¿'ýßÉ·Ñü¯vƒ,µYFgc4#à‡T%µ>Ûk¶-®D6¬‘e©!'4XœÓukmVhñS&¡¤ ¬4ànKn®;„y(QS.yUäp%^E‹yUiÊ–æ¤?Fœë"q©Ôié/G[( 0ˆý7Υв¥•t©òÓnCæ8y›ÓsR*‘k*§ Ê‘^«¢­-²†ÑiüÒ¢ð¼•yÈoþØòù¬ªÒàK©În&¹_rä ”¤T¥)D€”¥ ©JQ HI|“®x§‡pFσ¨ê:Öz^,±äê3áÇ?&YÛ?'Òí­èû†;;âEV]R¯Y&+ñ›5°%”=L”—Pp’ç˜ ¦ý·¾RP8¼ ’§á á]wJŽ£ÿ]Öòprcô¾o_O=ïäÐAÇÚuǧJ†XŒÂ¢áÊ쩬°ÇœdÞ/8°Ú³H*F*9¤&éê"\y%=[ÇÊÔÛ­:‚•¶´›¨à‚,AÕÎnä U›¨Òç¹S…OwÃx%1¼®Š*ã'&‹ˆ äJÊÍ–¬ÇÏ|…kuÕ|wtÕ«œ?ˆÍz_yñò,¯¬/kÚöÐFi¦šši i¦šši i¦šÁøÿyM§ûf³^•G÷kÍ_ÀÏûÊm?Û3ý›úôª?»VεP^Ö²­ÕüWô•¯1ªŸŒd~ðëÓŸk?ÙVêþ+úJטÕ?Æ2?xó¯A͇i÷Gá}þxðý¿¾ÑÿíïÝ=þ¡ªV®¾Ò?íïÝ=þ¤ê•¨<íùÅ·—¶òWE§Æ¯t§6õº\Ú½^£&>#ìÆ&,DÈyNº—T"œlc‹.\åpq’2·µ'T%M4¨ÀjS‘âHqÄF\ìO`ËN(-Ç(SQZS¤„¸°T@&×=ì¬,í•]&±%ÚTŽ·•éPRËíôŒ&Cø´—•|ÚÓ…Öœ•pp#Z¤Ï—JªDª@w†\7ÐûbƒˆPRMˆ ØØ‹jpï -È„iôše:^£þ…Že2ïPØiü”ãŠsÎÚRƒŠÅ€ºqUÔCKmÔ)Rgµ"-V*¥CzzÚ„Bã­¨9›…´Ù]¾pÜcï8Œ[z].m^¯Q“ Ÿöc"d<§]KªG‘N61Å—.r¸8€ É ›…3*¤É¢S ˆÔÌŸÐÙRÖFAÎSóŽ-w.so¢uŽƒ]4¸² È¦AªÀ’¶Ýr,²êQÊØXCM- ¸8-–'3pHI`‰ìÿ§„TêÝ=BEmú73.JiM¥EÅ©H-µ“ÍÙIJÕlŽ"À*£Ð½á~%œn~ z–ù²Ç+ñež6ü¼q¿kß¶¬·Ý]™NN• Bˆ¹T)ô-+‰1Â’·–Ô”KmœV• `, ¯õ,ø_Gáñ¹ùùzÌœæÇqÛ<1¿{ã•ÿ*Ý´ ûN¼ÚЬ« €ÊÀ”÷"OäŒN¿Æ$ø…ø§QЧªâêºLxy0Ë›ÝËl<׿“Q«¦á5z}2 $) iP›.©…4¶ËN·u­NYhRÁ!y Ž%6Þùa'¬¿„Ó<3¢è‡H‘²U¿;^T¹_‡>\8ócÆw§0’œAͰâ¸íçmâǵ½ ÕÐNM½…Iª!«K‘TœÃ®d|ͶÔE Zö.¹Ü œ»ú +"Röå5·š½^}æ8JãÆ>VQÅ”¿;‡ n‚Â’@Rr|·)lRÖíâG}×ÚoåqĶ•›Úæá¦ûaoSuR|ºœç&Íw•÷, J@)JR JRR”€ÐNoxt˜Ûº;lFðÊ|ˆTù.·)îhŒºæÅÝ^e¨€¥þaqëªô´ÇL§“×]Ž ÒÝl!jEû$’=@&ßœúëz¥^«T+¨®?/Ž ßÑ›L~.$¥ à–ÂB1J ZÃZ2äH—)érßvD‡–§uÕ•-ŨܩD÷$“rN‚ß좃M®T*)­. x Ц¿ÀÛ:âP•…—’âÊðmDò%•¤ œ“½ì×hÓžÜtTDÚÚ©I§ÈaÐã|A交)+i@>€‹±eàI¨S·~›À§W*pâÓ °Äµ¶Ùu%%.b„¯PR?0ÖÌ}㻣N•>>é®3.fKíÔKà,ŒÔub; úMÕ*•E²êòÚ›'Åéì71ÖœƒfÃ}BÁ‡»ß 95{¢À|·ª{&^³†gY[ŽÜån÷Ç~UûjMÕR30ç9=B5E¤[1’â[]À=ƒˆB»^ÝÒ;ƒk‹Kí°^¥n‰s!5-p©ØÁÅ­!·U*;<žE ”¥Õ Ó{\MWµ½J«N¥·9¸n4”OЍ’Rãp-¢¤ªÖX8¤$… (‚ÐY¶æÍ¦Ï‹ ÊžàvÒéÒªˆi¨<ùFŽ 9§çŠ£®ÈV(Ç¿&VAŒ©Å‚=ŸPª-Bi™®TgE}ä-d¾†Ñh* QH#™cÊqkÜ÷9vfõ¬m†eÄŽ®²›1‡™~&K錾T`µ”4â.¬<·7·b;„‘*­:U !×è ­×BB[¸æ¥) )dàu`€¶ƒô@GOÐI’þL%OóÇKX;ß$¦ËVI¬£‰?Tk{gGƒ+p°ÝI‡^Šë‹Jµ$`Ú”àoÏ Ap£Ì )î´gÔ'Ô:~¾t™}3 ŽÇ;ª_I¾-¦çÊ‘sdŽÂúRêéS›ŸK& ¶¯ÆügTÛˆ¸ ÙI ‹‚Gì'As­mÊ4™É—-S(±é ü¸ rgV ²Á[-¿Ä´€êÃX8RBZ*ÉÂA^-¹¶(­oÇ)•Ér^§µK]U’Ìk©öÄ>­´¸žD”Ý»d”®÷)X¿"k_(+þ;ãÞ9SñÓúµõGû—Ëèù}};zipWéõIXÊœJ„œ¹å1-hyÜ”¬– ÕuMÏr/ ²Ê mi›r=Z.¸**¤Ø`F¥¥m¬£„²ØAZn@½Ü7$Xá“”}I±¸+ìxr¦×Š_Ä0–±Õß+òØüåòUò¿Ò?œê3At®lvi[Iº³µø=y‹Z¡ウßR„‡‹ÅÀ—¢ÊY $¯æÚôZ,:lß©¾ÅAôïIãRcZqKP.Ø: ¶ _Ì—2q²¨?”ÿð©øGèZú¥Ÿý»ãô¼Þž½ýu–NéÜÒœIÜUw–%"`S“\Q”½rPí ²³µ¶ë»éêDº„æiÉ Š‹E„9E92[kyA$‹•² †)À(T+Q#Ä”“S€ú‘_—G[ȹJ”±`´­7 ?DúµQÝ;š¥*$ºŽâ«Ì‘ |‘~kŽ-…ÜI%&éI¸· üÚѪT'Õg9>©:LénÛ‘ù.©Ç`º”I6؃f‡M…Qæê÷2ÇŽ=crË{ßœô°½íê-~öÙC¦Iê9·êiìÌ<ɳ‰Á…Y'ÜŠ»wHÔš ˆÔŠ{´c9ÍÓH !jjQ|”ÞɺY-ÝVóÛ¸¹ì“H§µF›Ý4‰ £¶¥ÁU®›©ÝÓs=»Úðúh¾ÈÛ4jžìDiö¨HFÚbwPqô:ð€™n-Û¬©Ýbí¤Û€ƒruBÕª¾êô­Ãº¨Ðf͉NðèËq `¶Ðoˆ+(êmEÀÙ( Q*Ýî”ÛooR[ö‡´"ºvÛmBå.”)x뛸µ‡Üªº„S7/ªCД˜ët—‚ÒT«§Šêl%D ‚«ÛPÿ,ww…øWÊšç‡ðtý/ˆ;ÃÅŽ¹S—P§å­o5ŠŠ“ŠÉºl¢H±ìMõеX«Öå&]j«:¥! ¥ÙròÂ$$mrM¿YÐNmÊ.Ù•³*5ªÕV¯DZŒh©LH ¾ŒmåBA$–O¼caô²ò);bšªN§_«N¤=K¨µ T? ät—éH@.'ç™XRW€ …Y‹X«Ñ%*]«:›!h-©Ø’ÊÊ ¤”mp ¿PÖX‚¿O¥È¥@®TâSäåωkC.ä•d€l«¤n;m†‡±ESi9WM]¦fô²&3Î$²ÀZ–|î¥Õ4å‹m8‹€ ÒCœxº µÙ[UUõ/Öê^Œß$cÆÇŽ KɵÐ÷r¶×s忍Ê^ñÝÔ¨-À¥îšä_ˆÕ[m$›%*\’i:ÖùA_ð/ñÊŸ„~Õ¯§úYÿÛ¾?KÍéëß×Aa®Ó¶” ¡¶8ÝœgÕ™³$ªÖÓ\Ï2´7ÿP  W ( WÒ.$4â©mˆŽodQáKÂ¥µPTŽ˜¥|e8®2âþt +˘IY°(IòÄ;ºw3±aDsqU×ÛrJšáDe¶,…6/dŽÀ‹[Ý¥Gtîj”¨’ê;Н2D%òDuù®8¶prA$”›¥&âÞƒóh1nÔXÏFð:´š‹±›¦L.™Æ\ÍIÀ¤-`ùR•\(‹,Ä¥› £ÍÕî ezÆä+–÷¾<-9éa{ÛÔZý튵X«Öå&]j«:¥! ¥ÙròÂ$$mrM¿YÖŽ‚r™'¨æÞ48\O©¤s³0ó$ZÎ'dŸpV*íÝ#XãR)îÑŒç7M"<€…¨@q©EòS{&éd·uX[Ïnâäw´>š ‰4Š{Ta9½ÓH‘ ¡ 0jP|Z麙 Ý77óÛ±±=®­R)ð"¥è›¦‘VZ–YˆÔ¤­"ÄäyYBmÚÝûŽÞ¶‡ÓAØ¿ßï5´?÷Ëÿhö¾õi$DiIaÇ®,JC»¨Úÿ?¾Úø+ð-þó[Cÿ|¿ökî×Gd(61MÆL6¿P>ºM½=Ú‘ÉñŒÕåûk^tD wé*öw»ÉAAð§†&ý¬´ý`ù¯€ªŒ$~ðëï½ôØkÙÆíBmaIxöHHî´ŸD€ÿa¯*Œ$~ðÿç]öBèöŸt{QŸË·ëÚGâ}½û§¿ÔRuÞvNÛ¢îŠÍ&Ÿ]…ÕÆn•%䣕h²ÃÌ€n‚¢ùêðˆÙûsiø€Sº>«¨æùçË,~š­‘ôüúóÜïË6ç=­Âbxs„ã£ð‰íÇ«±¥“²ýÚ‹ÕžLšjã*áN8GþSÛŽ¨ìr]4ÓZN´ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4 4Ó@ÓM4ƒð2q-~»MÕåŠ µRTl!¿èsû¾ï2"!!³Q¦’‘cÿ^Ïüõð—à[ý涇þùí×ÞkÜѤT¦µUQžË)yæ!AIJ½ ýŸ²é½®/¿qš±œ;š÷Œ4b¯oÛþÏ7`bLG”i–d¡Ó`‰8adžæÝì=N¼ÿ©þ0‘ûï¿·6ãj­ìÃxH…S3cŠT–”CÅ`,Y'×õ+ÿ¸ ÷|Süa#÷‡]î@áOiÕ„M¶taÙûuÏcÿþ©¦ÿ•ýv5øZÿý³ÿûÿÇTgjüÑX&)`Ùy ²›úúë[ìZWÝSð×7—sržqU–i¼E1«ƒÁŸ§ƒ¯=NnÃ#Wg•hÊ3?ãŒpp׌UñïìQ´ÕàÌŠþKû²~þuq>Ʀ}Ù? mó>­´n—]Ê5lýaHÓW~®'ØÔÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâýLû²~uq~Ǧ}Ù? 9ŸVÚ7IÊ5lýaHÓW~®/ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq~Ǧ}Ù? 9ŸVÞ7J¼£VÏÖ5wêâýLû²~uq~Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãt©Ê5lýaHÓW~®/ØôÏ»'á§WìjgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®/ØôÏ»'á§WìzgÝ“ðÓ™õmãt«ÊlýaHÓW~®/ØÔÏ»'á§WìjgÝ“ðÓ™õmãt©Ê5lýaHÓW~®'ØÔÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ʀ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®/ØôÏ»'á§WìzgÝ“ðÓ™õmãt«ÊlýaHÓW~®/ØôÏ»'á§WìzgÝ“ðÓ™õmãt©Ê5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâýLû²~uq~Ǧ}Ù? 9ŸVÞ7J¼¡VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7Jœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâýLû²~uq~Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ5wêâ}Lû²~uq>Ǧ}Ù? 9ŸVÞ7IÊ5lýaHÓW~®'ØôÏ»'á§WìzgÝ“ðÓ™õmãtœ£VÏÖ¯À½IGá/´Vµ¥*–I&Àí}¥»ö!ÜmÇ\ˆusX}LvˆZš=–Ù6±IIPýW÷‚Aø>_“E©5S£0Å6s7â“ Content-Length: 0 ]]> Content-Type: application/sdp Content-Length: [len] v=0 o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip] s=- c=IN IP[media_ip_type] [media_ip] t=0 0 m=audio [media_port] RTP/AVP 0 a=rtpmap:0 PCMU/8000 ]]> Content-Length: 0 ]]> sipp-3.6.1/docs/statistics.rst0000664000175000017500000001160013730472040015726 0ustar walterwalterStatistics ========== Response times `````````````` Response times can be gathered and reported. Response time names can be arbitrary strings, but for backwards compatibility the value "true" is treated as if it were named "1". Each response time can be used to compute time between two SIPp commands (send, recv or nop). You can start a timer by using the start_rtd attribute and stop it using the rtd attribute. You can view the value of those timers in the SIPp interface by pressing 3, 6, 7, 8 or 9. You can also save the values in a CSV file using the -trace_stat option (see below). If the -trace_rtt option is set, the response times are also dumped in the >scenario file name<_>pid<_rtt.csv. Each line represents a RTD measure (triggered by a message reception with a rtd="n" attribute). The dump frequency is tuned by the -rtt_freq parameter. Available counters `````````````````` The -trace_stat option dumps all statistics in the scenario_name_pid.csv file. The dump starts with one header line with all counters. All following lines are 'snapshots' of statistics counter given the statistics report frequency (-fd option). When SIPp exits, the last values of the statistics are also dumped in this file. This file can be easily imported in any spreadsheet application, like Excel. In counter names, (P) means 'Periodic' - since last statistic row and (C) means 'Cumulated' - since sipp was started. Available statistics are: + StartTime: Date and time when the test has started. + LastResetTime: Date and time when periodic counters where last reseted. + CurrentTime: Date and time of the statistic row. + ElapsedTime: Elapsed time. + CallRate: Call rate (calls per seconds). + IncomingCall: Number of incoming calls. + OutgoingCall: Number of outgoing calls. + TotalCallCreated: Number of calls created. + CurrentCall: Number of calls currently ongoing. + SuccessfulCall: Number of successful calls. + FailedCall: Number of failed calls (all reasons). + FailedCannotSendMessage: Number of failed calls because Sipp cannot send the message (transport issue). + FailedMaxUDPRetrans: Number of failed calls because the maximum number of UDP retransmission attempts has been reached. + FailedUnexpectedMessage: Number of failed calls because the SIP message received is not expected in the scenario. + FailedCallRejected: Number of failed calls because of Sipp internal error. (a scenario sync command is not recognized or a scenario action failed or a scenario variable assignment failed). + FailedCmdNotSent: Number of failed calls because of inter-Sipp communication error (a scenario sync command failed to be sent). + FailedRegexpDoesntMatch: Number of failed calls because of regexp that doesn't match (there might be several regexp that don't match during the call but the counter is increased only by one). + FailedRegexpShouldntMatch: Number of failed calls because of regexp that shouldn't match (there might be several regexp that shouldn't match during the call but the counter is increased only by one). + FailedRegexpHdrNotFound: Number of failed calls because of regexp with hdr option but no matching header found. + FailedOutboundCongestion: Number of failed outgoing calls because of TCP congestion. + FailedTimeoutOnRecv: Number of failed calls because of a recv timeout statement. + FailedTimeoutOnSend: Number of failed calls because of a send timeout statement. + OutOfCallMsgs: Number of SIP messages received that cannot be associated with an existing call. + Retransmissions: Number of SIP messages being retransmitted. + AutoAnswered: Number of unexpected specific messages received for new Call-ID. The message has been automatically answered by a 200 OK Currently, implemented for 'PING' message only. The counters defined in the scenario are also dumped in the stat file. Counters that have a numeric name are identified by the GenericCounter columns. In addition, two other statistics are gathered: + ResponseTime (see previous section) + CallLength: this is the time of the duration of an entire call. Both ResponseTime and CallLength statistics can be tuned using ResponseTimeRepartition and CallLengthRepartition commands in the scenario. The standard deviation (STDev) is also available in the log stat file for these two statistics. Detailed Message Counts ``````````````````````` The SIPp screens provide detailed information about the number of messages sent or recieved, retransmissions, messages lost, and the number of unexpected messages for each scenario element. Although these screens can be parsed, it is much simpler to parse a CSV file. To produce a CSV file that contains the per-message information contained in the main display screen pass the -trace_counts option. Each column of the file represents a message and a particular count of interest (e.g., "1_INVITE_Sent" or "2_100_Unexp"). Each row corresponds to those statistics at a given statistics reporting interval. sipp-3.6.1/docs/scenarios/0000775000175000017500000000000013730472040014772 5ustar walterwaltersipp-3.6.1/docs/scenarios/branching_01.gif0000664000175000017500000002506613730472040017725 0ustar walterwalterGIF89ab¨p,b¨‡   $   $$$$$$$( ( $($0( ,,,,,0(0(<0,0,@44440804844880<00XÍÜ´#µìSU©otÁ—7è ôcSi6Pgý¦ØÐyãU€ÞV}Š$~¤'^0âçTÞ¡@;ñ·È…eç˜~Þ`#ÕB•ŠÁŒÕši†1gÖŽc9“‡gUœs‘ˆVAH:háPz·dqfh¥†s9“sop8¢†5‚-–iQ>ðÔ\î-r iþ†¡YZÊ©ÃM¿M)"Š.Is)ŠHáfÎèY啈v„¤šÁ(bŸa äߘ$$YZjšõ ±%I—œõ%õ•€cÝy‹nz¶f¢bJ²¨ê݉ÿH'Oqbè¤%ªkE]Å4[£IܲH‡ÎÊIv”D]*wu,¬{òáÜ’¨ÌÅ$”úF*;¥¢\jý¡e“@âd«Q9í”V€M¥ë ¹à¹ë½½†µˆ¸çz“J\1eTÀ¥²­N]àé.Š»H.‹øVlñÅg,Ú}ã¨ñÇv|¹ —lòÉ(§¬òÊ,·ìòË0Ç,óÌ4×lóÍ8ç¬óÎ<÷ìóÏ@-ôÐDmôÑH'­ôÒL7íôÓPG-õÔTWmõÕXg­õÖ ar×`›|_‡m6ÆgxDÙg·½kÚÒ¬íö܈Â7Ûtç]šÝwëÿí÷a|÷ý÷à?.8ሳdøá‰7nÒâŒ;.9HG>ùåUn9æœS¤ùæW÷òƤŸqÈ4“ô9èRkƒ‡X¡ÅìZüP@Ê¥þÑê¬?ýO¼ÍðÄH(ô¢»G¼÷ÎtHìRüôÄk²&ËsÔ¼óHkóÔ‡?¼02à­&¥§¯þúì·ïþûðÇ/ÿüô×ï~ ^H£ÿþü÷/wÔ>„ø `%ð‚ÈÀ:ðŒ 'HÁ Zð‚|à*úÇAÿ™oio°¨ (o%è  WȺð…0Œ¡ gHÃþiÎ`€2F8@H¼5 ¢ÿ‡HÄ"Q…7TÚÚ@@Yƒ€Dák8 Tð‚†¨à_Q1 þ¡â‹_ì¢þxñE-ú#¥¡F.²ñ‹Wœâ “x4m`‡â3†Ä@@@ø@ŠrŒaÙ)̰üë@Ù+.’CÐß™ETt@³xd! (ò‘™dd$9HÒ†$š#x0@>x|$a´¡’žÒ…rˆ¤þ F"r(Ãþ¤`Èd‘RÐ¥4qIKv’“lü¥4ÊpÉ]sÆæ&n¹B:­ àŸ¢°ÇF1%¶äæ ™G1ÒÀ“œ,ƒ<I^tÀ“Ò”F0‡9ÿÊkî2Žúë¢3‡ Ì^R›þ”F6õ×hª“ƒÞ 41Âr ö:I:ÚÁ24 CØæ„©ÐY”!’É@ääÍD’²‘ e¤2yÆ2:SCX¤zÐ]Ê4¦„4%G;Q aàåµp†Z•…&mÀ&Ù9”¤¦ìÀ1«ÉP) B¼Ü¥ À˜ ^ürƒÀ¹ÊƜ ë“WT A¨O…h*&áТKm*:óÚ¿¾•šrEE2®ª¿¬n5‘$•C5ªMþÓ™xÕç1áJYkJÒ„åK´‰&u„LÀƒSC«?fn­›8©4’Ñ“ÿbuš‘DE>÷ÉPanRÒhÄ&¥Á Vñ’½b#¸ÊÙÄfq¡fÝ&kG{4#èÁœq!±ZÖ.S‘h„þ¤pOñÊ¥8/ Ê¿•îo4H†'ƒª¿MÌwWœ….©Ùô2—û›o);ÙKA(3¯Ô=Ú!~ÀÃ*¨¬w'LaŽ&øhÓ@>²Â q/Œ´ ‚Ãâ;§„EÌâÃÄH#F6Œâá@t±ŽwÜ?'í d¨qñB»óøÈ,öqÒ¼GQ!?AµÌ ”§Lå*[ùÊ ,† •¬´i` Éæ‚\‚û™ùÌhN³š×L:üÅÿËK›Æ ˜ÈÃ]È€´ÙÃÈöàÌ4m\×_-žÀ€æ9#Íã³Óˆa„ü` šˆ4$´`à–‡Î\þZ¨h¨MãW@¨_peÑ›^a§OºÏ­šÕÓÜ«a͹ÊÍšÖ˜ƒÜ­q}¹Åíš×“3ܯ-¹À ›ØŽãÛ±‘Ý8»-›Ù‰ƒÛ³¡¸´M›Ú„;ƒðŒmV‚ÛÝ·¸ÇMnŽ`ºÜ‡Ö ¸‹îìiÃØk»%÷îKuÞ«7÷ðí7}ï›ßtó÷¿Þ6œàa3øÁ¾5…/œáXsøÃ!^5‰Oœâ­³Á%º)oŒkÍâú»·ÇKs‹P3ÿàÀ þ°æ€<äùV0 @ºÃ$€#Ãåw¡Èe~”ip@H |C'zOnP„9çvzÐçs©»¤À9Šƒ¬ûäéü;ÔÃö¶»ýíp»ÜçN÷ºÛýîx»æe±×˜MÐúÆcXŒMþðˆO¼âÏøÆ;þñ¼ä'OyÅbH  ‰'„®›íH=Gsy£Ý›'Þ.`ö•€^ô°¿%éöH PqŸü’×q±WÝDBa˜ ôÂФ×4i/7!€òOep¨èª?9ˆ—³/10@8ÀàÃÿAÁ—øÞˆº½jÞICýÆš'5är©)ÞdRÓùh©²©UI²Õ»ÙG4!$>¥àŒ àÄ?çz[GXºeYq„FiÔK8 É@}H\ndYÔ‡HµE\—ôKÆ?4`|Ë¥Y8u`€C£y%ôžççwD˜Fˆ„V'I<8†THóÄF ¥KC Ì´~¥E#ˆPÕtSÄ•O%¥Uìb.(Q`&>?YH=©¥7ˆƒðQ‹$^4MZu†úS& RPÎôK#ØE)ÕAá¥PN˜F\… eI÷ô-XzC“ 0àwá³…âG@L¥6p}Þÿ•ƒwÅPdØÔtME†40PH8J“èO–MÒdLOÈUfÕ?¢IUXaWø3Ã-t‡Ù5B‹˜¡@Í0a9ØIÂÔÒ•Eý' VZ¼@F—_þ…‡iäE¾LqÔHÕ´ v‡õuIý—RŽˆBà žÐ×P{â# (÷}28@4¸·˜‹¬µ‹™4 Ë… ÂÅ ï(YöÔ–dÕ4Hã5_ÅV0RŒuRv%L'hIÒ¥ƒp( ^Á¸ à|¬µŠ;ó }` N⣠b°‘b0‹â³¡ÀéÈZ‹Å? ^ð†qÔŒ…Iò´MÅW_)R`|³¥~-ÿi`Æ—KÙ8FëWq4}ûc}ª(ˆ=Ó up  +zÅ# Ðz'1’±W•i”:“ ` Ü@Ú€Âà”ãcæTi•h9zX‰3¾¾pxðb `j舋i™—Ü$‘4à ™×€ÚÀÒ“zˆ?q–z¹˜EÄ—2ó u  å(dµ°-÷ŠÉ˜œICŽ 3¦PÏÐXc°v©™x C„w®ùš°›²9›Ò¡p3Ü žÐ•qH@ct™!¹›ÙMa .È™œÊ¹œÌÙœÎùœÐÒ9ÔYÌÙ€4£•¸ ]H=ÊÐÿpBdQœDUƒ^gÒÃ@±€+À ¯  p†æéAé)Ë£i¡à!`? g™ú¹šן á Þ¨Ú .op©@ž²Ÿ0'¡A † :ã¡Q¢! À›% ¡ü‰¢ñ¢ð3æy¢0ªÜ´4Åi£7z×o)4›é£?JÙËP4Ši¤Gê 9ÚžFs–Nz¤Š°HC•Uz£ž`‘J3’[Š¢­` M“Ža*¡¾Os‹;€ž7úŸ,Z¦à¦(ºž5¡À¡OjÙÐKº§\à †0¤€º5]Z5·€…*ÿ§Ps d³¨„5Otêuº`TóÅp¦Ç lp§–:Zª— ’)5žú¡‹ §¬jªP—©2×Rú¨²:«‹Š Š«ZÆ?¨ŠpÙ¤ê4­ú¢{:¦¥ú«Èú¤¶Š«ÌÚ¬?z i­š¬dêÓ€œ±(ÇŠD´Jq´ /A V º‡ •dñ­à:oš =âôÊÁh °†hµˆ€ìÂVø¥O;¼É¿(våUÖXŒ#(|»xF!hÉ£ÈËý£UŠäQ €žœq! sçËCÞ§ˆM7°AºH‚ @WŒÔ’e‰¤Ä—ĆVTƒÔKË´P¾¬‡–eŠhÅR1üÆ+./oðh;l›;÷<W!Ç3Ïy!/ý‘Q aZá# ˆ€ 8@VÀ±"A # Õ¼?ìõ¿Õ'_×w‚ exÎdO0uÍkø|@}úÔEö¤Œ‚Zµ¿ù«‚ú´¿îœ:4’ÿB%X‘ IÁ›á­!² ß—kÃRÇ qÇâã}߇‡èÇáнŽÒ„ §'ÕÈ?¼@Ò·?dtM¨pb´ÕäüFkD\‚ÐK³ ÅÌF;ü^ZôÙ¡EÄ!!MÊgñ¡¬×lrQ·jBI /ÃB½sIŸà½¼È½B®ý·q¼q-Ρf¡Û¡-Ò#á¹b™Ê,qÜÉÿýÝû³ÜQÔÓíqÛÓM»=ðÑ)Ð2”)– ÎýØ Þø-Þaõý'öÂÀA›ËÛÙ1%.Ú½y¼çÞß˫ߡ%"ÐÏ 1Ê¢³ÁL,¶+ K"#? Ä`"[c’ðCGñà.Ô •ã2>ã4^ã6nx—÷ÎÚ2Ï ±ãoÀ¯‹©káÏÑÊÐàB©)>²&Ÿ?áâ,äkG›V~åXžås§J“ aÇat€PÞâ÷Íu’º½ O°ØÔ£ !Ð}[®e޲gž®Ã< ŸÅS k`(PÛ[!åz¨“ ½ç>ÿM{ŸFèsN°Oª ™ð4‚®«{*ªÅº4•~qw¨”þèÀêÔ w lਚê® ¨°£P#åÁŠpËPRã⯎p”:ë^ë·©SãݺNp†  ½^æ¿p×P™¾h÷]ì7 }@5PÍì' 3:ì ê¡ÚJÙë €í' u@¢Sã q~¤´ðu®5«žîXà p@®ìn5ÇЦïPã †Pïöî4ø®ïûÎ4¼úïTsî?5_ðQsðÿ4 ¿ðMCíÿ4ñÏ•Ï4­P¡¯4Ç”!:és:63"Pò&ò(Ÿò*¿ò,ßò./50æ~ÿs }Ð á:°#;´c;¸C3"0{ôB?ôD_ôFôHŸôJ?€®7Ü` ÏÀãWÔs<É#3"°°°õ\ßõ^ÿõ`öb?ödŸMï7Ÿì =l>Öch.“õd?÷t_÷v f?9¿íáïÞN F>ÞŽ(r÷†øuŸ÷“Có6¥uïƒ5¦b,Søˆù˜ßõŠ?9OÏ•–Pp`ê¥)d%äØ(cù™Ÿú‡¿ù—ƒ”UðúèN9”· æa-ƒúªŸûtÏú““ cðú¯Ïê±D#T P÷)ƒûcO ¤0ö‘@öÏŸû¼/9ü¯¿jG´?~tûZ_÷K°c…pþéÏõ Ðþ `øÓß8º`ýÀ$:´·‡rß>Ê0K•?þKà@‚—,)XðB¡„ìt¡áÄ‚RyØQãFŽ=~RäH’%MžäøLU *-[òÖ„4š5mzø ”MžÐd`BTèÐ"öPLx ™ '˜u¡éSX…š>|(pÏŒ,YN´HTìX²eÍ–ä¦Ë›–ªPhêYó‡ŒR:ãÖDòçì^¾F¿T:pF§ª/x-” êÑNгÂZraI‰ †å›YófÎUúpê.#Œøàs—¦–3]›ôk2ÿB‚Y(&5R©DzLðTÙ°0¿Fž\ùH ¯îŠ€ÕÐX/Ç®1¶åÀ%²ªÛ{cðà£2‰”@®Å_Ì_¾ë·w•*u:µj&xæ/ßî¯ÉÊ#%õ(‹*Xæh¸ªC–fx07ÙŽû/C ‰2Bê ‘ÅYªãÁ‘ _ ð«,z;l‰†;AÂ,^ŒD šªq†k* á;1H!C:ä‡Ti†Ü,E÷ž„) ™¤rÈiPæÈê y¡ÊÌœŒ2L1§ô²Ì s@DKÕ~2ó,0Å„Ó=2Û¤s9b"ÈRMžNÁ Î²ÞŒ3Яæô³ÐÎÿ¾ COžBÒP¡4Ò„u´Ò³´y.E¡yÂ?K=JRQ¥lôSSÍšMÕäˆSƒ‚tTI)}µÖ“¦y¡ -w‘¡5[a UVa¢õWcCÒæ <¼«–'êX’b6Îb¥Å–#bŒ(à‡54-,`m²I„)ö`·]wß…7^yç¥·Þ JE7_¦9ä þ}áŒ[ôÉF8a…f¸a‡†Xˆ!¸b‹/Æ8c7æ¸c?9d‘G&¹d“OF9e•Wf¹e—_†9f™g¦¹f›oÆ9gwæ¹gŸ:h¡‡&ºh£Ñ=÷h¥¥}ƒˆ¥Ÿ¶õ ÿœ†ºjKÏðª­æºN¬¥Ñºk±Íüì­ÇF;ȲÍN»í ×fÛm¹ãƒ;î¹ïF®n»ñæ[3½÷î;ð²þ\pÃ…"¼ðÃ')qžÅ_¢ãà…?œaÜo/¤á¼sÏç ,ø¶š@ Œ 8ó³ç{í·ç¾{ï¿?|ñÇ'_ûÊø<}ÏeÏ9Õ祣 ùؤ~ûïÇ?ý÷ç¿ÿÿ`8@ý5C}]Ì|ˆMÕD‡ˆ8A VЂÄ`5¸Aÿª}5»…X“]0`zÉ‘`U¸B¶Ð…/üàÌ^ Õà§#ºK§²“¾r(ƒ 6ˆ¾ÎÕOrØD7ш$vމrHÆ&Ê0Å)"‘ŠSLFUC˜ƒªNt< Pp`‡Zt!/ …2 ¡l€ç¦(4 ‹Ò˜¢ hà¹ð¢¨bÖX 6àŠwDã¹è²7XA5?ð~px´N9v´—ýìÙKûÛ±÷µ«½íl‡»Ûë.w P@f±‘ÕôŠ È/Ϥõ=×€O ‚ó¿‚­2ø¤ûy-Õú?Ï‘¦œ’?¢%pÀlª©ý3¥M˜…2H­Yh(Š"ÿó=Ö„õó§_è˜q«¸¨‹(ð€ûz${{ üË¿ëÓ«7º&T¸&i¨ÁœR#&ò¤)ª%™Ú„¼ÁÏ™£ð%p:Âôi¬i¢(i–:?9ø+ ü#)Sò(ƃ €HZÁ§Úä8„Üó'TÚ€ÿ!˜¥Ø„óèιÀ!¥!`#VâtC8$Bô1Bi¿?üC t¨Šú¤8ú£?¤COZ',ô'-|‘º‹º˜«{Cì€?P)ýS-;R¬á‹(9j€áË)ô!$ úDÚC¾BD$D¡jÄB<")NJD^*N,«Gl¼—™§¸¨'hø“º +x츅 ÒÅ!x%OÄ#ŒÊ"^('Nʦ@j@ÚkÃ;J,›J(Õâ¤nªE%Ô¥Äj¥OºEXBX¨ŒÊÂ^t4QÓpÔ +ìŒd\ÆwÒE†j—¢&Õš¦–¢#6êAï«)€tÆôÑ©kÚ¥YrÈ>"GY(ÿ‹ %\„¨Úã&hzG˜9-¨YàƒIº‹83dTÆ3ô^ø$ D¾RB>@¥;JDrIõ©I9|&äHÏÁœü½Ü#+²"¥d@…d€Åw‚D—é…ø.hx ˜dÒ‹¬dȾ j£ƒšÀŽ<ª¤!š^°«Œ,¬ ÿ3C•ʼ˜±/é*¡ˆ[Žm0˺ì%åÛÂ.l ±Ôm 8»LÂË—!† H=èy¯!†”ä _ÐäsÌüÌÇìœÈÄŸÉ”ÌʤLαÌûÁÌËÔÌÌäÌÍ”†Î´ŸÏ¼d˜™XØ€”SP0ÃÓ}Ä F°nà†OØž7ÀÍÜÜÍÿÞÔMíáMÜ ÎìNßNàüMâ\Nä4NåLNæŒNçÄžã,NìÑ‹™IçQ:@ÙÜÚ´ ’+¹ é x‚Ãì M&Ë Ï‚ÃÇòümÀà!³‰ZX @|’ø¬7ú¬mpé$5*¹….¹ù$М¡ËŠ“Ð Å™ = òÄPžé‚}LíPP'jÐ%QÆôœmCÑ )QmQÈQQi€Ñý™d¤¸F8QÍbÐPåQ©P Òœ±†Q Ò"½™^8€¸P%µ™[0€'…Òš!ð¬Ò,ÕÒ-=œ7ØQ.•™"h50Õ™"P Rÿ2U3m†$UÓ5UrÓ7=6åœ9¥Ó’!ºÓ4ÍS‘©†Ï9·?mm˜†@¥6Bemp'7EMFÓG…T“‘T>MŸg«Ô’¹ÔÝT³H…38ƒ0‚3¸?éTOýT¢˜†/@€Ð-€=ÐÕAh1“C˜T ]U¢¸O2PÏšÐÀy+D•Ï^ mð'pMÕ8…0‘*AÖdUV“Ð` 5QÃ9D9°¤CÕsÕrE×sU×u=ƒv]WxEWye׌0Ww¥×tµWw}×}ÅWW€U\…™`Ú”;½ Uðd‹/€ØˆmØÿÎñ‰X‡‹…XŒ­XåX؇õXŠÙY‘µX“MY’=ÙŒX'€Ç—°R 8É QØÀÔÙ "Á˜q<=á’ ÉÙ5Z Ì–¹]‘.6ù¢m!£ %ZEÊÕìIPšZ9´  T®¤]m(€ …†Rp€³=[Õ˜m€Ú$Úã&ßË pD ²Ûs">½í€âÄ4l¥5¨»UúZ˜‰:û1(©¹²±xOרÓDÊJÁJJ4’&vjÂ?jB€¬~,,ËkËÊš º£êpÚì¨V-ªÜ¾Hà%—¢= ?Ø%»õ\C”]XäAÿQò£šä?2\Ðå»E…!€]^r#Ú‹©Vâ¦OZÞ¯Rʘe™_Œ‹]pú뉼ÀC]]ÖEÚk€FP£,Ò£òÅ£¾J"Uú£ô#ôÕ£ôQ')¨j"¢á\Ú³E>ü£žª¨˜b'6ò_:êÆLZÊ–Aªêpªªä¸G]¦]B…¿"ë¾\ªðí&QŠ#)? ÝŸj„šB'áM,ë»HkLG<:Þ&º¢8Ä6‚áN¤f™æ¨ècào} ‚wÊJëC¥D ¥AŒ¨#¥[¢Å7 Ä#–_¬"¢ž +X2C] 'ƒúÃ\áß`PÚ¤Üc#^„ëå ÿìÕޞاåÐâ^ª\±ú)9Ъr¢*ý»%5ª©[¢ã(¶Ü,B§ßEJŽÔÅ¿BÊ‹TG–B%CŠ%0Vdë³a4Âá•éÕ’¼ „í^7î%9ð[Ï]Ǿ¥ÙµFØ+'$«'eQNŸãk'éí+‚”îd£ã›Ÿ’ƒO*a;Ô+sª©Y0§]–•dê]™"yÊ$ÁÒ×hã7>Úwb-C< h¦£ª½acV™+![¡íùhã{æDšÝÚ› q¶ÆwêY˜‘Ǧ%Øøèjçx® °]™;Ùæ#á“ ygyægõ¡ç•AᲿ !,èçƒæœ^™„½ô” iæÿbXM„Ιh‹­hιh‰ÍhiØèˆíè†ØY’¦h‹.i”>iNi–^iÙJ˜™i04Ñ“'ðá ‰è ÒÔ™ãW\½…ŸÞ9¡:¢öiwj£j¤j¦.j§>êuMj¨^êuõS’™† ƒ{®‰Z‡’ŽÐk Š/`€6xV›x…‡Ðk^%k”p†+(Ð=2°,»êøxë íë¸ÎL8#ø—/xƒ›5mx†kÁÞ˜}VUÈÖ˜n†É¦ìŒ!gþ<Íìcùëh½Ï®˜Ð®QÒÆ˜¿>mÔ¾˜·^mÖ¶m°ŠëiØŽlÇölÛ>–}ÎmÝ6–‚îmß¶•6€ÀîZцhçF ;sipp-3.6.1/docs/scenarios/inject_from_csv.rst0000664000175000017500000001225013730472040020676 0ustar walterwalterInjecting values from an external CSV during calls `````````````````````````````````````````````````` You can use "-inf file_name" as a command line parameter to input values into the scenarios. The first line of the file should say whether the data is to be read in sequence (SEQUENTIAL), random order (RANDOM), or in a user based manner (USER). Each line corresponds to one call and has one or more ';' delimited data fields and they can be referred as [field0], [field1], ... in the xml scenario file. Example:: SEQUENTIAL #This line will be ignored Sarah;sipphone32 Bob;sipphone12 #This line too Fred;sipphone94 Will be read in sequence (first call will use first line, second call second line). At any place where the keyword "[field0]" appears in the scenario file, it will be replaced by either "Sarah", "Bob" or "Fred" depending on the call. At any place where the keyword "[field1]" appears in the scenario file, it will be replaced by either "sipphone32" or "sipphone12" or "sipphone94" depending on the call. At the end of the file, SIPp will re-start from the beginning. The file is not limited in size. You can override the default line selection strategy with the optional line argument. For example:: [field0 line=1] Selects the second line in the file (the first line is line zero. The line parameters support keywords in the argument, so in conjunction with a lookup action it is possible to select values based on a key. The CSV file can contain comment lines. A comment line is a line that starts with a "#". As a picture says more than 1000 words, here is one: .. image:: sipp-02.gif Think of the possibilities of this feature. They are huge. It is possible to use more than one injection file, and is necessary when you want to select different types of data in different ways. For example, when running a user-based benchmark, you may have a caller.csv with "USER" as the first line and a callee.csv with "RANDOM" as the first line. To specify which CSV file is used, add the file= parameter to the keyword. For example:: INVITE sip:[field0 file="callee.csv"] SIP/2.0 From: sipp user <[field0 file="caller.csv"]>;tag=[pid]SIPpTag00[call_number] To: sut user <[field0 file="callee.csv"]> ... Will select the destination user from callee.csv and the sending user from caller.csv. If no file parameter is specified, then the first input file on the command line is used by default. PRINTF Injection files ++++++++++++++++++++++ An extension of the standard injection file is a "PRINTF" injection file. Often, an input file will has a repetitive nature such as:: USERS user000;password000 user001;password001 ... user999;password999 SIPp must maintain this structure in memory, which can reduce performance for very large injection files. To eliminate this problem, SIPp can automatically generate such a structured file based on one or more template lines. For example:: USERS,PRINTF=999 user%03d;password%03d Has the same logical meaning as the original example, yet SIPp only needs to store one entry in memory. Each time a line is used; SIPp will replace %d with the requested line number (starting from zero). Standard printf format decimal specifiers can be used. When more than one template line is available, SIPp cycles through them. This example:: USERS,PRINTF=4 user%03d;password%03d;Foo user%03d;password%03d;Bar Is equivalent to the following injection file:: USERS user000;password000;Foo user001;password001;Bar user002;password002;Foo user003;password003;Bar The following parameters are used to control the behavior of printf injection files: Printf Injection File Parameters ```````````````````````````````` Parameter Description Example PRINTF How many virtual lines exist in this file. PRINTF=10, creates 10 virtual lines PRINTFMULTIPLE Multiple the virtual line number by this value before generating the substitutions used. PRINTF=10,PRINTFMULTIPLE=2 creates 10 virtual lines numbered 0,2,4,...,18. PRINTFOFFSET Add this value to the virtual line number before generating the substitutions used (applied after PRINTFMULTIPLE). PRINTF=10,PRINTFOFFSET=100 creates 10 virtual lines numbered 100-109. PRINTF=10,PRINTFMULTIPLE=2,PRINTFOFFSET=10 creates 10 users numbered 10,12,14,...28. Indexing Injection files ++++++++++++++++++++++++ The -infindex option allows you to generate an index of an injection file. The arguments to -infindex are the injection file to index and the field number that should be indexed. For example if you have an injection file that contains user names and passwords (as the following):: USERS alice,pass_A bob,pass_B carol,pass_C You may want to extract the password for a given user in the file. To do this efficiently, SIPp must build an index for the first field (0). Thus you would pass the argument ``-infindex users.csv 0`` (assuming the file basename is ``users.csv``). SIPp will create an index that contains the logical entries ``{"alice" => 0, "bob" => 1, "carol" => 2}``. To extract a particular password, you can use the lookup action to store the line number into a variable (say ``$line``) and then use the keyword ``[field1 line="[$line]"]``. sipp-3.6.1/docs/scenarios/cond_branching.rst0000664000175000017500000000502713730472040020466 0ustar walterwalter.. _cond-branching: Conditional branching ````````````````````` Conditional branching in scenarios ++++++++++++++++++++++++++++++++++ It is possible to execute a scenario in a non-linear way. You can jump from one part of the scenario to another for example when a message is received or if a call variable is set. You define a label (in the xml) as