From 887750106a0999aca4638c31d01ffa89eb3d4a74 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 27 Feb 2006 00:00:30 +0000 Subject: Moved pjsua framework to pjsua-lib git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@238 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/src/pjsua/getopt.c | 751 ----------------------------- pjsip/src/pjsua/getopt.h | 136 ------ pjsip/src/pjsua/main.c | 2 +- pjsip/src/pjsua/pjsua.h | 468 ------------------ pjsip/src/pjsua/pjsua_call.c | 1090 ------------------------------------------ pjsip/src/pjsua/pjsua_core.c | 910 ----------------------------------- pjsip/src/pjsua/pjsua_opt.c | 860 --------------------------------- pjsip/src/pjsua/pjsua_pres.c | 504 ------------------- pjsip/src/pjsua/pjsua_reg.c | 167 ------- 9 files changed, 1 insertion(+), 4887 deletions(-) delete mode 100644 pjsip/src/pjsua/getopt.c delete mode 100644 pjsip/src/pjsua/getopt.h delete mode 100644 pjsip/src/pjsua/pjsua.h delete mode 100644 pjsip/src/pjsua/pjsua_call.c delete mode 100644 pjsip/src/pjsua/pjsua_core.c delete mode 100644 pjsip/src/pjsua/pjsua_opt.c delete mode 100644 pjsip/src/pjsua/pjsua_pres.c delete mode 100644 pjsip/src/pjsua/pjsua_reg.c (limited to 'pjsip/src/pjsua') diff --git a/pjsip/src/pjsua/getopt.c b/pjsip/src/pjsua/getopt.c deleted file mode 100644 index 30f45381..00000000 --- a/pjsip/src/pjsua/getopt.c +++ /dev/null @@ -1,751 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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 - */ - -/* - * getopt entry points - * - * modified by Mike Borella - * - * $Id$ - */ - -#include "getopt.h" -#include - -/* Internal only. Users should not call this directly. */ -static -int _getopt_internal (int argc, char *const *argv, - const char *shortopts, - const struct option *longopts, int *longind, - int long_only); - -/* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -# define GETOPT_INTERFACE_VERSION 2 - - -int -getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 0); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt (int argc, char * const * argv, const char * optstring) -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - - -#define _(msgid) (msgid) - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = NULL; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -static char * -my_index (const char *str, int chr) -{ - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -# define SWAP_FLAGS(ch1, ch2) - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (char **argv) -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -static const char *_getopt_initialize (int argc, char *const *argv, - const char *optstring) -{ - PJ_UNUSED_ARG(argc); - PJ_UNUSED_ARG(argv); - - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - //posixly_correct = getenv ("POSIXLY_CORRECT"); - posixly_correct = NULL; - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -static int -_getopt_internal (int argc, char *const *argv, const char *optstring, - const struct option *longopts, int *longind, - int long_only) -{ - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) - { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring); - __getopt_initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !pj_ansi_strcmp(argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) - { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - nextchar += strlen (nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - nextchar += strlen (nextchar); - - optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - nextchar += strlen (nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') - { - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - nextchar += strlen (nextchar); - optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - diff --git a/pjsip/src/pjsua/getopt.h b/pjsip/src/pjsua/getopt.h deleted file mode 100644 index ea19c718..00000000 --- a/pjsip/src/pjsua/getopt.h +++ /dev/null @@ -1,136 +0,0 @@ -/* $Id$ */ -/* This file has now become GPL. */ -/* Declarations for getopt. - Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GETOPT_H -#define _GETOPT_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `--', then non-option arguments are treated as - arguments to the option '\0'. This behavior is specific to the GNU - `getopt'. */ - -int getopt (int argc, char *const *argv, const char *shortopts); - -int getopt_long (int argc, char *const *argv, const char *options, - const struct option *longopts, int *longind); -int getopt_long_only (int argc, char *const *argv, - const char *shortopts, - const struct option *longopts, int *longind); - - -#ifdef __cplusplus -} -#endif - -#endif /* getopt.h */ - diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index 25ce6dcf..ce317078 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "pjsua.h" +#include #include diff --git a/pjsip/src/pjsua/pjsua.h b/pjsip/src/pjsua/pjsua.h deleted file mode 100644 index 09874160..00000000 --- a/pjsip/src/pjsua/pjsua.h +++ /dev/null @@ -1,468 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef __PJSUA_H__ -#define __PJSUA_H__ - -/* Include all PJSIP core headers. */ -#include - -/* Include all PJMEDIA headers. */ -#include - -/* Include all PJMEDIA-CODEC headers. */ -#include - -/* Include all PJSIP-UA headers */ -#include - -/* Include all PJSIP-SIMPLE headers */ -#include - -/* Include all PJLIB-UTIL headers. */ -#include - -/* Include all PJLIB headers. */ -#include - - -PJ_BEGIN_DECL - - -/** - * Max buddies in buddy list. - */ -#ifndef PJSUA_MAX_BUDDIES -# define PJSUA_MAX_BUDDIES 32 -#endif - - -/** - * Max simultaneous calls. - */ -#ifndef PJSUA_MAX_CALLS -# define PJSUA_MAX_CALLS 256 -#endif - - -/** - * Aditional ports to be allocated in the conference ports for non-call - * streams. - */ -#define PJSUA_CONF_MORE_PORTS 2 - - -/** - * Maximum accounts. - */ -#ifndef PJSUA_MAX_ACC -# define PJSUA_MAX_ACC 8 -#endif - - -/** - * Maximum credentials. - */ -#ifndef PJSUA_MAX_CRED -# define PJSUA_MAX_CRED PJSUA_MAX_ACC -#endif - - -/** - * Structure to be attached to invite dialog. - * Given a dialog "dlg", application can retrieve this structure - * by accessing dlg->mod_data[pjsua.mod.id]. - */ -struct pjsua_call -{ - unsigned index; /**< Index in pjsua array. */ - pjsip_inv_session *inv; /**< The invite session. */ - int acc_index; /**< Account index being used. */ - pjmedia_session *session; /**< The media session. */ - unsigned conf_slot; /**< Slot # in conference bridge. */ - pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this - call was triggered by xfer. */ - pjmedia_sock_info skinfo; /**< Preallocated media sockets. */ - - void *app_data; /**< Application data. */ -}; - -typedef struct pjsua_call pjsua_call; - - -/** - * Buddy data. - */ -struct pjsua_buddy -{ - pj_str_t uri; /**< Buddy URI */ - int acc_index; /**< Which account to use. */ - pj_bool_t monitor; /**< Should we monitor? */ - pjsip_evsub *sub; /**< Buddy presence subscription */ - pjsip_pres_status status; /**< Buddy presence status. */ -}; - -typedef struct pjsua_buddy pjsua_buddy; - - -/** - * Server presence subscription list head. - */ -struct pjsua_srv_pres -{ - PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); - pjsip_evsub *sub; - char *remote; -}; - -typedef struct pjsua_srv_pres pjsua_srv_pres; - - -/** - * Account - */ -struct pjsua_acc -{ - int index; /**< Index in accounts array. */ - pj_str_t local_uri; /**< Uri in From: header. */ - pj_str_t user_part; /**< User part of local URI. */ - pj_str_t host_part; /**< Host part of local URI. */ - pj_str_t contact_uri; /**< Uri in Contact: header. */ - - pj_str_t reg_uri; /**< Registrar URI. */ - pjsip_regc *regc; /**< Client registration session. */ - pj_int32_t reg_timeout; /**< Default timeout. */ - pj_timer_entry reg_timer; /**< Registration timer. */ - pj_status_t reg_last_err; /**< Last registration error. */ - int reg_last_code; /**< Last status last register. */ - - pj_str_t proxy; /**< Proxy URL. */ - pjsip_route_hdr route_set; /**< Route set. */ - - pj_bool_t online_status; /**< Our online status. */ - pjsua_srv_pres pres_srv_list; /**< Server subscription list. */ - - void *app_data; /**< Application data. */ -}; - - -typedef struct pjsua_acc pjsua_acc; - - -/* PJSUA application variables. */ -struct pjsua -{ - /* Control: */ - pj_caching_pool cp; /**< Global pool factory. */ - pjsip_endpoint *endpt; /**< Global endpoint. */ - pj_pool_t *pool; /**< pjsua's private pool. */ - pjsip_module mod; /**< pjsua's PJSIP module. */ - - - /* Media: */ - pjmedia_endpt *med_endpt; /**< Media endpoint. */ - pjmedia_conf *mconf; /**< Media conference. */ - pj_bool_t null_audio; /**< Null audio flag. */ - char *wav_file; /**< WAV file name to play. */ - unsigned wav_slot; /**< WAV player slot in bridge */ - pj_bool_t auto_play; /**< Auto play file for calls? */ - pj_bool_t auto_loop; /**< Auto loop RTP stream? */ - pj_bool_t auto_conf; /**< Auto put to conference? */ - - - /* User Agent behaviour: */ - int auto_answer; /**< Automatically answer in calls. */ - - /* Account: */ - int acc_cnt; /**< Number of client registrations */ - pjsua_acc acc[PJSUA_MAX_ACC]; /** Client regs array. */ - - - /* Authentication credentials: */ - - int cred_count; /**< Number of credentials. */ - pjsip_cred_info cred_info[10]; /**< Array of credentials. */ - - - /* Threading (optional): */ - int thread_cnt; /**< Thread count. */ - pj_thread_t *threads[8]; /**< Thread instances. */ - pj_bool_t quit_flag; /**< To signal thread to quit. */ - - /* Transport (UDP): */ - pj_uint16_t sip_port; /**< SIP signaling port. */ - pj_sock_t sip_sock; /**< SIP UDP socket. */ - pj_sockaddr_in sip_sock_name; /**< Public/STUN UDP socket addr. */ - - pj_str_t outbound_proxy;/**< Outbound proxy. */ - - - /* STUN: */ - pj_str_t stun_srv1; - int stun_port1; - pj_str_t stun_srv2; - int stun_port2; - - - /* Logging: */ - int log_level; /**< Logging verbosity. */ - int app_log_level; /**< stdout log verbosity. */ - unsigned log_decor; /**< Log decoration. */ - char *log_filename; /**< Log filename. */ - - - /* PJSUA Calls: */ - int max_calls; /**< Max nb of calls. */ - int call_cnt; /**< Number of calls. */ - pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */ - - - /* SIMPLE and buddy status: */ - int buddy_cnt; - pjsua_buddy buddies[PJSUA_MAX_BUDDIES]; -}; - - -/** PJSUA instance. */ -extern struct pjsua pjsua; - - - -/***************************************************************************** - * PJSUA API (defined in pjsua_core.c). - */ - -/** - * Initialize pjsua settings with default parameters. - */ -void pjsua_default(void); - - -/** - * Display error message for the specified error code. - */ -void pjsua_perror(const char *sender, const char *title, - pj_status_t status); - - -/** - * Initialize pjsua application. Application can call this before parsing - * application settings. - * - * This will initialize all libraries, create endpoint instance, and register - * pjsip modules. Transport will NOT be created however. - * - * Application may register module after calling this function. - */ -pj_status_t pjsua_init(void); - - -/** - * Start pjsua stack. Application calls this after pjsua settings has been - * configured. - * - * This will start the transport, worker threads (if any), and registration - * process, if registration is configured. - */ -pj_status_t pjsua_start(void); - - -/** - * Destroy pjsua. - */ -pj_status_t pjsua_destroy(void); - - -/** - * Find account for incoming request. - */ -int pjsua_find_account_for_incoming(pjsip_rx_data *rdata); - - -/** - * Find account for outgoing request. - */ -int pjsua_find_account_for_outgoing(const pj_str_t *url); - - -/***************************************************************************** - * PJSUA Call API (defined in pjsua_call.c). - */ - -/** - * Init pjsua call module. - */ -pj_status_t pjsua_call_init(void); - -/** - * Make outgoing call. - */ -pj_status_t pjsua_make_call(int acc_index, - const char *cstr_dest_uri, - int *p_call_index); - - -/** - * Handle incoming invite request. - */ -pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata); - - -/** - * Answer call. - */ -void pjsua_call_answer(int call_index, int code); - -/** - * Hangup call. - */ -void pjsua_call_hangup(int call_index, int code); - - -/** - * Put call on-hold. - */ -void pjsua_call_set_hold(int call_index); - - -/** - * Send re-INVITE (to release hold). - */ -void pjsua_call_reinvite(int call_index); - - -/** - * Transfer call. - */ -void pjsua_call_xfer(int call_index, const char *dest); - - -/** - * Terminate all calls. - */ -void pjsua_inv_shutdown(void); - - -/***************************************************************************** - * PJSUA Client Registration API (defined in pjsua_reg.c). - */ - -/** - * Initialize client registration session. - * - * @param app_callback Optional callback - */ -pj_status_t pjsua_regc_init(int acc_index); - -/** - * Update registration or perform unregistration. If renew argument is zero, - * this will start unregistration process. - */ -void pjsua_regc_update(int acc_index, pj_bool_t renew); - - - - -/***************************************************************************** - * PJSUA Presence (pjsua_pres.c) - */ - -/** - * Init presence. - */ -pj_status_t pjsua_pres_init(); - -/** - * Refresh both presence client and server subscriptions. - */ -void pjsua_pres_refresh(int acc_index); - -/** - * Terminate all subscriptions - */ -void pjsua_pres_shutdown(void); - -/** - * Dump presence subscriptions. - */ -void pjsua_pres_dump(void); - - -/***************************************************************************** - * User Interface API. - * - * The UI API specifies functions that will be called by pjsua upon - * occurence of various events. - */ - -/** - * Notify UI when invite state has changed. - */ -void pjsua_ui_inv_on_state_changed(int call_index, pjsip_event *e); - -/** - * Notify UI when registration status has changed. - */ -void pjsua_ui_regc_on_state_changed(int acc_index); - - -/***************************************************************************** - * Utilities. - * - */ - -/** String to describe invite session states */ -extern const char *pjsua_inv_state_names[]; - -/** - * Parse arguments (pjsua_opt.c). - */ -pj_status_t pjsua_parse_args(int argc, char *argv[]); - -/** - * Load settings from a file. - */ -pj_status_t pjsua_load_settings(const char *filename); - -/** - * Dump settings. - */ -int pjsua_dump_settings(char *buf, pj_size_t max); - -/** - * Save settings to a file. - */ -pj_status_t pjsua_save_settings(const char *filename); - - -/* - * Verify that valid SIP url is given. - * @return PJ_SUCCESS if valid. - */ -pj_status_t pjsua_verify_sip_url(const char *c_url); - -/* - * Dump application states. - */ -void pjsua_dump(void); - - -PJ_END_DECL - - -#endif /* __PJSUA_H__ */ diff --git a/pjsip/src/pjsua/pjsua_call.c b/pjsip/src/pjsua/pjsua_call.c deleted file mode 100644 index e0a9a0af..00000000 --- a/pjsip/src/pjsua/pjsua_call.c +++ /dev/null @@ -1,1090 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "pjsua.h" -#include - - -/* - * pjsua_inv.c - * - * Invite session specific functionalities. - */ - -#define THIS_FILE "pjsua_inv.c" - - -/** - * Make outgoing call. - */ -pj_status_t pjsua_make_call(int acc_index, - const char *cstr_dest_uri, - int *p_call_index) -{ - pj_str_t dest_uri; - pjsip_dialog *dlg; - pjmedia_sdp_session *offer; - pjsip_inv_session *inv; - int call_index = -1; - pjsip_tx_data *tdata; - pj_status_t status; - - /* Convert cstr_dest_uri to dest_uri */ - - dest_uri = pj_str((char*)cstr_dest_uri); - - /* Find free call slot. */ - for (call_index=0; call_indexpool, 1, - &pjsua.calls[call_index].skinfo, - &offer); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status); - goto on_error; - } - - /* Create the INVITE session: */ - - status = pjsip_inv_create_uac( dlg, offer, 0, &inv); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Invite session creation failed", status); - goto on_error; - } - - - /* Create and associate our data in the session. */ - - pjsua.calls[call_index].inv = inv; - - dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; - inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; - - - /* Set dialog Route-Set: */ - - if (!pj_list_empty(&pjsua.acc[acc_index].route_set)) - pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set); - - - /* Set credentials: */ - - pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count, - pjsua.cred_info); - - - /* Create initial INVITE: */ - - status = pjsip_inv_invite(inv, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", - status); - goto on_error; - } - - - /* Send initial INVITE: */ - - status = pjsip_inv_send_msg(inv, tdata, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", - status); - goto on_error; - } - - - /* Done. */ - - ++pjsua.call_cnt; - - if (p_call_index) - *p_call_index = call_index; - - return PJ_SUCCESS; - - -on_error: - PJ_TODO(DESTROY_DIALOG_ON_FAIL); - if (call_index != -1) { - pjsua.calls[call_index].inv = NULL; - } - return status; -} - - -/** - * Handle incoming INVITE request. - */ -pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) -{ - pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); - pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); - pjsip_msg *msg = rdata->msg_info.msg; - pjsip_tx_data *response = NULL; - unsigned options = 0; - pjsip_inv_session *inv; - int acc_index; - int call_index = -1; - pjmedia_sdp_session *answer; - pj_status_t status; - - /* Don't want to handle anything but INVITE */ - if (msg->line.req.method.id != PJSIP_INVITE_METHOD) - return PJ_FALSE; - - /* Don't want to handle anything that's already associated with - * existing dialog or transaction. - */ - if (dlg || tsx) - return PJ_FALSE; - - - /* Verify that we can handle the request. */ - status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, - pjsua.endpt, &response); - if (status != PJ_SUCCESS) { - - /* - * No we can't handle the incoming INVITE request. - */ - - if (response) { - pjsip_response_addr res_addr; - - pjsip_get_response_addr(response->pool, rdata, &res_addr); - pjsip_endpt_send_response(pjsua.endpt, &res_addr, response, - NULL, NULL); - - } else { - - /* Respond with 500 (Internal Server Error) */ - pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, - NULL, NULL); - } - - return PJ_TRUE; - } - - - /* - * Yes we can handle the incoming INVITE request. - */ - - /* Find free call slot. */ - for (call_index=0; call_index < pjsua.max_calls; ++call_index) { - if (pjsua.calls[call_index].inv == NULL) - break; - } - - if (call_index == PJSUA_MAX_CALLS) { - pjsip_endpt_respond_stateless(pjsua.endpt, rdata, - PJSIP_SC_BUSY_HERE, NULL, - NULL, NULL); - return PJ_TRUE; - } - - - /* Get media capability from media endpoint: */ - - status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1, - &pjsua.calls[call_index].skinfo, - &answer ); - if (status != PJ_SUCCESS) { - pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, - NULL, NULL); - - return PJ_TRUE; - } - - /* TODO: - * - * Get which account is most likely to be associated with this incoming - * call. We need the account to find which contact URI to put for - * the call. - */ - acc_index = 0; - - /* Create dialog: */ - - status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, - &pjsua.acc[acc_index].contact_uri, - &dlg); - if (status != PJ_SUCCESS) { - pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, - NULL, NULL); - - return PJ_TRUE; - } - - - /* Create invite session: */ - - status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv); - if (status != PJ_SUCCESS) { - - pjsip_dlg_respond(dlg, rdata, 500, NULL); - - // TODO: Need to delete dialog - return PJ_TRUE; - } - - - /* Create and attach pjsua data to the dialog: */ - - pjsua.calls[call_index].inv = inv; - - dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; - inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index]; - - - /* Must answer with some response to initial INVITE. - * If auto-answer flag is set, send 200 straight away, otherwise send 100. - */ - - status = pjsip_inv_initial_answer(inv, rdata, - (pjsua.auto_answer ? 200 : 100), - NULL, NULL, &response); - if (status != PJ_SUCCESS) { - - pjsua_perror(THIS_FILE, "Unable to create 100 response", status); - - pjsip_dlg_respond(dlg, rdata, 500, NULL); - - // TODO: Need to delete dialog - - } else { - status = pjsip_inv_send_msg(inv, response, NULL); - if (status != PJ_SUCCESS) - pjsua_perror(THIS_FILE, "Unable to send 100 response", status); - } - - if (pjsua.auto_answer < 200) { - PJ_LOG(3,(THIS_FILE, - "\nIncoming call!!\n" - "From: %.*s\n" - "To: %.*s\n" - "(press 'a' to answer, 'h' to decline)", - (int)dlg->remote.info_str.slen, - dlg->remote.info_str.ptr, - (int)dlg->local.info_str.slen, - dlg->local.info_str.ptr)); - } else { - PJ_LOG(3,(THIS_FILE, - "Call From:%.*s To:%.*s was answered with %d (%s)", - (int)dlg->remote.info_str.slen, - dlg->remote.info_str.ptr, - (int)dlg->local.info_str.slen, - dlg->local.info_str.ptr, - pjsua.auto_answer, - pjsip_get_status_text(pjsua.auto_answer)->ptr )); - } - - ++pjsua.call_cnt; - - /* This INVITE request has been handled. */ - return PJ_TRUE; -} - - -/* - * This callback receives notification from invite session when the - * session state has changed. - */ -static void pjsua_call_on_state_changed(pjsip_inv_session *inv, - pjsip_event *e) -{ - pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id]; - - /* If this is an outgoing INVITE that was created because of - * REFER/transfer, send NOTIFY to transferer. - */ - if (call && call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) { - int st_code = -1; - pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE; - - - switch (call->inv->state) { - case PJSIP_INV_STATE_NULL: - case PJSIP_INV_STATE_CALLING: - /* Do nothing */ - break; - - case PJSIP_INV_STATE_EARLY: - case PJSIP_INV_STATE_CONNECTING: - st_code = e->body.tsx_state.tsx->status_code; - ev_state = PJSIP_EVSUB_STATE_ACTIVE; - break; - - case PJSIP_INV_STATE_CONFIRMED: - /* When state is confirmed, send the final 200/OK and terminate - * subscription. - */ - st_code = e->body.tsx_state.tsx->status_code; - ev_state = PJSIP_EVSUB_STATE_TERMINATED; - break; - - case PJSIP_INV_STATE_DISCONNECTED: - st_code = e->body.tsx_state.tsx->status_code; - ev_state = PJSIP_EVSUB_STATE_TERMINATED; - break; - } - - if (st_code != -1) { - pjsip_tx_data *tdata; - pj_status_t status; - - status = pjsip_xfer_notify( call->xfer_sub, - ev_state, st_code, - NULL, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status); - } else { - status = pjsip_xfer_send_request(call->xfer_sub, tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status); - } - } - } - } - - - pjsua_ui_inv_on_state_changed(call->index, e); - - /* call->inv may be NULL now */ - - /* Destroy media session when invite session is disconnected. */ - if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { - - pj_assert(call != NULL); - - if (call && call->session) { - pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot); - pjmedia_session_destroy(call->session); - call->session = NULL; - - PJ_LOG(3,(THIS_FILE,"Media session is destroyed")); - } - - call->inv = NULL; - --pjsua.call_cnt; - } -} - - -/* - * Callback called by event framework when the xfer subscription state - * has changed. - */ -static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) -{ - - PJ_UNUSED_ARG(event); - - /* - * We're only interested when subscription is terminated, to - * clear the xfer_sub member of the inv_data. - */ - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - pjsua_call *call; - - call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); - if (!call) - return; - - pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); - call->xfer_sub = NULL; - - PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated")); - } -} - - -/* - * Follow transfer (REFER) request. - */ -static void on_call_transfered( pjsip_inv_session *inv, - pjsip_rx_data *rdata ) -{ - pj_status_t status; - pjsip_tx_data *tdata; - pjsua_call *existing_call; - int new_call; - const pj_str_t str_refer_to = { "Refer-To", 8}; - pjsip_generic_string_hdr *refer_to; - char *uri; - struct pjsip_evsub_user xfer_cb; - pjsip_evsub *sub; - - existing_call = inv->dlg->mod_data[pjsua.mod.id]; - - /* Find the Refer-To header */ - refer_to = (pjsip_generic_string_hdr*) - pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL); - - if (refer_to == NULL) { - /* Invalid Request. - * No Refer-To header! - */ - PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!")); - pjsip_dlg_respond( inv->dlg, rdata, 400, NULL); - return; - } - - PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s", - (int)inv->dlg->remote.info_str.slen, - inv->dlg->remote.info_str.ptr, - (int)refer_to->hvalue.slen, - refer_to->hvalue.ptr)); - - /* Init callback */ - pj_memset(&xfer_cb, 0, sizeof(xfer_cb)); - xfer_cb.on_evsub_state = &xfer_on_evsub_state; - - /* Create transferee event subscription */ - status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create xfer uas", status); - pjsip_dlg_respond( inv->dlg, rdata, 500, NULL); - return; - } - - /* Accept the REFER request, send 200 (OK). */ - pjsip_xfer_accept(sub, rdata, 200, NULL); - - /* Create initial NOTIFY request */ - status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, - 100, NULL, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status); - return; - } - - /* Send initial NOTIFY request */ - status = pjsip_xfer_send_request( sub, tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status); - return; - } - - /* We're cheating here. - * We need to get a null terminated string from a pj_str_t. - * So grab the pointer from the hvalue and NULL terminate it, knowing - * that the NULL position will be occupied by a newline. - */ - uri = refer_to->hvalue.ptr; - uri[refer_to->hvalue.slen] = '\0'; - - /* Now make the outgoing call. */ - status = pjsua_make_call(existing_call->acc_index, uri, &new_call); - if (status != PJ_SUCCESS) { - - /* Notify xferer about the error */ - status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, - 500, NULL, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", - status); - return; - } - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", - status); - return; - } - return; - } - - /* Put the server subscription in inv_data. - * Subsequent state changed in pjsua_inv_on_state_changed() will be - * reported back to the server subscription. - */ - pjsua.calls[new_call].xfer_sub = sub; - - /* Put the invite_data in the subscription. */ - pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]); -} - - -/* - * This callback is called when transaction state has changed in INVITE - * session. We use this to trap incoming REFER request. - */ -static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, - pjsip_transaction *tsx, - pjsip_event *e) -{ - pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id]; - - if (tsx->role==PJSIP_ROLE_UAS && - tsx->state==PJSIP_TSX_STATE_TRYING && - pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0) - { - /* - * Incoming REFER request. - */ - on_call_transfered(call->inv, e->body.tsx_state.src.rdata); - } -} - - -/* - * This callback is called by invite session framework when UAC session - * has forked. - */ -static void pjsua_call_on_forked( pjsip_inv_session *inv, - pjsip_event *e) -{ - PJ_UNUSED_ARG(inv); - PJ_UNUSED_ARG(e); - - PJ_TODO(HANDLE_FORKED_DIALOG); -} - - -/* - * Create inactive SDP for call hold. - */ -static pj_status_t create_inactive_sdp(pjsua_call *call, - pjmedia_sdp_session **p_answer) -{ - pj_status_t status; - pjmedia_sdp_conn *conn; - pjmedia_sdp_attr *attr; - pjmedia_sdp_session *sdp; - - /* Create new offer */ - status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1, - &call->skinfo, &sdp); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create local SDP", status); - return status; - } - - /* Get SDP media connection line */ - conn = sdp->media[0]->conn; - if (!conn) - conn = sdp->conn; - - /* Modify address */ - conn->addr = pj_str("0.0.0.0"); - - /* Remove existing directions attributes */ - pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv"); - pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly"); - pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly"); - pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive"); - - /* Add inactive attribute */ - attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL); - pjmedia_sdp_media_add_attr(sdp->media[0], attr); - - *p_answer = sdp; - - return status; -} - -/* - * Called when session received new offer. - */ -static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, - const pjmedia_sdp_session *offer) -{ - pjsua_call *call; - pjmedia_sdp_conn *conn; - pjmedia_sdp_session *answer; - pj_bool_t is_remote_active; - pj_status_t status; - - call = inv->dlg->mod_data[pjsua.mod.id]; - - /* - * See if remote is offering active media (i.e. not on-hold) - */ - is_remote_active = PJ_TRUE; - - conn = offer->media[0]->conn; - if (!conn) - conn = offer->conn; - - if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 || - pj_strcmp2(&conn->addr, "0")==0) - { - is_remote_active = PJ_FALSE; - - } - else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL)) - { - is_remote_active = PJ_FALSE; - } - - PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s", - (is_remote_active ? "active" : "inactive"))); - - /* Supply candidate answer */ - if (is_remote_active) { - status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1, - &call->skinfo, &answer); - } else { - status = create_inactive_sdp( call, &answer ); - } - - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create local SDP", status); - return; - } - - status = pjsip_inv_set_sdp_answer(call->inv, answer); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to set answer", status); - return; - } - -} - - -/* - * Callback to be called when SDP offer/answer negotiation has just completed - * in the session. This function will start/update media if negotiation - * has succeeded. - */ -static void pjsua_call_on_media_update(pjsip_inv_session *inv, - pj_status_t status) -{ - pjsua_call *call; - const pjmedia_sdp_session *local_sdp; - const pjmedia_sdp_session *remote_sdp; - pjmedia_port *media_port; - pj_str_t port_name; - char tmp[PJSIP_MAX_URL_SIZE]; - - call = inv->dlg->mod_data[pjsua.mod.id]; - - if (status != PJ_SUCCESS) { - - pjsua_perror(THIS_FILE, "SDP negotiation has failed", status); - return; - - } - - /* Destroy existing media session, if any. */ - - if (call && call->session) { - pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot); - pjmedia_session_destroy(call->session); - call->session = NULL; - } - - /* Get local and remote SDP */ - - status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to retrieve currently active local SDP", - status); - return; - } - - - status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to retrieve currently active remote SDP", - status); - return; - } - - /* Create new media session. - * The media session is active immediately. - */ - if (pjsua.null_audio) - return; - - status = pjmedia_session_create( pjsua.med_endpt, 1, - &call->skinfo, - local_sdp, remote_sdp, - call, - &call->session ); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create media session", - status); - return; - } - - - /* Get the port interface of the first stream in the session. - * We need the port interface to add to the conference bridge. - */ - pjmedia_session_get_port(call->session, 0, &media_port); - - - /* - * Add the call to conference bridge. - */ - port_name.ptr = tmp; - port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, - call->inv->dlg->remote.info->uri, - tmp, sizeof(tmp)); - if (port_name.slen < 1) { - port_name = pj_str("call"); - } - status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool, - media_port, - &port_name, - &call->conf_slot); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create conference slot", - status); - pjmedia_session_destroy(call->session); - call->session = NULL; - return; - } - - /* If auto-play is configured, connect the call to the file player - * port - */ - if (pjsua.auto_play && pjsua.wav_file && - call->inv->role == PJSIP_ROLE_UAS) - { - - pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot, - call->conf_slot); - - } else if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) { - - pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, - call->conf_slot); - - } else if (pjsua.auto_conf) { - - int i; - - pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot); - pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0); - - for (i=0; i < pjsua.max_calls; ++i) { - - if (!pjsua.calls[i].session) - continue; - - pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, - pjsua.calls[i].conf_slot); - pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot, - call->conf_slot); - } - - } else { - - /* Connect new call to the sound device port (port zero) in the - * main conference bridge. - */ - pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot); - pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0); - } - - - /* Done. */ - { - struct pjmedia_session_info sess_info; - char info[80]; - int info_len = 0; - unsigned i; - - pjmedia_session_get_info(call->session, &sess_info); - for (i=0; idir) { - case PJMEDIA_DIR_NONE: - dir = "inactive"; - break; - case PJMEDIA_DIR_ENCODING: - dir = "sendonly"; - break; - case PJMEDIA_DIR_DECODING: - dir = "recvonly"; - break; - case PJMEDIA_DIR_ENCODING_DECODING: - dir = "sendrecv"; - break; - default: - dir = "unknown"; - break; - } - len = pj_ansi_sprintf( info+info_len, - ", stream #%d: %.*s (%s)", i, - (int)strm_info->fmt.encoding_name.slen, - (int)strm_info->fmt.encoding_name.ptr, - dir); - if (len > 0) - info_len += len; - } - PJ_LOG(3,(THIS_FILE,"Media started%s", info)); - } -} - - -/* - * Hangup call. - */ -void pjsua_call_hangup(int call_index, int code) -{ - pjsua_call *call; - pj_status_t status; - pjsip_tx_data *tdata; - - - call = &pjsua.calls[call_index]; - - if (!call->inv) { - PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; - } - - status = pjsip_inv_end_session(call->inv, code, NULL, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Failed to create end session message", - status); - return; - } - - /* pjsip_inv_end_session may return PJ_SUCCESS with NULL - * as p_tdata when INVITE transaction has not been answered - * with any provisional responses. - */ - if (tdata == NULL) - return; - - status = pjsip_inv_send_msg(call->inv, tdata, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Failed to send end session message", - status); - return; - } -} - - -/* - * Put call on-Hold. - */ -void pjsua_call_set_hold(int call_index) -{ - pjmedia_sdp_session *sdp; - pjsua_call *call; - pjsip_tx_data *tdata; - pj_status_t status; - - call = &pjsua.calls[call_index]; - - if (!call->inv) { - PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; - } - - if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { - PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed")); - return; - } - - status = create_inactive_sdp(call, &sdp); - if (status != PJ_SUCCESS) - return; - - /* Send re-INVITE with new offer */ - status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); - return; - } - - status = pjsip_inv_send_msg( call->inv, tdata, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); - return; - } -} - - -/* - * re-INVITE. - */ -void pjsua_call_reinvite(int call_index) -{ - pjmedia_sdp_session *sdp; - pjsip_tx_data *tdata; - pjsua_call *call; - pj_status_t status; - - call = &pjsua.calls[call_index]; - - if (!call->inv) { - PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; - } - - - if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { - PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed")); - return; - } - - /* Create SDP */ - status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1, - &call->skinfo, &sdp); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", - status); - return; - } - - /* Send re-INVITE with new offer */ - status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); - return; - } - - status = pjsip_inv_send_msg( call->inv, tdata, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); - return; - } -} - - -/* - * Transfer call. - */ -void pjsua_call_xfer(int call_index, const char *dest) -{ - pjsip_evsub *sub; - pjsip_tx_data *tdata; - pjsua_call *call; - pj_str_t tmp; - pj_status_t status; - - - call = &pjsua.calls[call_index]; - - if (!call->inv) { - PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; - } - - /* Create xfer client subscription. - * We're not interested in knowing the transfer result, so we - * put NULL as the callback. - */ - status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create xfer", status); - return; - } - - /* - * Create REFER request. - */ - status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create REFER request", status); - return; - } - - /* Send. */ - status = pjsip_xfer_send_request(sub, tdata); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to send REFER request", status); - return; - } - - /* For simplicity (that's what this program is intended to be!), - * leave the original invite session as it is. More advanced application - * may want to hold the INVITE, or terminate the invite, or whatever. - */ -} - - -/* - * Terminate all calls. - */ -void pjsua_inv_shutdown() -{ - int i; - - for (i=0; iinv, 410, NULL, &tdata)==0) { - if (tdata) - pjsip_inv_send_msg(call->inv, tdata, NULL); - } - } -} - - -pj_status_t pjsua_call_init(void) -{ - /* Initialize invite session callback. */ - pjsip_inv_callback inv_cb; - pj_status_t status; - - pj_memset(&inv_cb, 0, sizeof(inv_cb)); - inv_cb.on_state_changed = &pjsua_call_on_state_changed; - inv_cb.on_new_session = &pjsua_call_on_forked; - inv_cb.on_media_update = &pjsua_call_on_media_update; - inv_cb.on_rx_offer = &pjsua_call_on_rx_offer; - inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed; - - - /* Initialize invite session module: */ - status = pjsip_inv_usage_init(pjsua.endpt, &pjsua.mod, &inv_cb); - - return status; -} diff --git a/pjsip/src/pjsua/pjsua_core.c b/pjsip/src/pjsua/pjsua_core.c deleted file mode 100644 index 974c6d53..00000000 --- a/pjsip/src/pjsua/pjsua_core.c +++ /dev/null @@ -1,910 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "pjsua.h" - -/* - * pjsua_core.c - * - * Core application functionalities. - */ - -#define THIS_FILE "pjsua_core.c" - - -/* - * Global variable. - */ -struct pjsua pjsua; - - -/* - * Default local URI, if none is specified in cmd-line - */ -#define PJSUA_LOCAL_URI "" - - - -/* - * Init default application parameters. - */ -void pjsua_default(void) -{ - unsigned i; - - - /* Normally need another thread for console application, because main - * thread will be blocked in fgets(). - */ - pjsua.thread_cnt = 1; - - - /* Default transport settings: */ - pjsua.sip_port = 5060; - - - /* Default logging settings: */ - pjsua.log_level = 5; - pjsua.app_log_level = 4; - pjsua.log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME | - PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE; - - - /* Default: do not use STUN: */ - pjsua.stun_port1 = pjsua.stun_port2 = 0; - - /* Init accounts: */ - pjsua.acc_cnt = 1; - for (i=0; imsg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) { - - return pjsua_call_on_incoming(rdata); - } - - return PJ_FALSE; -} - - -/* - * Handler for receiving incoming responses. - * - * This handler serves multiple purposes: - * - it receives strayed responses (i.e. outside any dialog and - * outside any transactions). - * - it receives responses coming to a transaction, when pjsua - * module is set as transaction user for the transaction. - * - it receives responses inside a dialog, when these responses - * are unhandled by other dialog usages. - */ -static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata) -{ - PJ_UNUSED_ARG(rdata); - return PJ_FALSE; -} - - -/* - * Initialize sockets and optionally get the public address via STUN. - */ -static pj_status_t init_sockets(pj_bool_t sip, - pjmedia_sock_info *skinfo) -{ - enum { - RTP_START_PORT = 4000, - RTP_RANDOM_START = 2, - RTP_RETRY = 100 - }; - enum { - SIP_SOCK, - RTP_SOCK, - RTCP_SOCK, - }; - int i; - static pj_uint16_t rtp_port = RTP_START_PORT; - pj_sock_t sock[3]; - pj_sockaddr_in mapped_addr[3]; - pj_status_t status = PJ_SUCCESS; - - for (i=0; i<3; ++i) - sock[i] = PJ_INVALID_SOCKET; - - /* Create and bind SIP UDP socket. */ - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[SIP_SOCK]); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "socket() error", status); - goto on_error; - } - - if (sip) { - status = pj_sock_bind_in(sock[SIP_SOCK], 0, pjsua.sip_port); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "bind() error", status); - goto on_error; - } - } else { - status = pj_sock_bind_in(sock[SIP_SOCK], 0, 0); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "bind() error", status); - goto on_error; - } - } - - - /* Loop retry to bind RTP and RTCP sockets. */ - for (i=0; irtp_sock = sock[RTP_SOCK]; - pj_memcpy(&skinfo->rtp_addr_name, - &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in)); - - skinfo->rtcp_sock = sock[RTCP_SOCK]; - pj_memcpy(&skinfo->rtcp_addr_name, - &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in)); - - if (sip) { - PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", - pj_inet_ntoa(pjsua.sip_sock_name.sin_addr), - pj_ntohs(pjsua.sip_sock_name.sin_port))); - } - PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d", - pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr), - pj_ntohs(skinfo->rtp_addr_name.sin_port))); - PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d", - pj_inet_ntoa(skinfo->rtcp_addr_name.sin_addr), - pj_ntohs(skinfo->rtcp_addr_name.sin_port))); - - rtp_port += 2; - return PJ_SUCCESS; - -on_error: - for (i=0; i<3; ++i) { - if (sip && i==0) - continue; - if (sock[i] != PJ_INVALID_SOCKET) - pj_sock_close(sock[i]); - } - return status; -} - - - -/* - * Initialize stack. - */ -static pj_status_t init_stack(void) -{ - pj_status_t status; - - /* Create global endpoint: */ - - { - const pj_str_t *hostname; - const char *endpt_name; - - /* Endpoint MUST be assigned a globally unique name. - * The name will be used as the hostname in Warning header. - */ - - /* For this implementation, we'll use hostname for simplicity */ - hostname = pj_gethostname(); - endpt_name = hostname->ptr; - - /* Create the endpoint: */ - - status = pjsip_endpt_create(&pjsua.cp.factory, endpt_name, - &pjsua.endpt); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status); - return status; - } - } - - - /* Initialize transaction layer: */ - - status = pjsip_tsx_layer_init_module(pjsua.endpt); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Transaction layer initialization error", - status); - goto on_error; - } - - /* Initialize UA layer module: */ - - status = pjsip_ua_init_module( pjsua.endpt, NULL ); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "UA layer initialization error", status); - goto on_error; - } - - /* Initialize and register pjsua's application module: */ - - { - pjsip_module my_mod = - { - NULL, NULL, /* prev, next. */ - { "mod-pjsua", 9 }, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ - NULL, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - NULL, /* unload() */ - &mod_pjsua_on_rx_request, /* on_rx_request() */ - &mod_pjsua_on_rx_response, /* on_rx_response() */ - NULL, /* on_tx_request. */ - NULL, /* on_tx_response() */ - NULL, /* on_tsx_state() */ - }; - - pjsua.mod = my_mod; - - status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to register pjsua module", - status); - goto on_error; - } - } - - /* Initialize invite session module: */ - - status = pjsua_call_init(); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Invite usage initialization error", - status); - goto on_error; - } - - /* Done */ - - return PJ_SUCCESS; - - -on_error: - pjsip_endpt_destroy(pjsua.endpt); - pjsua.endpt = NULL; - return status; -} - - -static int PJ_THREAD_FUNC pjsua_poll(void *arg) -{ - PJ_UNUSED_ARG(arg); - - do { - pj_time_val timeout = { 0, 10 }; - pjsip_endpt_handle_events (pjsua.endpt, &timeout); - } while (!pjsua.quit_flag); - - return 0; -} - -/* - * Initialize pjsua application. - * This will initialize all libraries, create endpoint instance, and register - * pjsip modules. - */ -pj_status_t pjsua_init(void) -{ - pj_status_t status; - - /* Init PJLIB logging: */ - - pj_log_set_level(pjsua.log_level); - pj_log_set_decor(pjsua.log_decor); - - - /* Init PJLIB: */ - - status = pj_init(); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "pj_init() error", status); - return status; - } - - /* Init PJLIB-UTIL: */ - - status = pjlib_util_init(); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "pjlib_util_init() error", status); - return status; - } - - /* Init memory pool: */ - - /* Init caching pool. */ - pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0); - - /* Create memory pool for application. */ - pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL); - - - /* Init PJSIP : */ - - status = init_stack(); - if (status != PJ_SUCCESS) { - pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror(THIS_FILE, "Stack initialization has returned error", - status); - return status; - } - - - /* Init core SIMPLE module : */ - - pjsip_evsub_init_module(pjsua.endpt); - - /* Init presence module: */ - - pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance()); - - /* Init xfer/REFER module */ - - pjsip_xfer_init_module( pjsua.endpt ); - - /* Init pjsua presence handler: */ - - pjsua_pres_init(); - - - /* Init media endpoint: */ - - status = pjmedia_endpt_create(&pjsua.cp.factory, &pjsua.med_endpt); - if (status != PJ_SUCCESS) { - pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror(THIS_FILE, - "Media stack initialization has returned error", - status); - return status; - } - - /* Init conference bridge. */ - - status = pjmedia_conf_create(pjsua.pool, - pjsua.max_calls+PJSUA_CONF_MORE_PORTS, - 8000, 160, 16, &pjsua.mconf); - if (status != PJ_SUCCESS) { - pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror(THIS_FILE, - "Media stack initialization has returned error", - status); - return status; - } - - /* Init pjmedia-codecs: */ - - status = pjmedia_codec_init(pjsua.med_endpt); - if (status != PJ_SUCCESS) { - pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror(THIS_FILE, - "Media codec initialization has returned error", - status); - return status; - } - - - /* Done. */ - return PJ_SUCCESS; -} - - -/* - * Find account for incoming request. - */ -int pjsua_find_account_for_incoming(pjsip_rx_data *rdata) -{ - pjsip_uri *uri; - pjsip_sip_uri *sip_uri; - int acc_index; - - uri = rdata->msg_info.to->uri; - - /* Just return account #0 if To URI is not SIP: */ - if (!PJSIP_URI_SCHEME_IS_SIP(uri) && - !PJSIP_URI_SCHEME_IS_SIPS(uri)) - { - return 0; - } - - - sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri); - - /* Find account which has matching username and domain. */ - for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) { - - pjsua_acc *acc = &pjsua.acc[acc_index]; - - if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 && - pj_stricmp(&acc->host_part, &sip_uri->host)==0) - { - /* Match ! */ - return acc_index; - } - } - - /* No matching, try match domain part only. */ - for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) { - - pjsua_acc *acc = &pjsua.acc[acc_index]; - - if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) { - /* Match ! */ - return acc_index; - } - } - - /* Still no match, just return account #0 */ - return 0; -} - - -/* - * Find account for outgoing request. - */ -int pjsua_find_account_for_outgoing(const pj_str_t *url) -{ - PJ_UNUSED_ARG(url); - - /* Just use account #0 */ - return 0; -} - - -/* - * Start pjsua stack. - * This will start the registration process, if registration is configured. - */ -pj_status_t pjsua_start(void) -{ - int i; /* Must be signed */ - pjsip_transport *udp_transport; - pj_status_t status = PJ_SUCCESS; - - /* Create WAV file player if required: */ - - if (pjsua.wav_file) { - pjmedia_port *port; - pj_str_t port_name; - - /* Create the file player port. */ - status = pjmedia_file_player_port_create( pjsua.pool, pjsua.wav_file, - 0, -1, NULL, &port); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Error playing media file", - status); - return status; - } - - /* Add port to conference bridge: */ - status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, port, - pj_cstr(&port_name, pjsua.wav_file), - &pjsua.wav_slot); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to add file player to conference bridge", - status); - return status; - } - } - - - /* Init sockets (STUN etc): */ - for (i=0; i<(int)pjsua.max_calls; ++i) { - status = init_sockets(i==0, &pjsua.calls[i].skinfo); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "init_sockets() has returned error", - status); - --i; - if (i >= 0) - pj_sock_close(pjsua.sip_sock); - while (i >= 0) { - pj_sock_close(pjsua.calls[i].skinfo.rtp_sock); - pj_sock_close(pjsua.calls[i].skinfo.rtcp_sock); - } - return status; - } - } - - /* Add UDP transport: */ - - { - /* Init the published name for the transport. - * Depending whether STUN is used, this may be the STUN mapped - * address, or socket's bound address. - */ - pjsip_host_port addr_name; - - addr_name.host.ptr = pj_inet_ntoa(pjsua.sip_sock_name.sin_addr); - addr_name.host.slen = pj_ansi_strlen(addr_name.host.ptr); - addr_name.port = pj_ntohs(pjsua.sip_sock_name.sin_port); - - /* Create UDP transport from previously created UDP socket: */ - - status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock, - &addr_name, 1, - &udp_transport); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to start UDP transport", - status); - return status; - } - } - - /* Initialize Contact URI, if one is not specified: */ - for (i=0; iuser; - pjsua.acc[i].host_part = sip_uri->host; - - if (pjsua.acc[i].contact_uri.slen == 0 && - pjsua.acc[i].local_uri.slen) - { - char contact[128]; - int len; - - /* The local Contact is the username@ip-addr, where - * - username is taken from the local URI, - * - ip-addr in UDP transport's address name (which may have been - * resolved from STUN. - */ - - /* Build temporary contact string. */ - - if (sip_uri->user.slen) { - - /* With the user part. */ - len = pj_snprintf(contact, sizeof(contact), - "", - (int)sip_uri->user.slen, - sip_uri->user.ptr, - (int)udp_transport->local_name.host.slen, - udp_transport->local_name.host.ptr, - udp_transport->local_name.port); - } else { - - /* Without user part */ - - len = pj_snprintf(contact, sizeof(contact), - "", - (int)udp_transport->local_name.host.slen, - udp_transport->local_name.host.ptr, - udp_transport->local_name.port); - } - - if (len < 1 || len >= sizeof(contact)) { - pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG); - return PJSIP_EURITOOLONG; - } - - /* Duplicate Contact uri. */ - - pj_strdup2(pjsua.pool, &pjsua.acc[i].contact_uri, contact); - - } - } - - /* If outbound_proxy is specified, put it in the route_set: */ - - if (pjsua.outbound_proxy.slen) { - - pjsip_route_hdr *route; - const pj_str_t hname = { "Route", 5 }; - int parsed_len; - - route = pjsip_parse_hdr( pjsua.pool, &hname, - pjsua.outbound_proxy.ptr, - pjsua.outbound_proxy.slen, - &parsed_len); - if (route == NULL) { - pjsua_perror(THIS_FILE, "Invalid outbound proxy URL", - PJSIP_EINVALIDURI); - return PJSIP_EINVALIDURI; - } - - for (i=0; i=0; --i) { - pj_thread_join(pjsua.threads[i]); - pj_thread_destroy(pjsua.threads[i]); - } - return status; - } - } - - /* Start registration: */ - - /* Create client registration session: */ - for (i=0; i - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "pjsua.h" -#include "getopt.h" -#include - -#define THIS_FILE "pjsua_opt.c" - - -const char *pjsua_inv_state_names[] = -{ - "NULL ", - "CALLING ", - "INCOMING ", - "EARLY ", - "CONNECTING", - "CONFIRMED ", - "DISCONNCTD", - "TERMINATED", -}; - - - -/* Show usage */ -static void usage(void) -{ - puts("Usage:"); - puts(" pjsua [options]"); - puts(""); - puts("General options:"); - puts(" --help Display this help screen"); - puts(" --version Display version info"); - puts(""); - puts("Logging options:"); - puts(" --config-file=file Read the config/arguments from file."); - puts(" --log-file=fname Log to filename (default stderr)"); - puts(" --log-level=N Set log max level to N (0(none) to 6(trace))"); - puts(" --app-log-level=N Set log max level for stdout display to N"); - puts(""); - puts("SIP Account options:"); - puts(" --id=url Set the URL of local ID (used in From header)"); - puts(" --contact=url Override the Contact information"); - puts(" --proxy=url Set the URL of proxy server"); - puts(""); - puts("SIP Account Registration Options:"); - puts(" --registrar=url Set the URL of registrar server"); - puts(" --reg-timeout=secs Set registration interval to secs (default 3600)"); - puts(""); - puts("SIP Account Control:"); - puts(" --next-account Add more account"); - puts(""); - puts("Authentication options:"); - puts(" --realm=string Set realm"); - puts(" --username=string Set authentication username"); - puts(" --password=string Set authentication password"); - puts(" --next-cred Add more credential"); - puts(""); - puts("Transport Options:"); - puts(" --local-port=port Set TCP/UDP port"); - puts(" --outbound=url Set the URL of outbound proxy server"); - puts(" --use-stun1=host[:port]"); - puts(" --use-stun2=host[:port] Resolve local IP with the specified STUN servers"); - puts(""); - puts("Media Options:"); - puts(" --null-audio Use NULL audio device"); - puts(" --play-file=file Play WAV file in conference bridge"); - puts(" --auto-play Automatically play the file (to incoming calls only)"); - puts(" --auto-loop Automatically loop incoming RTP to outgoing RTP"); - puts(" --auto-conf Automatically put incoming calls to conference"); - puts(""); - puts("Buddy List (can be more than one):"); - puts(" --add-buddy url Add the specified URL to the buddy list."); - puts(""); - puts("User Agent options:"); - puts(" --auto-answer=code Automatically answer incoming calls with code (e.g. 200)"); - puts(" --max-calls=N Maximum number of concurrent calls (default:4, max:255)"); - puts(""); - fflush(stdout); -} - - - -/* - * Verify that valid SIP url is given. - */ -pj_status_t pjsua_verify_sip_url(const char *c_url) -{ - pjsip_uri *p; - pj_pool_t *pool; - char *url; - int len = (c_url ? pj_ansi_strlen(c_url) : 0); - - if (!len) return -1; - - pool = pj_pool_create(&pjsua.cp.factory, "check%p", 1024, 0, NULL); - if (!pool) return -1; - - url = pj_pool_alloc(pool, len+1); - pj_ansi_strcpy(url, c_url); - - p = pjsip_parse_uri(pool, url, len, 0); - if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) - p = NULL; - - pj_pool_release(pool); - return p ? 0 : -1; -} - - -/* - * Read command arguments from config file. - */ -static int read_config_file(pj_pool_t *pool, const char *filename, - int *app_argc, char ***app_argv) -{ - int i; - FILE *fhnd; - char line[200]; - int argc = 0; - char **argv; - enum { MAX_ARGS = 64 }; - - /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ - argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); - argv[argc++] = *app_argv[0]; - - /* Open config file. */ - fhnd = fopen(filename, "rt"); - if (!fhnd) { - printf("Unable to open config file %s\n", filename); - fflush(stdout); - return -1; - } - - /* Scan tokens in the file. */ - while (argc < MAX_ARGS && !feof(fhnd)) { - char *token, *p = line; - - if (fgets(line, sizeof(line), fhnd) == NULL) break; - - for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; - token = strtok(NULL, " \t\r\n")) - { - int token_len; - - if (!token) break; - if (*token == '#') break; - - token_len = strlen(token); - if (!token_len) - continue; - argv[argc] = pj_pool_alloc(pool, token_len+1); - pj_memcpy(argv[argc], token, token_len+1); - ++argc; - } - } - - /* Copy arguments from command line */ - for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) - argv[argc++] = (*app_argv)[i]; - - if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { - printf("Too many arguments specified in cmd line/config file\n"); - fflush(stdout); - fclose(fhnd); - return -1; - } - - fclose(fhnd); - - /* Assign the new command line back to the original command line. */ - *app_argc = argc; - *app_argv = argv; - return 0; - -} - - -/* Parse arguments. */ -pj_status_t pjsua_parse_args(int argc, char *argv[]) -{ - int c; - int option_index; - enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, - OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, - OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, - OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, - OPT_REALM, OPT_USERNAME, OPT_PASSWORD, - OPT_USE_STUN1, OPT_USE_STUN2, - OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, - OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP, - OPT_AUTO_CONF, - OPT_PLAY_FILE, - OPT_NEXT_ACCOUNT, OPT_NEXT_CRED, OPT_MAX_CALLS, - }; - struct option long_options[] = { - { "config-file",1, 0, OPT_CONFIG_FILE}, - { "log-file", 1, 0, OPT_LOG_FILE}, - { "log-level", 1, 0, OPT_LOG_LEVEL}, - { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, - { "help", 0, 0, OPT_HELP}, - { "version", 0, 0, OPT_VERSION}, - { "null-audio", 0, 0, OPT_NULL_AUDIO}, - { "local-port", 1, 0, OPT_LOCAL_PORT}, - { "proxy", 1, 0, OPT_PROXY}, - { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, - { "registrar", 1, 0, OPT_REGISTRAR}, - { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, - { "id", 1, 0, OPT_ID}, - { "contact", 1, 0, OPT_CONTACT}, - { "realm", 1, 0, OPT_REALM}, - { "username", 1, 0, OPT_USERNAME}, - { "password", 1, 0, OPT_PASSWORD}, - { "use-stun1", 1, 0, OPT_USE_STUN1}, - { "use-stun2", 1, 0, OPT_USE_STUN2}, - { "add-buddy", 1, 0, OPT_ADD_BUDDY}, - { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, - { "no-presence", 0, 0, OPT_NO_PRESENCE}, - { "auto-answer",1, 0, OPT_AUTO_ANSWER}, - { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, - { "auto-play", 0, 0, OPT_AUTO_PLAY}, - { "auto-loop", 0, 0, OPT_AUTO_LOOP}, - { "auto-conf", 0, 0, OPT_AUTO_CONF}, - { "play-file", 1, 0, OPT_PLAY_FILE}, - { "next-account",0,0, OPT_NEXT_ACCOUNT}, - { "next-cred", 0, 0, OPT_NEXT_CRED}, - { "max-calls", 1, 0, OPT_MAX_CALLS}, - { NULL, 0, 0, 0} - }; - pj_status_t status; - pjsua_acc *cur_acc; - pjsip_cred_info *cur_cred; - char *config_file = NULL; - - /* Run getopt once to see if user specifies config file to read. */ - while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { - switch (c) { - case OPT_CONFIG_FILE: - config_file = optarg; - break; - } - if (config_file) - break; - } - - if (config_file) { - status = read_config_file(pjsua.pool, config_file, &argc, &argv); - if (status != 0) - return status; - } - - - cur_acc = &pjsua.acc[0]; - cur_cred = &pjsua.cred_info[0]; - - - /* Reinitialize and re-run getopt again, possibly with new arguments - * read from config file. - */ - optind = 0; - while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { - char *p; - pj_str_t tmp; - long lval; - - switch (c) { - - case OPT_LOG_FILE: - pjsua.log_filename = optarg; - break; - - case OPT_LOG_LEVEL: - c = pj_strtoul(pj_cstr(&tmp, optarg)); - if (c < 0 || c > 6) { - printf("Error: expecting integer value 0-6 for --log-level\n"); - return PJ_EINVAL; - } - pj_log_set_level( c ); - break; - - case OPT_APP_LOG_LEVEL: - pjsua.app_log_level = pj_strtoul(pj_cstr(&tmp, optarg)); - if (pjsua.app_log_level < 0 || pjsua.app_log_level > 6) { - printf("Error: expecting integer value 0-6 for --app-log-level\n"); - return PJ_EINVAL; - } - break; - - case OPT_HELP: - usage(); - return PJ_EINVAL; - - case OPT_VERSION: /* version */ - pj_dump_config(); - return PJ_EINVAL; - - case OPT_NULL_AUDIO: - pjsua.null_audio = 1; - break; - - case OPT_LOCAL_PORT: /* local-port */ - lval = pj_strtoul(pj_cstr(&tmp, optarg)); - if (lval < 1 || lval > 65535) { - printf("Error: expecting integer value for --local-port\n"); - return PJ_EINVAL; - } - pjsua.sip_port = (pj_uint16_t)lval; - break; - - case OPT_PROXY: /* proxy */ - if (pjsua_verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); - return PJ_EINVAL; - } - cur_acc->proxy = pj_str(optarg); - break; - - case OPT_OUTBOUND_PROXY: /* outbound proxy */ - if (pjsua_verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); - return PJ_EINVAL; - } - pjsua.outbound_proxy = pj_str(optarg); - break; - - case OPT_REGISTRAR: /* registrar */ - if (pjsua_verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); - return PJ_EINVAL; - } - cur_acc->reg_uri = pj_str(optarg); - break; - - case OPT_REG_TIMEOUT: /* reg-timeout */ - cur_acc->reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg)); - if (cur_acc->reg_timeout < 1 || cur_acc->reg_timeout > 3600) { - printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n"); - return PJ_EINVAL; - } - break; - - case OPT_ID: /* id */ - if (pjsua_verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); - return PJ_EINVAL; - } - cur_acc->local_uri = pj_str(optarg); - break; - - case OPT_CONTACT: /* contact */ - if (pjsua_verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); - return PJ_EINVAL; - } - cur_acc->contact_uri = pj_str(optarg); - break; - - case OPT_NEXT_ACCOUNT: /* Add more account. */ - pjsua.acc_cnt++; - cur_acc = &pjsua.acc[pjsua.acc_cnt - 1]; - break; - - case OPT_USERNAME: /* Default authentication user */ - if (pjsua.cred_count==0) pjsua.cred_count=1; - cur_cred->username = pj_str(optarg); - break; - - case OPT_REALM: /* Default authentication realm. */ - if (pjsua.cred_count==0) pjsua.cred_count=1; - cur_cred->realm = pj_str(optarg); - break; - - case OPT_PASSWORD: /* authentication password */ - if (pjsua.cred_count==0) pjsua.cred_count=1; - cur_cred->data_type = 0; - cur_cred->data = pj_str(optarg); - break; - - case OPT_NEXT_CRED: /* Next credential */ - pjsua.cred_count++; - cur_cred = &pjsua.cred_info[pjsua.cred_count - 1]; - break; - - case OPT_USE_STUN1: /* STUN server 1 */ - p = pj_ansi_strchr(optarg, ':'); - if (p) { - *p = '\0'; - pjsua.stun_srv1 = pj_str(optarg); - pjsua.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1)); - if (pjsua.stun_port1 < 1 || pjsua.stun_port1 > 65535) { - printf("Error: expecting port number with option --use-stun1\n"); - return PJ_EINVAL; - } - } else { - pjsua.stun_port1 = 3478; - pjsua.stun_srv1 = pj_str(optarg); - } - break; - - case OPT_USE_STUN2: /* STUN server 2 */ - p = pj_ansi_strchr(optarg, ':'); - if (p) { - *p = '\0'; - pjsua.stun_srv2 = pj_str(optarg); - pjsua.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1)); - if (pjsua.stun_port2 < 1 || pjsua.stun_port2 > 65535) { - printf("Error: expecting port number with option --use-stun2\n"); - return PJ_EINVAL; - } - } else { - pjsua.stun_port2 = 3478; - pjsua.stun_srv2 = pj_str(optarg); - } - break; - - case OPT_ADD_BUDDY: /* Add to buddy list. */ - if (pjsua_verify_sip_url(optarg) != 0) { - printf("Error: invalid URL '%s' in --add-buddy option\n", optarg); - return -1; - } - if (pjsua.buddy_cnt == PJSUA_MAX_BUDDIES) { - printf("Error: too many buddies in buddy list.\n"); - return -1; - } - pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(optarg); - break; - - case OPT_AUTO_PLAY: - pjsua.auto_play = 1; - break; - - case OPT_AUTO_LOOP: - pjsua.auto_loop = 1; - break; - - case OPT_AUTO_CONF: - pjsua.auto_conf = 1; - break; - - case OPT_PLAY_FILE: - pjsua.wav_file = optarg; - break; - - case OPT_AUTO_ANSWER: - pjsua.auto_answer = atoi(optarg); - if (pjsua.auto_answer < 100 || pjsua.auto_answer > 699) { - puts("Error: invalid code in --auto-answer (expecting 100-699"); - return -1; - } - break; - - case OPT_MAX_CALLS: - pjsua.max_calls = atoi(optarg); - if (pjsua.max_calls < 1 || pjsua.max_calls > 255) { - puts("Too many calls for max-calls (1-255)"); - return -1; - } - break; - } - } - - if (optind != argc) { - printf("Error: unknown options %s\n", argv[optind]); - return PJ_EINVAL; - } - - return PJ_SUCCESS; -} - - - -static void print_call(const char *title, - int call_index, - char *buf, pj_size_t size) -{ - int len; - pjsip_inv_session *inv = pjsua.calls[call_index].inv; - pjsip_dialog *dlg = inv->dlg; - char userinfo[128]; - - /* Dump invite sesion info. */ - - len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); - if (len < 1) - pj_ansi_strcpy(userinfo, "<--uri too long-->"); - else - userinfo[len] = '\0'; - - len = pj_snprintf(buf, size, "%s[%s] %s", - title, - pjsua_inv_state_names[inv->state], - userinfo); - if (len < 1 || len >= (int)size) { - pj_ansi_strcpy(buf, "<--uri too long-->"); - len = 18; - } else - buf[len] = '\0'; -} - -static void dump_media_session(pjmedia_session *session) -{ - unsigned i; - pjmedia_session_info info; - - pjmedia_session_get_info(session, &info); - - for (i=0; ilocal_uri.slen) { - pj_ansi_sprintf(line, "--id %.*s\n", - (int)acc->local_uri.slen, - acc->local_uri.ptr); - pj_strcat2(result, line); - } - - /* Registrar server */ - if (acc->reg_uri.slen) { - pj_ansi_sprintf(line, "--registrar %.*s\n", - (int)acc->reg_uri.slen, - acc->reg_uri.ptr); - pj_strcat2(result, line); - - pj_ansi_sprintf(line, "--reg-timeout %u\n", - acc->reg_timeout); - pj_strcat2(result, line); - } - - - /* Proxy */ - if (acc->proxy.slen) { - pj_ansi_sprintf(line, "--proxy %.*s\n", - (int)acc->proxy.slen, - acc->proxy.ptr); - pj_strcat2(result, line); - } -} - - - -/* - * Dump settings. - */ -int pjsua_dump_settings(char *buf, pj_size_t max) -{ - int acc_index; - int i; - pj_str_t cfg; - char line[128]; - - cfg.ptr = buf; - cfg.slen = 0; - - - /* Logging. */ - pj_strcat2(&cfg, "#\n# Logging options:\n#\n"); - pj_ansi_sprintf(line, "--log-level %d\n", - pjsua.log_level); - pj_strcat2(&cfg, line); - - pj_ansi_sprintf(line, "--app-log-level %d\n", - pjsua.app_log_level); - pj_strcat2(&cfg, line); - - if (pjsua.log_filename) { - pj_ansi_sprintf(line, "--log-file %s\n", - pjsua.log_filename); - pj_strcat2(&cfg, line); - } - - - /* Save account settings. */ - for (acc_index=0; acc_index < pjsua.acc_cnt; ++acc_index) { - - save_account_settings(acc_index, &cfg); - - if (acc_index < pjsua.acc_cnt-1) - pj_strcat2(&cfg, "--next-account\n"); - } - - /* Credentials. */ - for (i=0; i - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "pjsua.h" - -/* - * pjsua_pres.c - * - * Presence related stuffs. - */ - -#define THIS_FILE "pjsua_pres.c" - - - -/* ************************************************************************** - * THE FOLLOWING PART HANDLES SERVER SUBSCRIPTION - * ************************************************************************** - */ - -/* Proto */ -static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata); - -/* The module instance. */ -static pjsip_module mod_pjsua_pres = -{ - NULL, NULL, /* prev, next. */ - { "mod-pjsua-pres", 14 }, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ - NULL, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - NULL, /* unload() */ - &pres_on_rx_request, /* on_rx_request() */ - NULL, /* on_rx_response() */ - NULL, /* on_tx_request. */ - NULL, /* on_tx_response() */ - NULL, /* on_tsx_state() */ - -}; - - -/* Callback called when *server* subscription state has changed. */ -static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event) -{ - pjsua_srv_pres *uapres = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); - - PJ_UNUSED_ARG(event); - - if (uapres) { - PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s", - uapres->remote, pjsip_evsub_get_state_name(sub))); - - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); - pj_list_erase(uapres); - } - } -} - -/* This is called when request is received. - * We need to check for incoming SUBSCRIBE request. - */ -static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) -{ - int acc_index; - pjsip_method *req_method = &rdata->msg_info.msg->line.req.method; - pjsua_srv_pres *uapres; - pjsip_evsub *sub; - pjsip_evsub_user pres_cb; - pjsip_tx_data *tdata; - pjsip_pres_status pres_status; - pjsip_dialog *dlg; - pj_status_t status; - - if (pjsip_method_cmp(req_method, &pjsip_subscribe_method) != 0) - return PJ_FALSE; - - /* Incoming SUBSCRIBE: */ - - /* Find which account for the incoming request. */ - acc_index = pjsua_find_account_for_incoming(rdata); - - /* Create UAS dialog: */ - status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, - &pjsua.acc[acc_index].contact_uri, - &dlg); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to create UAS dialog for subscription", - status); - return PJ_FALSE; - } - - /* Init callback: */ - pj_memset(&pres_cb, 0, sizeof(pres_cb)); - pres_cb.on_evsub_state = &pres_evsub_on_srv_state; - - /* Create server presence subscription: */ - status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub); - if (status != PJ_SUCCESS) { - PJ_TODO(DESTROY_DIALOG); - pjsua_perror(THIS_FILE, "Unable to create server subscription", - status); - return PJ_FALSE; - } - - /* Attach our data to the subscription: */ - uapres = pj_pool_alloc(dlg->pool, sizeof(pjsua_srv_pres)); - uapres->sub = sub; - uapres->remote = pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE); - status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri, - uapres->remote, PJSIP_MAX_URL_SIZE); - if (status < 1) - pj_ansi_strcpy(uapres->remote, "<-- url is too long-->"); - else - uapres->remote[status] = '\0'; - - pjsip_evsub_set_mod_data(sub, pjsua.mod.id, uapres); - - /* Add server subscription to the list: */ - pj_list_push_back(&pjsua.acc[acc_index].pres_srv_list, uapres); - - - /* Create and send 200 (OK) to the SUBSCRIBE request: */ - status = pjsip_pres_accept(sub, rdata, 200, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to accept presence subscription", - status); - pj_list_erase(uapres); - return PJ_FALSE; - } - - - /* Set our online status: */ - pj_memset(&pres_status, 0, sizeof(pres_status)); - pres_status.info_cnt = 1; - pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status; - //Both pjsua.local_uri and pjsua.contact_uri are enclosed in "<" and ">" - //causing XML parsing to fail. - //pres_status.info[0].contact = pjsua.local_uri; - - pjsip_pres_set_status(sub, &pres_status); - - /* Create and send the first NOTIFY to active subscription: */ - status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, NULL, - NULL, &tdata); - if (status == PJ_SUCCESS) - status = pjsip_pres_send_request( sub, tdata); - - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY", - status); - pj_list_erase(uapres); - return PJ_FALSE; - } - - - /* Done: */ - - return PJ_TRUE; -} - - -/* Refresh subscription (e.g. when our online status has changed) */ -static void refresh_server_subscription(int acc_index) -{ - pjsua_srv_pres *uapres; - - uapres = pjsua.acc[acc_index].pres_srv_list.next; - - while (uapres != &pjsua.acc[acc_index].pres_srv_list) { - - pjsip_pres_status pres_status; - pjsip_tx_data *tdata; - - pjsip_pres_get_status(uapres->sub, &pres_status); - if (pres_status.info[0].basic_open != pjsua.acc[acc_index].online_status) { - pres_status.info[0].basic_open = pjsua.acc[acc_index].online_status; - pjsip_pres_set_status(uapres->sub, &pres_status); - - if (pjsua.quit_flag) { - pj_str_t reason = { "noresource", 10 }; - if (pjsip_pres_notify(uapres->sub, - PJSIP_EVSUB_STATE_TERMINATED, NULL, - &reason, &tdata)==PJ_SUCCESS) - { - pjsip_pres_send_request(uapres->sub, tdata); - } - } else { - if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) - pjsip_pres_send_request(uapres->sub, tdata); - } - } - - uapres = uapres->next; - } -} - - - -/* ************************************************************************** - * THE FOLLOWING PART HANDLES CLIENT SUBSCRIPTION - * ************************************************************************** - */ - -/* Callback called when *client* subscription state has changed. */ -static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event) -{ - pjsua_buddy *buddy; - - PJ_UNUSED_ARG(event); - - buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); - if (buddy) { - PJ_LOG(3,(THIS_FILE, - "Presence subscription to %s is %s", - buddy->uri.ptr, - pjsip_evsub_get_state_name(sub))); - - if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { - buddy->sub = NULL; - buddy->status.info_cnt = 0; - pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); - } - } -} - -/* Callback called when we receive NOTIFY */ -static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub, - pjsip_rx_data *rdata, - int *p_st_code, - pj_str_t **p_st_text, - pjsip_hdr *res_hdr, - pjsip_msg_body **p_body) -{ - pjsua_buddy *buddy; - - buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); - if (buddy) { - /* Update our info. */ - pjsip_pres_get_status(sub, &buddy->status); - - if (buddy->status.info_cnt) { - PJ_LOG(3,(THIS_FILE, "%s is %s", - buddy->uri.ptr, - (buddy->status.info[0].basic_open?"online":"offline"))); - } else { - PJ_LOG(3,(THIS_FILE, "No presence info for %s", - buddy->uri.ptr)); - } - } - - /* The default is to send 200 response to NOTIFY. - * Just leave it there.. - */ - PJ_UNUSED_ARG(rdata); - PJ_UNUSED_ARG(p_st_code); - PJ_UNUSED_ARG(p_st_text); - PJ_UNUSED_ARG(res_hdr); - PJ_UNUSED_ARG(p_body); -} - - -/* Event subscription callback. */ -static pjsip_evsub_user pres_callback = -{ - &pjsua_evsub_on_state, - - NULL, /* on_tsx_state: don't care about transaction state. */ - - NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless - * we want to authenticate - */ - - &pjsua_evsub_on_rx_notify, - - NULL, /* on_client_refresh: Use default behaviour, which is to - * refresh client subscription. */ - - NULL, /* on_server_timeout: Use default behaviour, which is to send - * NOTIFY to terminate. - */ -}; - - -/* It does what it says.. */ -static void subscribe_buddy_presence(unsigned index) -{ - int acc_index; - pjsip_dialog *dlg; - pjsip_tx_data *tdata; - pj_status_t status; - - acc_index = pjsua.buddies[index].acc_index; - - status = pjsip_dlg_create_uac( pjsip_ua_instance(), - &pjsua.acc[acc_index].local_uri, - &pjsua.acc[acc_index].contact_uri, - &pjsua.buddies[index].uri, - NULL, &dlg); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create dialog", - status); - return; - } - - status = pjsip_pres_create_uac( dlg, &pres_callback, - &pjsua.buddies[index].sub); - if (status != PJ_SUCCESS) { - pjsua.buddies[index].sub = NULL; - pjsua_perror(THIS_FILE, "Unable to create presence client", - status); - return; - } - - pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id, - &pjsua.buddies[index]); - - status = pjsip_pres_initiate(pjsua.buddies[index].sub, -1, &tdata); - if (status != PJ_SUCCESS) { - pjsua.buddies[index].sub = NULL; - pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE", - status); - return; - } - - status = pjsip_pres_send_request(pjsua.buddies[index].sub, tdata); - if (status != PJ_SUCCESS) { - pjsua.buddies[index].sub = NULL; - pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE", - status); - return; - } - - PJ_TODO(DESTROY_DIALOG_ON_ERROR); -} - - -/* It does what it says... */ -static void unsubscribe_buddy_presence(unsigned index) -{ - pjsip_tx_data *tdata; - pj_status_t status; - - if (pjsua.buddies[index].sub == NULL) - return; - - if (pjsip_evsub_get_state(pjsua.buddies[index].sub) == - PJSIP_EVSUB_STATE_TERMINATED) - { - pjsua.buddies[index].sub = NULL; - return; - } - - status = pjsip_pres_initiate( pjsua.buddies[index].sub, 0, &tdata); - if (status == PJ_SUCCESS) - status = pjsip_pres_send_request( pjsua.buddies[index].sub, tdata ); - - if (status == PJ_SUCCESS) { - - //pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id, - // NULL); - //pjsua.buddies[index].sub = NULL; - - } else { - pjsua_perror(THIS_FILE, "Unable to unsubscribe presence", - status); - } -} - - -/* It does what it says.. */ -static void refresh_client_subscription(void) -{ - int i; - - for (i=0; isub), - uapres->remote)); - - uapres = uapres->next; - } - } - } - - PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:")); - if (pjsua.buddy_cnt == 0) { - PJ_LOG(3,(THIS_FILE, " - no buddy list - ")); - } else { - for (i=0; i - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include "pjsua.h" - - -/* - * pjsua_reg.c - * - * Client registration handler. - */ - -#define THIS_FILE "pjsua_reg.c" - - -/* - * This callback is called by pjsip_regc when outgoing register - * request has completed. - */ -static void regc_cb(struct pjsip_regc_cbparam *param) -{ - - pjsua_acc *acc = param->token; - - /* - * Print registration status. - */ - if (param->status!=PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "SIP registration error", - param->status); - pjsip_regc_destroy(acc->regc); - acc->regc = NULL; - - } else if (param->code < 0 || param->code >= 300) { - PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)", - param->code, - pjsip_get_status_text(param->code)->ptr)); - pjsip_regc_destroy(acc->regc); - acc->regc = NULL; - - } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { - - if (param->expiration < 1) { - pjsip_regc_destroy(acc->regc); - acc->regc = NULL; - PJ_LOG(3,(THIS_FILE, "%s: unregistration success", - acc->local_uri.ptr)); - } else { - PJ_LOG(3, (THIS_FILE, - "%s: registration success, status=%d (%s), " - "will re-register in %d seconds", - acc->local_uri.ptr, - param->code, - pjsip_get_status_text(param->code)->ptr, - param->expiration)); - } - - } else { - PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code)); - } - - acc->reg_last_err = param->status; - acc->reg_last_code = param->code; - - pjsua_ui_regc_on_state_changed(acc->index); -} - - -/* - * Update registration. If renew is false, then unregistration will be performed. - */ -void pjsua_regc_update(int acc_index, pj_bool_t renew) -{ - pj_status_t status; - pjsip_tx_data *tdata; - - if (renew) { - if (pjsua.acc[acc_index].regc == NULL) { - status = pjsua_regc_init(acc_index); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create registration", - status); - return; - } - } - status = pjsip_regc_register(pjsua.acc[acc_index].regc, 1, &tdata); - } else { - if (pjsua.acc[acc_index].regc == NULL) { - PJ_LOG(3,(THIS_FILE, "Currently not registered")); - return; - } - status = pjsip_regc_unregister(pjsua.acc[acc_index].regc, &tdata); - } - - if (status == PJ_SUCCESS) - status = pjsip_regc_send( pjsua.acc[acc_index].regc, tdata ); - - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create/send REGISTER", - status); - } else { - PJ_LOG(3,(THIS_FILE, "%s sent", - (renew? "Registration" : "Unregistration"))); - } -} - -/* - * Initialize client registration. - */ -pj_status_t pjsua_regc_init(int acc_index) -{ - pj_status_t status; - - /* initialize SIP registration if registrar is configured */ - if (pjsua.acc[acc_index].reg_uri.slen) { - - status = pjsip_regc_create( pjsua.endpt, - &pjsua.acc[acc_index], - ®c_cb, - &pjsua.acc[acc_index].regc); - - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, "Unable to create client registration", - status); - return status; - } - - - status = pjsip_regc_init( pjsua.acc[acc_index].regc, - &pjsua.acc[acc_index].reg_uri, - &pjsua.acc[acc_index].local_uri, - &pjsua.acc[acc_index].local_uri, - 1, &pjsua.acc[acc_index].contact_uri, - pjsua.acc[acc_index].reg_timeout); - if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Client registration initialization error", - status); - return status; - } - - pjsip_regc_set_credentials( pjsua.acc[acc_index].regc, - pjsua.cred_count, - pjsua.cred_info ); - - pjsip_regc_set_route_set( pjsua.acc[acc_index].regc, - &pjsua.acc[acc_index].route_set ); - } - - return PJ_SUCCESS; -} - -- cgit v1.2.3