1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-09-10 01:29:38 +02:00

Jingle Extension added to Smack Repository

git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@6517 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Thiago Camargo 2007-01-04 17:25:30 +00:00 committed by thiago
parent f1972c2571
commit 4b6de6647b
104 changed files with 16411 additions and 0 deletions

View file

@ -0,0 +1 @@
jdk.home.1.5=C:/Arquivos de programas/Java/jdk1.5.0_09

View file

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="jingle" default="all">
<property file="jingle.properties"/>
<!-- Uncomment the following property if no tests compilation is needed -->
<!--
<property name="skip.tests" value="true"/>
-->
<!-- Compiler options -->
<property name="compiler.debug" value="on"/>
<property name="compiler.generate.no.warnings" value="off"/>
<property name="compiler.args" value=""/>
<property name="compiler.max.memory" value="128m"/>
<patternset id="ignored.files">
<exclude name="**/CVS/**"/>
<exclude name="**/SCCS/**"/>
<exclude name="**/RCS/**"/>
<exclude name="**/rcs/**"/>
<exclude name="**/.DS_Store/**"/>
<exclude name="**/.svn/**"/>
<exclude name="**/vssver.scc/**"/>
<exclude name="**/vssver2.scc/**"/>
</patternset>
<patternset id="compiler.resources">
<include name="**/?*.properties"/>
<include name="**/?*.xml"/>
<include name="**/?*.html"/>
<include name="**/?*.dtd"/>
<include name="**/?*.tld"/>
<include name="**/?*.gif"/>
<include name="**/?*.png"/>
<include name="**/?*.jpeg"/>
<include name="**/?*.jpg"/>
</patternset>
<!-- JDK definitions -->
<property name="jdk.bin.1.5" value="${jdk.home.1.5}/bin"/>
<path id="jdk.classpath.1.5">
<fileset dir="${jdk.home.1.5}">
<include name="jre/lib/charsets.jar"/>
<include name="jre/lib/deploy.jar"/>
<include name="jre/lib/javaws.jar"/>
<include name="jre/lib/jce.jar"/>
<include name="jre/lib/jsse.jar"/>
<include name="jre/lib/plugin.jar"/>
<include name="jre/lib/rt.jar"/>
<include name="jre/lib/ext/dnsns.jar"/>
<include name="jre/lib/ext/localedata.jar"/>
<include name="jre/lib/ext/sunjce_provider.jar"/>
<include name="jre/lib/ext/sunpkcs11.jar"/>
</fileset>
</path>
<property name="project.jdk.home" value="${jdk.home.1.5}"/>
<property name="project.jdk.bin" value="${jdk.bin.1.5}"/>
<property name="project.jdk.classpath" value="jdk.classpath.1.5"/>
<!-- Project Libraries -->
<!-- Modules -->
<!-- Module JingleExtension -->
<dirname property="module.jingleextension.basedir" file="${ant.file}"/>
<property name="module.jdk.home.jingleextension" value="${project.jdk.home}"/>
<property name="module.jdk.bin.jingleextension" value="${project.jdk.bin}"/>
<property name="module.jdk.classpath.jingleextension" value="${project.jdk.classpath}"/>
<property name="compiler.args.jingleextension" value="${compiler.args}"/>
<property name="jingleextension.output.dir" value="${module.jingleextension.basedir}/../../classes"/>
<property name="jingleextension.testoutput.dir" value="${module.jingleextension.basedir}/../../classes"/>
<path id="jingleextension.module.bootclasspath">
<!-- Paths to be included in compilation bootclasspath -->
</path>
<path id="jingleextension.module.classpath">
<path refid="${module.jdk.classpath.jingleextension}"/>
<pathelement location="${module.jingleextension.basedir}/../merge/jstun-0.6.1.jar"/>
<pathelement location="${module.jingleextension.basedir}/../lib/smackx.jar"/>
<pathelement location="${module.jingleextension.basedir}/../lib/smack.jar"/>
<pathelement location="${module.jingleextension.basedir}/../lib/smackx-debug.jar"/>
<pathelement location="${module.jingleextension.basedir}/../lib/junit.jar"/>
</path>
<patternset id="excluded.from.module.jingleextension">
<patternset refid="ignored.files"/>
</patternset>
<patternset id="excluded.from.compilation.jingleextension">
<patternset refid="excluded.from.module.jingleextension"/>
</patternset>
<path id="jingleextension.module.sourcepath">
<dirset dir="${module.jingleextension.basedir}/../../../jingle-extension">
<include name="source"/>
</dirset>
</path>
<path id="jingleextension.module.test.sourcepath">
<dirset dir="${module.jingleextension.basedir}/../../../jingle-extension">
<include name="test"/>
</dirset>
</path>
<target name="compile.module.jingleextension" depends="compile.module.jingleextension.production,compile.module.jingleextension.tests" description="Compile module JingleExtension"/>
<target name="compile.module.jingleextension.production" description="Compile module JingleExtension; production classes">
<mkdir dir="${jingleextension.output.dir}"/>
<javac destdir="${jingleextension.output.dir}" debug="${compiler.debug}" nowarn="${compiler.generate.no.warnings}" memorymaximumsize="${compiler.max.memory}" fork="true" executable="${module.jdk.bin.jingleextension}/javac">
<compilerarg line="${compiler.args.jingleextension}"/>
<bootclasspath refid="jingleextension.module.bootclasspath"/>
<classpath refid="jingleextension.module.classpath"/>
<src refid="jingleextension.module.sourcepath"/>
<patternset refid="excluded.from.compilation.jingleextension"/>
</javac>
<copy todir="${jingleextension.output.dir}">
<fileset dir="${module.jingleextension.basedir}/../../source">
<patternset refid="compiler.resources"/>
<type type="file"/>
</fileset>
</copy>
</target>
<target name="compile.module.jingleextension.tests" depends="compile.module.jingleextension.production" description="compile module JingleExtension; test classes" unless="skip.tests">
<mkdir dir="${jingleextension.testoutput.dir}"/>
<javac destdir="${jingleextension.testoutput.dir}" debug="${compiler.debug}" nowarn="${compiler.generate.no.warnings}" memorymaximumsize="${compiler.max.memory}" fork="true" executable="${module.jdk.bin.jingleextension}/javac">
<compilerarg line="${compiler.args.jingleextension}"/>
<classpath refid="jingleextension.module.classpath"/>
<classpath location="${jingleextension.output.dir}"/>
<src refid="jingleextension.module.test.sourcepath"/>
<patternset refid="excluded.from.compilation.jingleextension"/>
</javac>
<copy todir="${jingleextension.testoutput.dir}">
<fileset dir="${module.jingleextension.basedir}/../../test">
<patternset refid="compiler.resources"/>
<type type="file"/>
</fileset>
</copy>
</target>
<target name="clean.module.jingleextension" description="cleanup module">
<delete dir="${jingleextension.output.dir}"/>
<delete dir="${jingleextension.testoutput.dir}"/>
</target>
<target name="init" description="Build initialization">
<!-- Perform any build initialization in this target -->
</target>
<target name="clean" depends="clean.module.jingleextension" description="cleanup all"/>
<target name="all" depends="init, clean, compile.module.jingleextension" description="build all"/>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,315 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4" relativePaths="true">
<component name="AntConfiguration">
<defaultAnt bundledAnt="true" />
<buildFile url="file://$PROJECT_DIR$/../build.xml">
<additionalClassPath>
<entry path="file://$PROJECT_DIR$/../junit.jar" />
</additionalClassPath>
<antReference projectDefault="true" />
<customJdkName value="" />
<maximumHeapSize value="128" />
<properties />
</buildFile>
</component>
<component name="BuildJarProjectSettings">
<option name="BUILD_JARS_ON_MAKE" value="false" />
</component>
<component name="CodeStyleProjectProfileManger">
<option name="PROJECT_PROFILE" />
<option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
</component>
<component name="CodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="WHILE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="RIGHT_MARGIN" value="100" />
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<option name="DEPLOY_AFTER_MAKE" value="0" />
<resourceExtensions>
<entry name=".+\.(properties|xml|html|dtd|tld)" />
<entry name=".+\.(gif|png|jpeg|jpg)" />
</resourceExtensions>
<wildcardResourcePatterns>
<entry name="?*.properties" />
<entry name="?*.xml" />
<entry name="?*.html" />
<entry name="?*.dtd" />
<entry name="?*.tld" />
<entry name="?*.gif" />
<entry name="?*.png" />
<entry name="?*.jpeg" />
<entry name="?*.jpg" />
</wildcardResourcePatterns>
</component>
<component name="DataSourceManagerImpl" />
<component name="DependenciesAnalyzeManager">
<option name="myForwardDirection" value="false" />
</component>
<component name="DependencyValidationManager" />
<component name="EclipseCompilerSettings">
<option name="DEBUGGING_INFO" value="true" />
<option name="GENERATE_NO_WARNINGS" value="true" />
<option name="DEPRECATION" value="false" />
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
<option name="MAXIMUM_HEAP_SIZE" value="128" />
</component>
<component name="EclipseEmbeddedCompilerSettings">
<option name="DEBUGGING_INFO" value="true" />
<option name="GENERATE_NO_WARNINGS" value="true" />
<option name="DEPRECATION" value="false" />
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
<option name="MAXIMUM_HEAP_SIZE" value="128" />
</component>
<component name="EntryPointsManager">
<entry_points />
</component>
<component name="ExportToHTMLSettings">
<option name="PRINT_LINE_NUMBERS" value="false" />
<option name="OPEN_IN_BROWSER" value="false" />
<option name="OUTPUT_DIRECTORY" />
</component>
<component name="GUI Designer component loader factory" />
<component name="IdProvider" IDEtalkID="4BDA8DF7DEE8CF1D6F91D577895A2C5C" />
<component name="InspectionProjectProfileManager">
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
<scopes />
<profiles>
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<used_levels>
<error>
<option name="myName" value="ERROR" />
<option name="myVal" value="400" />
</error>
<warning>
<option name="myName" value="WARNING" />
<option name="myVal" value="300" />
</warning>
<information>
<option name="myName" value="INFO" />
<option name="myVal" value="200" />
</information>
<server>
<option name="myName" value="SERVER PROBLEM" />
<option name="myVal" value="100" />
</server>
</used_levels>
</profile>
</profiles>
</component>
<component name="JavacSettings">
<option name="DEBUGGING_INFO" value="true" />
<option name="GENERATE_NO_WARNINGS" value="false" />
<option name="DEPRECATION" value="true" />
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
<option name="MAXIMUM_HEAP_SIZE" value="128" />
</component>
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY" value="$PROJECT_DIR$/../../javadocs" />
<option name="OPTION_SCOPE" value="package" />
<option name="OPTION_HIERARCHY" value="true" />
<option name="OPTION_NAVIGATOR" value="true" />
<option name="OPTION_INDEX" value="true" />
<option name="OPTION_SEPARATE_INDEX" value="true" />
<option name="OPTION_DOCUMENT_TAG_USE" value="false" />
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
<option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
<option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
<option name="OPTION_DEPRECATED_LIST" value="true" />
<option name="OTHER_OPTIONS" />
<option name="HEAP_SIZE" />
<option name="LOCALE" />
<option name="OPEN_IN_BROWSER" value="true" />
</component>
<component name="JikesSettings">
<option name="JIKES_PATH" value="" />
<option name="DEBUGGING_INFO" value="true" />
<option name="DEPRECATION" value="true" />
<option name="GENERATE_NO_WARNINGS" value="false" />
<option name="IS_EMACS_ERRORS_MODE" value="true" />
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
</component>
<component name="LogConsolePreferences">
<option name="FILTER_ERRORS" value="false" />
<option name="FILTER_WARNINGS" value="false" />
<option name="FILTER_INFO" value="true" />
<option name="CUSTOM_FILTER" />
</component>
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/JingleExtension.iml" filepath="$PROJECT_DIR$/JingleExtension.iml" />
</modules>
</component>
<component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" />
<component name="ProjectRunConfigurationManager" />
<component name="RmicSettings">
<option name="IS_EANABLED" value="false" />
<option name="DEBUGGING_INFO" value="true" />
<option name="GENERATE_NO_WARNINGS" value="false" />
<option name="GENERATE_IIOP_STUBS" value="false" />
<option name="ADDITIONAL_OPTIONS_STRING" value="" />
</component>
<component name="StarteamVcsAdapter" />
<component name="VssVcs" />
<component name="com.intellij.jsf.UserDefinedFacesConfigs">
<option name="USER_DEFINED_CONFIGS">
<value>
<list size="0" />
</value>
</option>
</component>
<component name="libraryTable">
<library name="Smack">
<CLASSES>
<root url="jar://$PROJECT_DIR$/../lib/jcert.jar!/" />
<root url="jar://$PROJECT_DIR$/../lib/jnet.jar!/" />
<root url="jar://$PROJECT_DIR$/../lib/jsse.jar!/" />
<root url="jar://$PROJECT_DIR$/../merge/xpp.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>
<component name="uidesigner-configuration">
<option name="INSTRUMENT_CLASSES" value="true" />
<option name="COPY_FORMS_RUNTIME_TO_OUTPUT" value="true" />
<option name="DEFAULT_LAYOUT_MANAGER" value="GridLayoutManager" />
</component>
<UsedPathMacros />
</project>

View file

@ -0,0 +1,635 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4" relativePaths="true">
<component name="BookmarkManager" />
<component name="ChangeBrowserSettings">
<option name="MAIN_SPLITTER_PROPORTION" value="0.3" />
<option name="MESSAGES_SPLITTER_PROPORTION" value="0.8" />
<option name="USE_DATE_BEFORE_FILTER" value="false" />
<option name="USE_DATE_AFTER_FILTER" value="false" />
<option name="USE_CHANGE_BEFORE_FILTER" value="false" />
<option name="USE_CHANGE_AFTER_FILTER" value="false" />
<option name="DATE_BEFORE" value="" />
<option name="DATE_AFTER" value="" />
<option name="CHANGE_BEFORE" value="" />
<option name="CHANGE_AFTER" value="" />
<option name="USE_USER_FILTER" value="false" />
<option name="USER" value="" />
</component>
<component name="ChangeListManager">
<list default="true" name="Default" comment="" />
</component>
<component name="ChangeListSynchronizer" />
<component name="ChangesViewManager" flattened_view="true" />
<component name="CheckinPanelState" />
<component name="Commander">
<leftPanel />
<rightPanel />
<splitter proportion="0.5" />
</component>
<component name="CompilerWorkspaceConfiguration">
<option name="COMPILE_IN_BACKGROUND" value="false" />
<option name="AUTO_SHOW_ERRORS_IN_EDITOR" value="true" />
<option name="CLOSE_MESSAGE_VIEW_IF_SUCCESS" value="true" />
<option name="COMPILE_DEPENDENT_FILES" value="false" />
<option name="CLEAR_OUTPUT_DIRECTORY" value="false" />
<option name="ASSERT_NOT_NULL" value="true" />
</component>
<component name="CoverageDataManager" />
<component name="Cvs2Configuration">
<option name="PRUNE_EMPTY_DIRECTORIES" value="true" />
<option name="MERGING_MODE" value="0" />
<option name="MERGE_WITH_BRANCH1_NAME" value="HEAD" />
<option name="MERGE_WITH_BRANCH2_NAME" value="HEAD" />
<option name="RESET_STICKY" value="false" />
<option name="CREATE_NEW_DIRECTORIES" value="true" />
<option name="DEFAULT_TEXT_FILE_SUBSTITUTION" value="kv" />
<option name="PROCESS_UNKNOWN_FILES" value="false" />
<option name="PROCESS_DELETED_FILES" value="false" />
<option name="PROCESS_IGNORED_FILES" value="false" />
<option name="RESERVED_EDIT" value="false" />
<option name="CHECKOUT_DATE_OR_REVISION_SETTINGS">
<value>
<option name="BRANCH" value="" />
<option name="DATE" value="" />
<option name="USE_BRANCH" value="false" />
<option name="USE_DATE" value="false" />
</value>
</option>
<option name="UPDATE_DATE_OR_REVISION_SETTINGS">
<value>
<option name="BRANCH" value="" />
<option name="DATE" value="" />
<option name="USE_BRANCH" value="false" />
<option name="USE_DATE" value="false" />
</value>
</option>
<option name="SHOW_CHANGES_REVISION_SETTINGS">
<value>
<option name="BRANCH" value="" />
<option name="DATE" value="" />
<option name="USE_BRANCH" value="false" />
<option name="USE_DATE" value="false" />
</value>
</option>
<option name="SHOW_OUTPUT" value="false" />
<option name="ADD_WATCH_INDEX" value="0" />
<option name="REMOVE_WATCH_INDEX" value="0" />
<option name="UPDATE_KEYWORD_SUBSTITUTION" />
<option name="MAKE_NEW_FILES_READONLY" value="false" />
<option name="SHOW_CORRUPTED_PROJECT_FILES" value="0" />
<option name="TAG_AFTER_PROJECT_COMMIT" value="false" />
<option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="true" />
<option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="" />
<option name="CLEAN_COPY" value="false" />
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints />
</component>
<component name="DebuggerManager">
<breakpoint_any>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
</breakpoint_any>
<breakpoint_rules />
<ui_properties />
</component>
<component name="ErrorTreeViewConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="HIDE_WARNINGS" value="false" />
</component>
<component name="FavoritesManager">
<favorites_list name="Jingle" />
</component>
<component name="FavoritesProjectViewPane" />
<component name="FileEditorManager" />
<component name="FindManager">
<FindUsagesManager>
<setting name="OPEN_NEW_TAB" value="false" />
</FindUsagesManager>
</component>
<component name="HierarchyBrowserManager">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="SORT_ALPHABETICALLY" value="false" />
<option name="HIDE_CLASSES_WHERE_METHOD_NOT_IMPLEMENTED" value="false" />
</component>
<component name="InspectionManager">
<option name="AUTOSCROLL_TO_SOURCE" value="false" />
<option name="SPLITTER_PROPORTION" value="0.5" />
<option name="GROUP_BY_SEVERITY" value="false" />
<option name="FILTER_RESOLVED_ITEMS" value="true" />
<option name="ANALYZE_TEST_SOURCES" value="true" />
<option name="SHOW_DIFF_WITH_PREVIOUS_RUN" value="false" />
<option name="SCOPE_TYPE" value="1" />
<option name="CUSTOM_SCOPE_NAME" value="" />
<option name="SHOW_ONLY_DIFF" value="false" />
<option name="myCurrentProfileName" value="Default" />
</component>
<component name="J2EEProjectPane" />
<component name="JspContextManager" />
<component name="ModuleEditorState">
<option name="LAST_EDITED_MODULE_NAME" />
<option name="LAST_EDITED_TAB_NAME" />
</component>
<component name="NamedScopeManager" />
<component name="PackagesPane" />
<component name="PerforceChangeBrowserSettings">
<option name="USE_CLIENT_FILTER" value="true" />
<option name="CLIENT" value="" />
</component>
<component name="PerforceDirect.Settings">
<option name="useP4CONFIG" value="true" />
<option name="port" value="&lt;perforce_server&gt;:1666" />
<option name="client" value="" />
<option name="user" value="" />
<option name="passwd" value="" />
<option name="showCmds" value="false" />
<option name="useNativeApi" value="true" />
<option name="pathToExec" value="p4" />
<option name="useCustomPathToExec" value="false" />
<option name="SYNC_FORCE" value="false" />
<option name="SYNC_RUN_RESOLVE" value="true" />
<option name="REVERT_UNCHANGED_FILES" value="true" />
<option name="CHARSET" value="none" />
<option name="SHOW_BRANCHES_HISTORY" value="true" />
<option name="ENABLED" value="true" />
<option name="USE_LOGIN" value="false" />
<option name="LOGIN_SILENTLY" value="false" />
<option name="INTEGRATE_RUN_RESOLVE" value="true" />
<option name="INTEGRATE_REVERT_UNCHANGED" value="true" />
<option name="SERVER_TIMEOUT" value="20000" />
</component>
<component name="ProjectLevelVcsManager">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkin" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<OptionsSetting value="true" id="Undo Check Out" />
<OptionsSetting value="true" id="Compare with SourceSafe Version" />
<OptionsSetting value="true" id="Get Latest Version" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Jingle.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Jingle.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Jingle.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension\test" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Jingle.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension\source" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Jingle.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension\source" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension\source\org\jivesoftware\smackx" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="0.16666667" version="1" splitterProportion="0.5">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<showStructure ProjectPane="false" Scope="false" />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
</navigator>
</component>
<component name="PropertiesComponent">
<property name="MemberChooser.copyJavadoc" value="false" />
<property name="GenerateAntBuildDialog.generateSingleFile" value="true" />
<property name="GoToClass.includeLibraries" value="false" />
<property name="MemberChooser.showClasses" value="true" />
<property name="MemberChooser.sorted" value="false" />
<property name="GoToFile.includeJavaFiles" value="false" />
<property name="GoToClass.toSaveIncludeLibraries" value="false" />
<property name="GenerateAntBuildDialog.enableUiFormCompile" value="false" />
<property name="GenerateAntBuildDialog.forceTargetJdk" value="true" />
<property name="GenerateAntBuildDialog.backupFiles" value="false" />
</component>
<component name="ReadonlyStatusHandler">
<option name="SHOW_DIALOG" value="true" />
</component>
<component name="RecentsManager" />
<component name="RestoreUpdateTree" />
<component name="RunManager">
<configuration default="true" type="Applet" factoryName="Applet">
<module name="" />
<option name="MAIN_CLASS_NAME" />
<option name="HTML_FILE_NAME" />
<option name="HTML_USED" value="false" />
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<option name="VM_PARAMETERS" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
</configuration>
<configuration default="true" type="Application" factoryName="Application" enabled="false" merge="false">
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<module name="" />
</configuration>
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit" enabled="false" merge="false">
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ADDITIONAL_CLASS_PATH" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
</configuration>
<configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
<Host>localhost</Host>
<Port>5050</Port>
</configuration>
</component>
<component name="ScopeViewComponent">
<subPane subId="Project">
<PATH>
<PATH_ELEMENT USER_OBJECT="Root">
<option name="myItemId" value="" />
<option name="myItemType" value="" />
</PATH_ELEMENT>
</PATH>
</subPane>
</component>
<component name="SelectInManager" />
<component name="StarteamConfiguration">
<option name="SERVER" value="" />
<option name="PORT" value="49201" />
<option name="USER" value="" />
<option name="PASSWORD" value="" />
<option name="PROJECT" value="" />
<option name="VIEW" value="" />
<option name="ALTERNATIVE_WORKING_PATH" value="" />
<option name="LOCK_ON_CHECKOUT" value="false" />
<option name="UNLOCK_ON_CHECKIN" value="false" />
</component>
<component name="StructuralSearchPlugin" />
<component name="StructureViewFactory">
<option name="AUTOSCROLL_MODE" value="true" />
<option name="AUTOSCROLL_FROM_SOURCE" value="false" />
<option name="ACTIVE_ACTIONS" value="" />
</component>
<component name="Struts Assistant">
<option name="showInputs" value="true" />
<option name="resources">
<value>
<option name="strutsPath" />
<option name="strutsHelp" />
</value>
</option>
<option name="selectedTaglibs" />
<option name="selectedTaglibs" />
<option name="myStrutsValidationEnabled" value="true" />
<option name="myTilesValidationEnabled" value="true" />
<option name="myValidatorValidationEnabled" value="true" />
<option name="myReportErrorsAsWarnings" value="true" />
</component>
<component name="SvnChangesBrowserSettings">
<option name="USE_AUTHOR_FIELD" value="true" />
<option name="AUTHOR" value="" />
<option name="LOCATION" value="" />
<option name="USE_PROJECT_SETTINGS" value="true" />
<option name="USE_ALTERNATE_LOCATION" value="false" />
</component>
<component name="SvnConfiguration">
<option name="USER" value="" />
<option name="PASSWORD" value="" />
<option name="PROCESS_UNRESOLVED" value="false" />
<option name="LAST_MERGED_REVISION" />
<option name="UPDATE_RUN_STATUS" value="false" />
<option name="UPDATE_RECURSIVELY" value="true" />
<option name="MERGE_DRY_RUN" value="false" />
<configuration useDefault="false">C:\Documents and Settings\ThiagoC\Dados de aplicativos\Subversion</configuration>
</component>
<component name="TodoView" selected-index="0">
<todo-panel id="selected-file">
<are-packages-shown value="false" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="-4" y="-4" width="1160" height="842" extended-state="0" />
<editor active="false" />
<layout>
<window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="3" />
<window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="8" />
<window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="3" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="7" />
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24954627" order="0" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="1" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="1" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.33053222" order="8" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="6" />
<window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="3" />
<window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="8" />
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="3" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="1" />
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="8" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="2" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="2" />
<window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="3" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="4" />
<window_info id="simpleUML" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="3" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="0" />
<window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="8" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="8" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="0" />
<window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="2" />
<window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="3" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="5" />
</layout>
</component>
<component name="VCS.FileViewConfiguration">
<option name="SELECTED_STATUSES" value="DEFAULT" />
<option name="SELECTED_COLUMNS" value="DEFAULT" />
<option name="SHOW_FILTERS" value="true" />
<option name="CUSTOMIZE_VIEW" value="true" />
<option name="SHOW_FILE_HISTORY_AS_TREE" value="true" />
</component>
<component name="VcsManagerConfiguration">
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="false" />
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="false" />
<option name="PUT_FOCUS_INTO_COMMENT" value="false" />
<option name="FORCE_NON_EMPTY_COMMENT" value="false" />
<option name="LAST_COMMIT_MESSAGE" />
<option name="SAVE_LAST_COMMIT_MESSAGE" value="true" />
<option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
<option name="ERROR_OCCURED" value="false" />
<option name="ACTIVE_VCS_NAME" />
<option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
</component>
<component name="VssConfiguration">
<option name="CLIENT_PATH" value="" />
<option name="SRCSAFEINI_PATH" value="" />
<option name="USER_NAME" value="" />
<option name="PWD" value="" />
<option name="VSS_IS_INITIALIZED" value="true" />
<CheckoutOptions>
<option name="COMMENT" value="" />
<option name="DO_NOT_GET_LATEST_VERSION" value="false" />
<option name="REPLACE_WRITABLE" value="false" />
<option name="RECURSIVE" value="false" />
</CheckoutOptions>
<CheckinOptions>
<option name="COMMENT" value="" />
<option name="KEEP_CHECKED_OUT" value="false" />
<option name="RECURSIVE" value="false" />
</CheckinOptions>
<AddOptions>
<option name="COMMENT" value="" />
<option name="STORE_ONLY_LATEST_VERSION" value="false" />
<option name="CHECK_OUT_IMMEDIATELY" value="false" />
<option name="FILE_TYPE" value="0" />
</AddOptions>
<UndocheckoutOptions>
<option name="MAKE_WRITABLE" value="false" />
<option name="REPLACE_LOCAL_COPY" value="0" />
<option name="RECURSIVE" value="false" />
</UndocheckoutOptions>
<GetOptions>
<option name="REPLACE_WRITABLE" value="0" />
<option name="MAKE_WRITABLE" value="false" />
<option name="ANSWER_NEGATIVELY" value="false" />
<option name="ANSWER_POSITIVELY" value="false" />
<option name="RECURSIVE" value="false" />
</GetOptions>
</component>
<component name="antWorkspaceConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="FILTER_TARGETS" value="false" />
<buildFile url="file://$PROJECT_DIR$/../build.xml">
<antCommandLine value="" />
<runInBackground value="false" />
<targetFilters>
<filter targetName="jar-test" isVisible="true" />
<filter targetName="javadoc" isVisible="true" />
<filter targetName="jar" isVisible="true" />
<filter targetName="test.jingle" isVisible="false" />
<filter targetName="release" isVisible="false" />
<filter targetName="test.smackx" isVisible="false" />
<filter targetName="test.messenger" isVisible="false" />
<filter targetName="compile-test" isVisible="true" />
<filter targetName="compile" isVisible="true" />
<filter targetName="jar-jingle" isVisible="true" />
<filter targetName="clean" isVisible="true" />
<filter targetName="all" isVisible="true" />
<filter targetName="func-test" isVisible="false" />
<filter targetName="test" isVisible="false" />
<filter targetName="release-exists" isVisible="false" />
</targetFilters>
<treeView value="true" />
<verbose value="true" />
<viewClosedWhenNoErrors value="false" />
</buildFile>
</component>
<component name="com.intellij.ide.util.scopeChooser.ScopeChooserConfigurable" proportions="" version="1">
<option name="myLastEditedConfigurable" />
</component>
<component name="com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectRootMasterDetailsConfigurable" proportions="0.16666667,0.5" version="1">
<option name="myPlainMode" value="false" />
<option name="myLastEditedConfigurable" value="JingleExtension" />
</component>
<component name="com.intellij.profile.ui.ErrorOptionsConfigurable" proportions="" version="1">
<option name="myLastEditedConfigurable" />
</component>
<component name="editorHistoryManager" />
<component name="simpleUML.DiagramSettingsWorkspace" />
<component name="simpleUML.UMLToolWindowPlugin">
<General>
<option name="birdViewUpdateDelay" value="2000" />
<option name="defaultFileLocation" value="file://$APPLICATION_HOME_DIR$/bin" />
</General>
<Classdiagram>
<option name="diagramTitleFont" value="SansSerif,1,12" />
<option name="diagramFont" value="SansSerif,0,10" />
<option name="defaultFieldsExpanded" value="false" />
<option name="defaultContructorsExpanded" value="false" />
<option name="defaultMethodsExpanded" value="false" />
<option name="showParameters" value="true" />
<option name="showTooltip" value="true" />
<option name="showReturnValues" value="true" />
<option name="longModifier" value="true" />
<option name="implementsBehaviour" value="1" />
<option name="extendsBehaviour" value="1" />
<option name="compartmentBehaviour" value="1" />
<option name="interfaceBackgroundColor" value="-6494306" />
<option name="abstractClassBackgroundColor" value="-1580132" />
<option name="classBackgroundColor" value="-6508057" />
<option name="diagramBackgroundColor" value="-1" />
<option name="useAntialiasedConnectors" value="true" />
<option name="quickSourceLinkColor" value="-16776961" />
<option name="quickDiagramLinkColor" value="-8454144" />
<option name="drawDecorations" value="false" />
<option name="hideFieldList" value="" />
<option name="showFieldList" value="" />
<option name="hideConstructorList" value="" />
<option name="showConstructorList" value="" />
<option name="hideMethodList" value="" />
<option name="showMethodList" value="" />
<option name="minimumFigureSize" value="0,0" />
</Classdiagram>
<Dependencydiagram>
<option name="diagramTitleFont" value="SansSerif,1,12" />
<option name="diagramFont" value="SansSerif,0,10" />
<option name="diagramBackgroundColor" value="-1" />
<option name="useAntialiasedConnectors" value="true" />
<option name="packageNameCompressionLevel" value="0" />
</Dependencydiagram>
<Packagediagram>
<option name="diagramTitleFont" value="SansSerif,0,12" />
<option name="diagramFont" value="SansSerif,0,10" />
<option name="packageBackgroundColor" value="-4144960" />
<option name="diagramBackgroundColor" value="-1" />
<option name="useAntialiasedConnectors" value="true" />
<option name="packageNameCompressionLevel" value="2" />
</Packagediagram>
</component>
</project>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4" relativePaths="true" type="JAVA_MODULE">
<component name="BuildJarSettings">
<containerInfo>
<containerElement type="module" name="JingleExtension">
<attribute name="method" value="1" />
<attribute name="URI" value="/" />
</containerElement>
</containerInfo>
<setting name="jarUrl" value="file://$MODULE_DIR$/../../JingleExtension.jar" />
<setting name="buildJar" value="true" />
<setting name="mainClass" value="" />
</component>
<component name="ModuleRootManager" />
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/../../classes" />
<exclude-output />
<exclude-exploded />
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../../source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library" exported="">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../merge/jstun-0.6.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/junit.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module" module-name="Smack" />
<orderEntryProperties />
</component>
<component name="VcsManagerConfiguration">
<option name="ACTIVE_VCS_NAME" value="svn" />
<option name="USE_PROJECT_VCS" value="false" />
</component>
</module>

View file

@ -0,0 +1,593 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4" relativePaths="true">
<component name="BookmarkManager" />
<component name="ChangeBrowserSettings">
<option name="MAIN_SPLITTER_PROPORTION" value="0.3" />
<option name="MESSAGES_SPLITTER_PROPORTION" value="0.8" />
<option name="USE_DATE_BEFORE_FILTER" value="false" />
<option name="USE_DATE_AFTER_FILTER" value="false" />
<option name="USE_CHANGE_BEFORE_FILTER" value="false" />
<option name="USE_CHANGE_AFTER_FILTER" value="false" />
<option name="DATE_BEFORE" value="" />
<option name="DATE_AFTER" value="" />
<option name="CHANGE_BEFORE" value="" />
<option name="CHANGE_AFTER" value="" />
<option name="USE_USER_FILTER" value="false" />
<option name="USER" value="" />
</component>
<component name="ChangeListManager">
<list default="true" name="Default" comment="">
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../README.html" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../lib/smackx.jar" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/Smack.ipr" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../merge" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../merge/jstun-0.6.1.jar" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../release.xml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../lib/junit.jar" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/JingleExtension.iml" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../lib" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../lib/smack.jar" />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/.." />
<change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/../lib/smackx-debug.jar" />
</list>
</component>
<component name="ChangeListSynchronizer" />
<component name="ChangesViewManager" flattened_view="true" />
<component name="CheckinPanelState" />
<component name="Commander">
<leftPanel />
<rightPanel />
<splitter proportion="0.5" />
</component>
<component name="CompilerWorkspaceConfiguration">
<option name="COMPILE_IN_BACKGROUND" value="false" />
<option name="AUTO_SHOW_ERRORS_IN_EDITOR" value="true" />
<option name="CLOSE_MESSAGE_VIEW_IF_SUCCESS" value="true" />
<option name="COMPILE_DEPENDENT_FILES" value="false" />
<option name="CLEAR_OUTPUT_DIRECTORY" value="false" />
<option name="ASSERT_NOT_NULL" value="true" />
</component>
<component name="CoverageDataManager" />
<component name="Cvs2Configuration">
<option name="PRUNE_EMPTY_DIRECTORIES" value="true" />
<option name="MERGING_MODE" value="0" />
<option name="MERGE_WITH_BRANCH1_NAME" value="HEAD" />
<option name="MERGE_WITH_BRANCH2_NAME" value="HEAD" />
<option name="RESET_STICKY" value="false" />
<option name="CREATE_NEW_DIRECTORIES" value="true" />
<option name="DEFAULT_TEXT_FILE_SUBSTITUTION" value="kv" />
<option name="PROCESS_UNKNOWN_FILES" value="false" />
<option name="PROCESS_DELETED_FILES" value="false" />
<option name="PROCESS_IGNORED_FILES" value="false" />
<option name="RESERVED_EDIT" value="false" />
<option name="CHECKOUT_DATE_OR_REVISION_SETTINGS">
<value>
<option name="BRANCH" value="" />
<option name="DATE" value="" />
<option name="USE_BRANCH" value="false" />
<option name="USE_DATE" value="false" />
</value>
</option>
<option name="UPDATE_DATE_OR_REVISION_SETTINGS">
<value>
<option name="BRANCH" value="" />
<option name="DATE" value="" />
<option name="USE_BRANCH" value="false" />
<option name="USE_DATE" value="false" />
</value>
</option>
<option name="SHOW_CHANGES_REVISION_SETTINGS">
<value>
<option name="BRANCH" value="" />
<option name="DATE" value="" />
<option name="USE_BRANCH" value="false" />
<option name="USE_DATE" value="false" />
</value>
</option>
<option name="SHOW_OUTPUT" value="false" />
<option name="ADD_WATCH_INDEX" value="0" />
<option name="REMOVE_WATCH_INDEX" value="0" />
<option name="UPDATE_KEYWORD_SUBSTITUTION" />
<option name="MAKE_NEW_FILES_READONLY" value="false" />
<option name="SHOW_CORRUPTED_PROJECT_FILES" value="0" />
<option name="TAG_AFTER_PROJECT_COMMIT" value="false" />
<option name="OVERRIDE_EXISTING_TAG_FOR_PROJECT" value="true" />
<option name="TAG_AFTER_PROJECT_COMMIT_NAME" value="" />
<option name="CLEAN_COPY" value="false" />
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints />
</component>
<component name="DebuggerManager">
<breakpoint_any>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
</breakpoint_any>
<breakpoint_rules />
<ui_properties />
</component>
<component name="ErrorTreeViewConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="HIDE_WARNINGS" value="false" />
</component>
<component name="FavoritesManager">
<favorites_list name="Smack" />
</component>
<component name="FavoritesProjectViewPane" />
<component name="FileEditorManager">
<leaf />
</component>
<component name="FindManager">
<FindUsagesManager>
<setting name="OPEN_NEW_TAB" value="false" />
</FindUsagesManager>
</component>
<component name="HierarchyBrowserManager">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="SORT_ALPHABETICALLY" value="false" />
<option name="HIDE_CLASSES_WHERE_METHOD_NOT_IMPLEMENTED" value="false" />
</component>
<component name="InspectionManager">
<option name="AUTOSCROLL_TO_SOURCE" value="false" />
<option name="SPLITTER_PROPORTION" value="0.5" />
<option name="GROUP_BY_SEVERITY" value="false" />
<option name="FILTER_RESOLVED_ITEMS" value="true" />
<option name="ANALYZE_TEST_SOURCES" value="true" />
<option name="SHOW_DIFF_WITH_PREVIOUS_RUN" value="false" />
<option name="SCOPE_TYPE" value="1" />
<option name="CUSTOM_SCOPE_NAME" value="" />
<option name="SHOW_ONLY_DIFF" value="false" />
<option name="myCurrentProfileName" value="Default" />
</component>
<component name="J2EEProjectPane" />
<component name="JspContextManager" />
<component name="ModuleEditorState">
<option name="LAST_EDITED_MODULE_NAME" />
<option name="LAST_EDITED_TAB_NAME" />
</component>
<component name="NamedScopeManager" />
<component name="PackagesPane" />
<component name="PerforceChangeBrowserSettings">
<option name="USE_CLIENT_FILTER" value="true" />
<option name="CLIENT" value="" />
</component>
<component name="PerforceDirect.Settings">
<option name="useP4CONFIG" value="true" />
<option name="port" value="&lt;perforce_server&gt;:1666" />
<option name="client" value="" />
<option name="user" value="" />
<option name="passwd" value="" />
<option name="showCmds" value="false" />
<option name="useNativeApi" value="true" />
<option name="pathToExec" value="p4" />
<option name="useCustomPathToExec" value="false" />
<option name="SYNC_FORCE" value="false" />
<option name="SYNC_RUN_RESOLVE" value="true" />
<option name="REVERT_UNCHANGED_FILES" value="true" />
<option name="CHARSET" value="none" />
<option name="SHOW_BRANCHES_HISTORY" value="true" />
<option name="ENABLED" value="true" />
<option name="USE_LOGIN" value="false" />
<option name="LOGIN_SILENTLY" value="false" />
<option name="INTEGRATE_RUN_RESOLVE" value="true" />
<option name="INTEGRATE_REVERT_UNCHANGED" value="true" />
<option name="SERVER_TIMEOUT" value="20000" />
</component>
<component name="ProjectLevelVcsManager">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkin" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<OptionsSetting value="true" id="Undo Check Out" />
<OptionsSetting value="true" id="Compare with SourceSafe Version" />
<OptionsSetting value="true" id="Get Latest Version" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectPane">
<subPane>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Smack.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
</PATH>
<PATH>
<PATH_ELEMENT>
<option name="myItemId" value="Smack.ipr" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="JingleExtension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewModuleNode" />
</PATH_ELEMENT>
<PATH_ELEMENT>
<option name="myItemId" value="PsiDirectory:C:\jingle-extension" />
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
</PATH_ELEMENT>
</PATH>
</subPane>
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0" />
</component>
<component name="ProjectView">
<navigator currentView="ProjectPane" proportions="0.16666667" version="1" splitterProportion="0.5">
<flattenPackages />
<showMembers />
<showModules />
<showLibraryContents />
<hideEmptyPackages />
<abbreviatePackageNames />
<showStructure ProjectPane="false" Scope="false" />
<autoscrollToSource />
<autoscrollFromSource />
<sortByType />
</navigator>
</component>
<component name="PropertiesComponent">
<property name="MemberChooser.copyJavadoc" value="false" />
<property name="GoToClass.includeLibraries" value="false" />
<property name="MemberChooser.showClasses" value="true" />
<property name="MemberChooser.sorted" value="false" />
<property name="GoToFile.includeJavaFiles" value="false" />
<property name="GoToClass.toSaveIncludeLibraries" value="false" />
</component>
<component name="ReadonlyStatusHandler">
<option name="SHOW_DIALOG" value="true" />
</component>
<component name="RecentsManager" />
<component name="RestoreUpdateTree" />
<component name="RunManager">
<configuration default="true" type="JUnit" factoryName="JUnit" enabled="false" merge="false">
<module name="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" />
<option name="METHOD_NAME" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" />
<option name="PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ADDITIONAL_CLASS_PATH" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="wholeProject" />
</option>
<method>
<option name="Make" value="true" />
</method>
</configuration>
<configuration default="true" type="Application" factoryName="Application" enabled="false" merge="false">
<option name="MAIN_CLASS_NAME" />
<option name="VM_PARAMETERS" />
<option name="PROGRAM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<option name="ENABLE_SWING_INSPECTOR" value="false" />
<module name="" />
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<module name="" />
<option name="MAIN_CLASS_NAME" />
<option name="HTML_FILE_NAME" />
<option name="HTML_USED" value="false" />
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<option name="VM_PARAMETERS" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
</configuration>
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
</configuration>
<configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
<Host>localhost</Host>
<Port>5050</Port>
</configuration>
</component>
<component name="ScopeViewComponent">
<subPane subId="Project" />
</component>
<component name="SelectInManager" />
<component name="StarteamConfiguration">
<option name="SERVER" value="" />
<option name="PORT" value="49201" />
<option name="USER" value="" />
<option name="PASSWORD" value="" />
<option name="PROJECT" value="" />
<option name="VIEW" value="" />
<option name="ALTERNATIVE_WORKING_PATH" value="" />
<option name="LOCK_ON_CHECKOUT" value="false" />
<option name="UNLOCK_ON_CHECKIN" value="false" />
</component>
<component name="StructuralSearchPlugin" />
<component name="StructureViewFactory">
<option name="AUTOSCROLL_MODE" value="true" />
<option name="AUTOSCROLL_FROM_SOURCE" value="false" />
<option name="ACTIVE_ACTIONS" value="" />
</component>
<component name="Struts Assistant">
<option name="showInputs" value="true" />
<option name="resources">
<value>
<option name="strutsPath" />
<option name="strutsHelp" />
</value>
</option>
<option name="selectedTaglibs" />
<option name="selectedTaglibs" />
<option name="myStrutsValidationEnabled" value="true" />
<option name="myTilesValidationEnabled" value="true" />
<option name="myValidatorValidationEnabled" value="true" />
<option name="myReportErrorsAsWarnings" value="true" />
</component>
<component name="SvnChangesBrowserSettings">
<option name="USE_AUTHOR_FIELD" value="true" />
<option name="AUTHOR" value="" />
<option name="LOCATION" value="" />
<option name="USE_PROJECT_SETTINGS" value="true" />
<option name="USE_ALTERNATE_LOCATION" value="false" />
</component>
<component name="SvnConfiguration">
<option name="USER" value="" />
<option name="PASSWORD" value="" />
<option name="PROCESS_UNRESOLVED" value="false" />
<option name="LAST_MERGED_REVISION" />
<option name="UPDATE_RUN_STATUS" value="false" />
<option name="UPDATE_RECURSIVELY" value="true" />
<option name="MERGE_DRY_RUN" value="false" />
<configuration useDefault="false">C:\Documents and Settings\ThiagoC\Dados de aplicativos\Subversion</configuration>
</component>
<component name="TodoView" selected-index="0">
<todo-panel id="selected-file">
<are-packages-shown value="false" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<are-modules-shown value="false" />
<flatten-packages value="false" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="-4" y="-4" width="1160" height="842" extended-state="0" />
<editor active="false" />
<layout>
<window_info id="UI Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="CVS" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="IDEtalk" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="7" />
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.24954627" order="0" />
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="1" />
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="1" />
<window_info id="Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="6" />
<window_info id="Module Dependencies" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Dependency Viewer" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="1" />
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="2" />
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="2" />
<window_info id="File View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="4" />
<window_info id="simpleUML" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.32940108" order="-1" />
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" order="0" />
<window_info id="IDEtalk Messages" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="-1" />
<window_info id="Web" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="2" />
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" order="0" />
<window_info id="EJB" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="3" />
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" order="5" />
</layout>
</component>
<component name="VCS.FileViewConfiguration">
<option name="SELECTED_STATUSES" value="DEFAULT" />
<option name="SELECTED_COLUMNS" value="DEFAULT" />
<option name="SHOW_FILTERS" value="true" />
<option name="CUSTOMIZE_VIEW" value="true" />
<option name="SHOW_FILE_HISTORY_AS_TREE" value="true" />
</component>
<component name="VcsManagerConfiguration">
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" />
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="false" />
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="false" />
<option name="PUT_FOCUS_INTO_COMMENT" value="false" />
<option name="FORCE_NON_EMPTY_COMMENT" value="false" />
<option name="LAST_COMMIT_MESSAGE" />
<option name="SAVE_LAST_COMMIT_MESSAGE" value="true" />
<option name="CHECKIN_DIALOG_SPLITTER_PROPORTION" value="0.8" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" />
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" />
<option name="ERROR_OCCURED" value="false" />
<option name="ACTIVE_VCS_NAME" />
<option name="UPDATE_GROUP_BY_PACKAGES" value="false" />
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
</component>
<component name="VssConfiguration">
<option name="CLIENT_PATH" value="" />
<option name="SRCSAFEINI_PATH" value="" />
<option name="USER_NAME" value="" />
<option name="PWD" value="" />
<option name="VSS_IS_INITIALIZED" value="true" />
<CheckoutOptions>
<option name="COMMENT" value="" />
<option name="DO_NOT_GET_LATEST_VERSION" value="false" />
<option name="REPLACE_WRITABLE" value="false" />
<option name="RECURSIVE" value="false" />
</CheckoutOptions>
<CheckinOptions>
<option name="COMMENT" value="" />
<option name="KEEP_CHECKED_OUT" value="false" />
<option name="RECURSIVE" value="false" />
</CheckinOptions>
<AddOptions>
<option name="COMMENT" value="" />
<option name="STORE_ONLY_LATEST_VERSION" value="false" />
<option name="CHECK_OUT_IMMEDIATELY" value="false" />
<option name="FILE_TYPE" value="0" />
</AddOptions>
<UndocheckoutOptions>
<option name="MAKE_WRITABLE" value="false" />
<option name="REPLACE_LOCAL_COPY" value="0" />
<option name="RECURSIVE" value="false" />
</UndocheckoutOptions>
<GetOptions>
<option name="REPLACE_WRITABLE" value="0" />
<option name="MAKE_WRITABLE" value="false" />
<option name="ANSWER_NEGATIVELY" value="false" />
<option name="ANSWER_POSITIVELY" value="false" />
<option name="RECURSIVE" value="false" />
</GetOptions>
</component>
<component name="antWorkspaceConfiguration">
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" />
<option name="FILTER_TARGETS" value="false" />
<buildFile url="file://$PROJECT_DIR$/../build.xml">
<antCommandLine value="" />
<runInBackground value="false" />
<targetFilters>
<filter targetName="jar-test" isVisible="true" />
<filter targetName="javadoc" isVisible="true" />
<filter targetName="jar" isVisible="true" />
<filter targetName="test.jingle" isVisible="false" />
<filter targetName="release" isVisible="false" />
<filter targetName="test.smackx" isVisible="false" />
<filter targetName="test.messenger" isVisible="false" />
<filter targetName="compile-test" isVisible="true" />
<filter targetName="compile" isVisible="true" />
<filter targetName="jar-jingle" isVisible="true" />
<filter targetName="clean" isVisible="true" />
<filter targetName="all" isVisible="true" />
<filter targetName="func-test" isVisible="false" />
<filter targetName="test" isVisible="false" />
<filter targetName="release-exists" isVisible="false" />
</targetFilters>
<treeView value="true" />
<verbose value="true" />
<viewClosedWhenNoErrors value="false" />
</buildFile>
</component>
<component name="com.intellij.ide.util.scopeChooser.ScopeChooserConfigurable" proportions="" version="1">
<option name="myLastEditedConfigurable" />
</component>
<component name="com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectRootMasterDetailsConfigurable" proportions="0.16666667" version="1">
<option name="myPlainMode" value="false" />
<option name="myLastEditedConfigurable" value="Global Resources" />
</component>
<component name="com.intellij.profile.ui.ErrorOptionsConfigurable" proportions="" version="1">
<option name="myLastEditedConfigurable" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/../../test/org/jivesoftware/smackx/jingle/JingleManagerTest.java">
<provider selected="true" editor-type-id="text-editor">
<state line="319" column="12" selection-start="11501" selection-end="11501" vertical-scroll-proportion="0.33233532">
<folding />
</state>
</provider>
</entry>
</component>
<component name="simpleUML.DiagramSettingsWorkspace" />
<component name="simpleUML.UMLToolWindowPlugin">
<General>
<option name="birdViewUpdateDelay" value="2000" />
<option name="defaultFileLocation" value="file://$APPLICATION_HOME_DIR$/bin" />
</General>
<Classdiagram>
<option name="diagramTitleFont" value="SansSerif,1,12" />
<option name="diagramFont" value="SansSerif,0,10" />
<option name="defaultFieldsExpanded" value="false" />
<option name="defaultContructorsExpanded" value="false" />
<option name="defaultMethodsExpanded" value="false" />
<option name="showParameters" value="true" />
<option name="showTooltip" value="true" />
<option name="showReturnValues" value="true" />
<option name="longModifier" value="true" />
<option name="implementsBehaviour" value="1" />
<option name="extendsBehaviour" value="1" />
<option name="compartmentBehaviour" value="1" />
<option name="interfaceBackgroundColor" value="-6494306" />
<option name="abstractClassBackgroundColor" value="-1580132" />
<option name="classBackgroundColor" value="-6508057" />
<option name="diagramBackgroundColor" value="-1" />
<option name="useAntialiasedConnectors" value="true" />
<option name="quickSourceLinkColor" value="-16776961" />
<option name="quickDiagramLinkColor" value="-8454144" />
<option name="drawDecorations" value="false" />
<option name="hideFieldList" value="" />
<option name="showFieldList" value="" />
<option name="hideConstructorList" value="" />
<option name="showConstructorList" value="" />
<option name="hideMethodList" value="" />
<option name="showMethodList" value="" />
<option name="minimumFigureSize" value="0,0" />
</Classdiagram>
<Dependencydiagram>
<option name="diagramTitleFont" value="SansSerif,1,12" />
<option name="diagramFont" value="SansSerif,0,10" />
<option name="diagramBackgroundColor" value="-1" />
<option name="useAntialiasedConnectors" value="true" />
<option name="packageNameCompressionLevel" value="0" />
</Dependencydiagram>
<Packagediagram>
<option name="diagramTitleFont" value="SansSerif,0,12" />
<option name="diagramFont" value="SansSerif,0,10" />
<option name="packageBackgroundColor" value="-4144960" />
<option name="diagramBackgroundColor" value="-1" />
<option name="useAntialiasedConnectors" value="true" />
<option name="packageNameCompressionLevel" value="2" />
</Packagediagram>
</component>
</project>

View file

@ -0,0 +1,421 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.listeners.JingleMediaListener;
import org.jivesoftware.smackx.jingle.listeners.JingleTransportListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.MediaNegotiator;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import org.jivesoftware.smackx.jingle.nat.TransportNegotiator;
import org.jivesoftware.smackx.jingle.nat.TransportResolver;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleContentDescription;
import org.jivesoftware.smackx.packet.JingleContentDescription.JinglePayloadType;
import org.jivesoftware.smackx.packet.JingleError;
import java.util.List;
/**
* An incoming Jingle Session implementation.
* This class has especific bahavior to accept and establish a received Jingle Session Request.
*
* This class is not directly used by users. Instead, users should refer to the
* JingleManager class, that will create the appropiate instance...
*
* @author Alvaro Saurin
*/
public class IncomingJingleSession extends JingleSession {
// states
private final Accepting accepting;
private final Pending pending;
private final Active active;
private JingleSessionRequest initialSessionRequest;
/**
* Constructor for a Jingle incoming session
*
* @param conn the XMPP connection
* @param responder the responder
* @param resolver The transport resolver
*/
protected IncomingJingleSession(XMPPConnection conn, String responder,
List payloadTypes, TransportResolver resolver) {
super(conn, responder, conn.getUser());
// Create the states...
accepting = new Accepting(this);
pending = new Pending(this);
active = new Active(this);
setMediaNeg(new MediaNegotiator(this, payloadTypes));
if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
setTransportNeg(new TransportNegotiator.RawUdp(this, resolver));
}
if (resolver.getType().equals(TransportResolver.Type.ice)) {
setTransportNeg(new TransportNegotiator.Ice(this, resolver));
}
}
/**
* Constructor for a Jingle Incoming session with a defined Media Manager
*
* @param conn the XMPP connection
* @param responder the responder
* @param resolver The transport resolver
* @param jingleMediaManager The Media Manager for this Session
*/
protected IncomingJingleSession(XMPPConnection conn, String responder,
List payloadTypes, TransportResolver resolver, JingleMediaManager jingleMediaManager) {
this(conn, responder, payloadTypes, resolver);
this.jingleMediaManager = jingleMediaManager;
}
/**
* Start the session for a initial Jingle request packet.
*
* @param initialJingleSessionRequest the initial Jingle Session Request
* @throws XMPPException
*/
public void start(JingleSessionRequest initialJingleSessionRequest) throws XMPPException {
if (invalidState()) {
Jingle packet = initialJingleSessionRequest.getJingle();
System.out.println("invalidState");
if (packet != null) {
// Initialize the session information
setSid(packet.getSid());
// Establish the default state
setState(accepting);
updatePacketListener();
respond(packet);
} else {
throw new IllegalStateException(
"Session request with null Jingle packet.");
}
} else {
throw new IllegalStateException("Starting session without null state.");
}
}
/**
* Start the session using initial Jingle Session Request used to create this session..
*
* @throws XMPPException
*/
public void start() throws XMPPException {
start(this.getInitialSessionRequest());
}
/**
* Get the initial Jingle packet request
*
* @return
*/
JingleSessionRequest getInitialSessionRequest() {
return initialSessionRequest;
}
/**
* Get the initial Jingle packet request
*
* @param initialRequest the initial Jingle packet
*/
void setInitialSessionRequest(JingleSessionRequest initialRequest) {
this.initialSessionRequest = initialRequest;
}
// States
/**
* First stage when we have received a session request, and we accept the
* request. We start in this stage, as the instance is created when the user
* accepts the connection...
*/
public class Accepting extends JingleNegotiator.State {
public Accepting(JingleNegotiator neg) {
super(neg);
}
/**
* Initiate the incoming session. We have already sent the ACK partially
* accepting the session...
*
* @throws XMPPException
*/
public Jingle eventInitiate(Jingle inJingle) throws XMPPException {
// Set the new session state
setState(pending);
return super.eventInitiate(inJingle);
}
/**
* An error has occurred.
*
* @throws XMPPException
*/
public void eventError(IQ iq) throws XMPPException {
triggerSessionClosedOnError(new JingleException(iq.getError().getMessage()));
super.eventError(iq);
}
}
/**
* "Pending" state: we are waiting for the transport and content
* negotiators.
*/
private class Pending extends JingleNegotiator.State {
JingleMediaListener jingleMediaListener;
JingleTransportListener jingleTransportListener;
public Pending(JingleNegotiator neg) {
super(neg);
// Create the listeners that will send a "session-accept" when the
// sub-negotiators are done.
jingleMediaListener = new JingleMediaListener() {
public void mediaClosed(PayloadType cand) {
}
public void mediaEstablished(PayloadType pt) {
checkFullyEstablished();
}
};
jingleTransportListener = new JingleTransportListener() {
public void transportEstablished(TransportCandidate local,
TransportCandidate remote) {
checkFullyEstablished();
}
public void transportClosed(TransportCandidate cand) {
}
public void transportClosedOnError(XMPPException e) {
}
};
}
/**
* Enter in the pending state: wait for the sub-negotiators.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventEnter() {
// Add the listeners to the sub-negotiators...
System.out.println("Pending eventEnter");
addMediaListener(jingleMediaListener);
addTransportListener(jingleTransportListener);
super.eventEnter();
}
/**
* Exit of the state
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventExit()
*/
public void eventExit() {
removeMediaListener(jingleMediaListener);
removeTransportListener(jingleTransportListener);
super.eventExit();
}
/**
* Check if the session has been fully accepted by all the
* sub-negotiators and, in that case, send an "accept" message...
*/
private void checkFullyEstablished() {
if (isFullyEstablished()) {
PayloadType.Audio bestCommonAudioPt = getMediaNeg()
.getBestCommonAudioPt();
TransportCandidate bestRemoteCandidate = getTransportNeg()
.getBestRemoteCandidate();
TransportCandidate acceptedLocalCandidate = getTransportNeg()
.getAcceptedLocalCandidate();
if (bestCommonAudioPt != null && bestRemoteCandidate != null
&& acceptedLocalCandidate != null) {
// Ok, send a packet saying that we accept this session
Jingle jout = new Jingle(Jingle.Action.SESSIONACCEPT);
// ... with the audio payload type and the transport
// candidate
jout.addDescription(new JingleContentDescription.Audio(
new JinglePayloadType(bestCommonAudioPt)));
jout.addTransport(getTransportNeg().getJingleTransport(
bestRemoteCandidate));
addExpectedId(jout.getPacketID());
sendFormattedJingle(jout);
}
}
}
/**
* The other endpoint has accepted the session.
*/
public Jingle eventAccept(Jingle jin) throws XMPPException {
PayloadType acceptedPayloadType = null;
TransportCandidate acceptedLocalCandidate = null;
// We process the "accepted" if we have finished the
// sub-negotiators. Maybe this is not needed (ie, the other endpoint
// can take the first valid transport candidate), but otherwise we
// must cancel the negotiators...
//
if (isFullyEstablished()) {
acceptedPayloadType = getAcceptedAudioPayloadType(jin);
acceptedLocalCandidate = getAcceptedLocalCandidate(jin);
if (acceptedPayloadType != null && acceptedLocalCandidate != null) {
if (acceptedPayloadType.equals(getMediaNeg().getBestCommonAudioPt())
&& acceptedLocalCandidate.equals(getTransportNeg()
.getAcceptedLocalCandidate())) {
setState(active);
}
} else {
throw new JingleException(JingleError.MALFORMED_STANZA);
}
}
return super.eventAccept(jin);
}
/**
* We have received a confirmation.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
*/
public Jingle eventAck(IQ iq) throws XMPPException {
setState(active);
return super.eventAck(iq);
}
/**
* An error has occurred.
*
* @throws XMPPException
*/
public void eventError(IQ iq) throws XMPPException {
if (iq == null) return;
triggerSessionClosedOnError(new XMPPException(iq.getError()));
super.eventError(iq);
}
}
/**
* "Active" state: we have an agreement about the session.
*/
private class Active extends JingleNegotiator.State {
public Active(JingleNegotiator neg) {
super(neg);
}
/**
* We have a established session: notify the listeners
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventEnter() {
PayloadType.Audio bestCommonAudioPt = getMediaNeg().getBestCommonAudioPt();
TransportCandidate bestRemoteCandidate = getTransportNeg()
.getBestRemoteCandidate();
TransportCandidate acceptedLocalCandidate = getTransportNeg()
.getAcceptedLocalCandidate();
// Trigger the session established flag
System.out.println("eventEntered");
triggerSessionEstablished(bestCommonAudioPt, bestRemoteCandidate,
acceptedLocalCandidate);
super.eventEnter();
}
/**
* Terminate the connection.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventTerminate(org.jivesoftware.smackx.packet.Jingle)
*/
public Jingle eventTerminate(Jingle jin) throws XMPPException {
triggerSessionClosed("Closed Remotely");
return super.eventTerminate(jin);
}
/**
* An error has occurred.
*
* @throws XMPPException
*/
public void eventError(IQ iq) throws XMPPException {
triggerSessionClosedOnError(new XMPPException(iq.getError().getMessage()));
super.eventError(iq);
}
}
}

View file

@ -0,0 +1,703 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright 2003-2005 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener;
import org.jivesoftware.smackx.jingle.listeners.JingleListener;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.BasicResolver;
import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import org.jivesoftware.smackx.jingle.nat.TransportResolver;
import org.jivesoftware.smackx.packet.DiscoverInfo;
import org.jivesoftware.smackx.packet.Jingle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Jingle is a session establishment protocol defined in (XEP-0166).
* It defines a framework for negotiating and managing out-of-band ( data that is send and receive through other connection than XMPP connection) data sessions over XMPP.
* With this protocol you can setup VOIP Calls, Video Streaming, File transfers and whatever out-of-band session based transmission.
*
* To create a Jingle Session you need a Transport method and a Payload type.
*
* A transport method is how it will trasmit and receive network packets. Transport MUST have one or more candidates.
* A transport candidate is an IP Address with a defined port, that other party must send data to.
*
* A supported payload type, is the data encoding format that the jmf will be transmitted.
* For instance an Audio Payload "GSM".
*
* A Jingle session negociates a payload type and a pair of transport candidates.
* Which means that when a Jingle Session is establhished you will have two defined transport candidates with addresses
* and a defined Payload type.
* In other words, you will have two IP address with their respective ports, and a Codec type defined.
*
* The JingleManager is a facade built upon Jabber Jingle (XEP-166) to allow the
* use of Jingle. This implementation allows the user to simply
* use this class for setting the Jingle parameters, create and receive Jingle Sessions.
*
* In order to use the Jingle, the user must provide a
* TransportManager that will handle the resolution of potential IP addresses taht can be used to transport the streaming (jmf).
* This TransportManager can be initialized with several default resolvers,
* including a fixed solver that can be used when the address and port are know
* in advance.
* This API have ready to use Transport Managers, for instance: BasicTransportManager, STUNTransportManager, BridgedTransportManager.
*
* You should also especify a JingleMediaManager if you want that JingleManager assume Media control
* Using a JingleMediaManager implementation is the easier way to implement a Jingle Application.
*
* Otherwise before creating an outgoing connection, the user must create jingle session
* listeners that will be called when different events happen. The most
* important event is <i>sessionEstablished()</i>, that will be called when all
* the negotiations are finished, providing the payload type for the
* transmission as well as the remote and local addresses and ports for the
* communication. See JingleSessionListener for a complete list of events that can be
* observed.
*
* This is an example of how to use the JingleManager:
* <i>This example implements a Jingle VOIP Call between two users.</i>
*
* <pre>
*
* To wait for an Incoming Jingle Session:
*
* try {
*
* // Connect to a XMPP Server
* XMPPConnection x1 = new XMPPConnection("xmpp.com");
* x1.connect();
* x1.login("juliet", "juliet");
*
* // Create a JingleManager using a BasicResolver
* final JingleManager jm1 = new JingleManager(
* x1, new BasicTransportManager());
*
* // Create a JingleMediaManager. In this case using Jingle Audio Media API
* JingleMediaManager jingleMediaManager = new AudioMediaManager();
*
* // Set the JingleMediaManager
* jm1.setMediaManager(jingleMediaManager);
*
* // Listen for incoming calls
* jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
* public void sessionRequested(JingleSessionRequest request) {
*
* try {
* // Accept the call
* IncomingJingleSession session = request.accept();
*
*
* // Start the call
* session.start();
* } catch (XMPPException e) {
* e.printStackTrace();
* }
*
* }
* });
*
* Thread.sleep(15000);
*
* } catch (Exception e) {
* e.printStackTrace();
* }
*
* To create an Outgoing Jingle Session:
*
* try {
*
* // Connect to a XMPP Server
* XMPPConnection x0 = new XMPPConnection("xmpp.com");
* x0.connect();
* x0.login("romeo", "romeo");
*
* // Create a JingleManager using a BasicResolver
* final JingleManager jm0 = new JingleManager(
* x0, new BasicTransportManager());
*
* // Create a JingleMediaManager. In this case using Jingle Audio Media API
* JingleMediaManager jingleMediaManager = new AudioMediaManager(); // Using Jingle Media API
*
* // Set the JingleMediaManager
* jm0.setMediaManager(jingleMediaManager);
*
* // Create a new Jingle Call with a full JID
* OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("juliet@xmpp.com/Smack");
*
* // Start the call
* js0.start();
*
* Thread.sleep(10000);
* js0.terminate();
*
* Thread.sleep(3000);
*
* } catch (Exception e) {
* e.printStackTrace();
* }
* </pre>
*
* @author Thiago Camargo
* @author Alvaro Saurin
* @see JingleListener
* @see TransportResolver
* @see org.jivesoftware.smackx.jingle.nat.JingleTransportManager
* @see OutgoingJingleSession
* @see IncomingJingleSession
* @see JingleMediaManager
* @see org.jivesoftware.smackx.jingle.nat.BasicTransportManager , STUNTransportManager, BridgedTransportManager, TransportResolver, BridgedResolver, ICEResolver, STUNResolver and BasicResolver.
*/
public class JingleManager implements JingleSessionListener {
// non-static
final List<JingleSession> jingleSessions = new ArrayList<JingleSession>();
// Listeners for manager events (ie, session requests...)
private List<JingleSessionRequestListener> jingleSessionRequestListeners;
// Listeners for created JingleSessions
private List<CreatedJingleSessionListener> creationListeners = new ArrayList<CreatedJingleSessionListener>();
// The XMPP connection
private XMPPConnection connection;
// The Media Manager
private JingleMediaManager jingleMediaManager;
// The Jingle transport manager
private final JingleTransportManager jingleTransportManager;
static {
ProviderManager providerManager = ProviderManager.getInstance();
providerManager.addIQProvider("jingle", "http://jabber.org/protocol/jingle",
new org.jivesoftware.smackx.provider.JingleProvider());
providerManager.addExtensionProvider("description", "http://jabber.org/protocol/jingle/description/audio",
new org.jivesoftware.smackx.provider.JingleContentDescriptionProvider.Audio());
providerManager.addExtensionProvider("description", "http://jabber.org/protocol/jingle/description/audio",
new org.jivesoftware.smackx.provider.JingleContentDescriptionProvider.Audio());
providerManager.addExtensionProvider("transport", "http://jabber.org/protocol/jingle/transport/ice",
new org.jivesoftware.smackx.provider.JingleTransportProvider.Ice());
providerManager.addExtensionProvider("transport", "http://jabber.org/protocol/jingle/transport/raw-udp",
new org.jivesoftware.smackx.provider.JingleTransportProvider.RawUdp());
providerManager.addExtensionProvider("busy", "http://jabber.org/protocol/jingle/info/audio",
new org.jivesoftware.smackx.provider.JingleContentInfoProvider.Audio.Busy());
providerManager.addExtensionProvider("hold", "http://jabber.org/protocol/jingle/info/audio",
new org.jivesoftware.smackx.provider.JingleContentInfoProvider.Audio.Hold());
providerManager.addExtensionProvider("mute", "http://jabber.org/protocol/jingle/info/audio",
new org.jivesoftware.smackx.provider.JingleContentInfoProvider.Audio.Mute());
providerManager.addExtensionProvider("queued", "http://jabber.org/protocol/jingle/info/audio",
new org.jivesoftware.smackx.provider.JingleContentInfoProvider.Audio.Queued());
providerManager.addExtensionProvider("ringing", "http://jabber.org/protocol/jingle/info/audio",
new org.jivesoftware.smackx.provider.JingleContentInfoProvider.Audio.Ringing());
// Enable the Jingle support on every established connection
// The ServiceDiscoveryManager class should have been already
// initialized
XMPPConnection.addConnectionCreationListener(new ConnectionCreationListener() {
public void connectionCreated(XMPPConnection connection) {
JingleManager.setServiceEnabled(connection, true);
}
});
}
/**
* Default constructor with a defined XMPPConnection, Transport Resolver and a Media Manager
* If a fully implemented JingleMediaSession is entered, JingleManager manage Jingle signalling and jmf
*
* @param connection XMPP Connection to be used
* @param jingleTransportManager transport resolver to be used
* @param jingleMediaManager an implemeted JingleMediaManager to be used.
*/
public JingleManager(XMPPConnection connection, JingleTransportManager jingleTransportManager, JingleMediaManager jingleMediaManager) {
this.connection = connection;
this.jingleTransportManager = jingleTransportManager;
this.jingleMediaManager = jingleMediaManager;
connection.getRoster().addRosterListener(new RosterListener() {
public void entriesAdded(Collection addresses) {
}
public void entriesUpdated(Collection addresses) {
}
public void entriesDeleted(Collection addresses) {
}
public void presenceChanged(String XMPPAddress) {
JingleSession aux = null;
for (JingleSession jingleSession : jingleSessions) {
if (jingleSession.getInitiator().equals(XMPPAddress) || jingleSession.getResponder().equals(XMPPAddress)) {
aux = jingleSession;
}
}
if (aux != null)
try {
aux.terminate();
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
}
/**
* Default constructor with a defined XMPPConnection and a Transport Resolver
*
* @param connection XMPP Connection to be used
* @param jingleTransportManager transport resolver to be used
*/
public JingleManager(XMPPConnection connection, JingleTransportManager jingleTransportManager) {
this(connection, jingleTransportManager, null);
}
/**
* Default constructor with a defined XMPPConnection.
* A default JingleTransportmanager based on BasicResolver will be used in this JingleManager transport.
*
* @param connection XMPP Connection to be used
*/
public JingleManager(XMPPConnection connection) {
this(connection, new JingleTransportManager() {
protected TransportResolver createResolver() {
return new BasicResolver();
}
});
}
/**
* Default constructor with a defined XMPPConnection and a defined Resolver.
* A default JingleTransportmanager based on BasicResolver will be used in this JingleManager transport.
*
* @param connection XMPP Connection to be used
*/
public JingleManager(XMPPConnection connection, final TransportResolver resolver) {
this(connection, new JingleTransportManager() {
protected TransportResolver createResolver() {
return resolver;
}
});
}
/**
* Enables or disables the Jingle support on a given connection.
*
*
* Before starting any Jingle jmf session, check that the user can handle
* it. Enable the Jingle support to indicate that this client handles Jingle
* messages.
*
* @param connection the connection where the service will be enabled or
* disabled
* @param enabled indicates if the service will be enabled or disabled
*/
public synchronized static void setServiceEnabled(XMPPConnection connection,
boolean enabled) {
if (isServiceEnabled(connection) == enabled) {
return;
}
if (enabled) {
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(
Jingle.NAMESPACE);
} else {
ServiceDiscoveryManager.getInstanceFor(connection).removeFeature(
Jingle.NAMESPACE);
}
}
/**
* Returns true if the Jingle support is enabled for the given connection.
*
* @param connection the connection to look for Jingle support
* @return a boolean indicating if the Jingle support is enabled for the
* given connection
*/
public static boolean isServiceEnabled(XMPPConnection connection) {
return ServiceDiscoveryManager.getInstanceFor(connection).includesFeature(
Jingle.NAMESPACE);
}
/**
* Returns true if the specified user handles Jingle messages.
*
* @param connection the connection to use to perform the service discovery
* @param userID the user to check. A fully qualified xmpp ID, e.g.
* jdoe@example.com
* @return a boolean indicating whether the specified user handles Jingle
* messages
*/
public static boolean isServiceEnabled(XMPPConnection connection, String userID) {
try {
DiscoverInfo result = ServiceDiscoveryManager.getInstanceFor(connection)
.discoverInfo(userID);
return result.containsFeature(Jingle.NAMESPACE);
}
catch (XMPPException e) {
e.printStackTrace();
return false;
}
}
/**
* Get the JingleTransportManager of this JingleManager
*
* @return
*/
public JingleTransportManager getJingleTransportManager() {
return jingleTransportManager;
}
/**
* Get the Media Manager of this Jingle Manager
*
* @return
*/
public JingleMediaManager getMediaManager() {
return jingleMediaManager;
}
/**
* Set the Media Manager of this Jingle Manager
*
* @param jingleMediaManager JingleMediaManager to be used for open, close, start and stop jmf streamings
*/
public void setMediaManager(JingleMediaManager jingleMediaManager) {
this.jingleMediaManager = jingleMediaManager;
}
/**
* Add a Jingle session request listenerJingle to listen to incoming session
* requests.
*
* @param jingleSessionRequestListener an implemented JingleSessionRequestListener
* @see #removeJingleSessionRequestListener(JingleSessionRequestListener)
* @see JingleListener
*/
public synchronized void addJingleSessionRequestListener(
final JingleSessionRequestListener jingleSessionRequestListener) {
if (jingleSessionRequestListener != null) {
if (jingleSessionRequestListeners == null) {
initJingleSessionRequestListeners();
}
synchronized (jingleSessionRequestListeners) {
jingleSessionRequestListeners.add(jingleSessionRequestListener);
}
}
}
/**
* Removes a Jingle session listenerJingle.
*
* @param jingleSessionRequestListener The jingle session jingleSessionRequestListener to be removed
* @see #addJingleSessionRequestListener(JingleSessionRequestListener)
* @see JingleListener
*/
public void removeJingleSessionRequestListener(JingleSessionRequestListener jingleSessionRequestListener) {
if (jingleSessionRequestListeners == null) {
return;
}
synchronized (jingleSessionRequestListeners) {
jingleSessionRequestListeners.remove(jingleSessionRequestListener);
}
}
/**
* Adds a CreatedJingleSessionListener.
* This listener will be called when a session is created by the JingleManager instance.
*
* @param createdJingleSessionListener
*/
public void addCreationListener(CreatedJingleSessionListener createdJingleSessionListener) {
this.creationListeners.add(createdJingleSessionListener);
}
/**
* Removes a CreatedJingleSessionListener.
* This listener will be called when a session is created by the JingleManager instance.
*
* @param createdJingleSessionListener
*/
public void removeCreationListener(CreatedJingleSessionListener createdJingleSessionListener) {
this.creationListeners.remove(createdJingleSessionListener);
}
/**
* Trigger CreatedJingleSessionListeners that a session was created.
*
* @param jingleSession
*/
public void triggerSessionCreated(JingleSession jingleSession) {
jingleSessions.add(jingleSession);
jingleSession.addListener(this);
for (CreatedJingleSessionListener createdJingleSessionListener : creationListeners) {
try {
createdJingleSessionListener.sessionCreated(jingleSession);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
jingleSession.close();
System.err.println("Declined");
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
}
public void sessionClosed(String reason, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
jingleSession.removeListener(this);
jingleSessions.remove(jingleSession);
}
/**
* Register the listenerJingles, waiting for a Jingle packet that tries to
* establish a new session.
*/
private void initJingleSessionRequestListeners() {
PacketFilter initRequestFilter = new PacketFilter() {
// Return true if we accept this packet
public boolean accept(Packet pin) {
if (pin instanceof IQ) {
IQ iq = (IQ) pin;
if (iq.getType().equals(IQ.Type.SET)) {
if (iq instanceof Jingle) {
Jingle jin = (Jingle) pin;
if (jin.getAction().equals(Jingle.Action.SESSIONINITIATE)) {
return true;
}
}
}
}
return false;
}
};
jingleSessionRequestListeners = new ArrayList<JingleSessionRequestListener>();
// Start a packet listener for session initiation requests
connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
triggerSessionRequested((Jingle) packet);
}
}, initRequestFilter);
}
/**
* Disconnect all Jingle Sessions
*/
public void disconnectAllSessions() {
List<JingleSession> sessions = jingleSessions.subList(0, jingleSessions.size());
for (JingleSession jingleSession : sessions)
try {
jingleSession.terminate();
} catch (XMPPException e) {
e.printStackTrace();
}
sessions.clear();
}
/**
* Activates the listenerJingles on a Jingle session request.
*
* @param initJin the packet that must be passed to the jingleSessionRequestListener.
*/
void triggerSessionRequested(Jingle initJin) {
JingleSessionRequestListener[] jingleSessionRequestListeners = null;
// Make a synchronized copy of the listenerJingles
synchronized (this.jingleSessionRequestListeners) {
jingleSessionRequestListeners = new JingleSessionRequestListener[this.jingleSessionRequestListeners.size()];
this.jingleSessionRequestListeners.toArray(jingleSessionRequestListeners);
}
// ... and let them know of the event
JingleSessionRequest request = new JingleSessionRequest(this, initJin);
for (int i = 0; i < jingleSessionRequestListeners.length; i++) {
jingleSessionRequestListeners[i].sessionRequested(request);
}
}
// Session creation
/**
* Creates an Jingle session to start a communication with another user.
*
* @param responder the fully qualified jabber ID with resource of the other
* user.
* @param payloadTypes list of supported payload types
* @return The session on which the negotiation can be run.
*/
public OutgoingJingleSession createOutgoingJingleSession(String responder,
List<PayloadType> payloadTypes) throws XMPPException {
if (responder == null || StringUtils.parseName(responder).length() <= 0
|| StringUtils.parseServer(responder).length() <= 0
|| StringUtils.parseResource(responder).length() <= 0) {
throw new IllegalArgumentException(
"The provided user id was not fully qualified");
}
OutgoingJingleSession session;
TransportResolver resolver = jingleTransportManager.getResolver();
if (jingleMediaManager != null)
session = new OutgoingJingleSession(connection, responder, payloadTypes, resolver, jingleMediaManager);
else
session = new OutgoingJingleSession(connection, responder, payloadTypes, jingleTransportManager.getResolver());
triggerSessionCreated(session);
return session;
}
/**
* Creates an Jingle session to start a communication with another user.
*
* @param responder the fully qualified jabber ID with resource of the other
* user.
* @return the session on which the negotiation can be run.
*/
public OutgoingJingleSession createOutgoingJingleSession(String responder) throws XMPPException {
if (this.getMediaManager() == null) return null;
return createOutgoingJingleSession(responder, this.getMediaManager().getPayloads());
}
/**
* When the session request is acceptable, this method should be invoked. It
* will create an JingleSession which allows the negotiation to procede.
*
* @param request the remote request that is being accepted.
* @param payloadTypes the list of supported Payload types that can be accepted
* @return the session which manages the rest of the negotiation.
*/
IncomingJingleSession createIncomingJingleSession(
JingleSessionRequest request, List<PayloadType> payloadTypes) throws XMPPException {
if (request == null) {
throw new NullPointerException("Received request cannot be null");
}
IncomingJingleSession session;
TransportResolver resolver = jingleTransportManager.getResolver();
if (jingleMediaManager != null)
session = new IncomingJingleSession(connection, request
.getFrom(), payloadTypes, resolver, jingleMediaManager);
else
session = new IncomingJingleSession(connection, request
.getFrom(), payloadTypes, resolver);
triggerSessionCreated(session);
return session;
}
/**
* When the session request is acceptable, this method should be invoked. It
* will create an JingleSession which allows the negotiation to procede.
* This method use JingleMediaManager to select the supported Payload types.
*
* @param request the remote request that is being accepted.
* @return the session which manages the rest of the negotiation.
*/
IncomingJingleSession createIncomingJingleSession(JingleSessionRequest request) throws XMPPException {
if (request == null) {
throw new NullPointerException("JingleMediaManager is not defined");
}
if (jingleMediaManager == null) return null;
return createIncomingJingleSession(request, jingleMediaManager.getPayloads());
}
/**
* Get a session with the informed JID. If no session is found, return null.
*
* @param jid
* @return
*/
public JingleSession getSession(String jid) {
for (JingleSession jingleSession : jingleSessions) {
if (jingleSession instanceof OutgoingJingleSession) {
if (jingleSession.getResponder().equals(jid)) {
return jingleSession;
}
} else if (jingleSession instanceof IncomingJingleSession) {
if (jingleSession.getInitiator().equals(jid)) {
return jingleSession;
}
}
}
return null;
}
/**
* Reject the session. If we don't want to accept the new session, send an
* appropriate error packet.
*
* @param request the request to be rejected.
*/
protected void rejectIncomingJingleSession(JingleSessionRequest request) {
Jingle initiation = request.getJingle();
IQ rejection = JingleSession.createError(initiation.getPacketID(), initiation
.getFrom(), initiation.getTo(), 403, "Declined");
connection.sendPacket(rejection);
}
}

View file

@ -0,0 +1,354 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.listeners.JingleListener;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleError;
import java.util.ArrayList;
/**
* Basic Jingle negotiator.
* <p/>
* </p>
* <p/>
* JingleNegotiator implements some basic behavior for every Jingle negotiation.
* It implements a "state" pattern: each stage should process Jingle packets and
* act depending on the current state in the negotiation...
* <p/>
* </p>
*
* @author Alvaro Saurin
*/
public abstract class JingleNegotiator {
private State state; // Current negotiation state
private XMPPConnection connection; // The connection associated
private final ArrayList listeners = new ArrayList();
private String expectedAckId;
/**
* Default constructor.
*/
public JingleNegotiator() {
this(null);
}
/**
* Default constructor with a XMPPConnection
*
* @param connection the connection associated
*/
public JingleNegotiator(XMPPConnection connection) {
this.connection = connection;
state = null;
}
/**
* Get the XMPP connection associated with this negotiation.
*
* @return the connection
*/
public XMPPConnection getConnection() {
return connection;
}
/**
* Set the XMPP connection associated.
*
* @param connection the connection to set
*/
public void setConnection(XMPPConnection connection) {
this.connection = connection;
}
/**
* Inform if current state is null
*
* @return true if current state is null
*/
public boolean invalidState() {
return state == null;
}
/**
* Return the current state
*
* @return the state
*/
public State getState() {
return state;
}
/**
* Return the current state class
*
* @return the state
*/
public Class getStateClass() {
if (state != null) {
return state.getClass();
} else {
return Object.class;
}
}
/**
* Set the new state.
*
* @param newState the state to set
* @throws XMPPException
*/
protected void setState(State newState) {
boolean transition = newState != state;
if (transition && state != null) {
state.eventExit();
}
state = newState;
if (transition && state != null) {
state.eventEnter();
}
}
// Acks management
/**
* Add expected ID
* @param id
*/
public void addExpectedId(String id) {
expectedAckId = id;
}
/**
* Check if the passed ID is the expected ID
* @param id
* @return
*/
public boolean isExpectedId(String id) {
if (id != null) {
return id.equals(expectedAckId);
} else {
return false;
}
}
/**
* Remove and expected ID
* @param id
*/
public void removeExpectedId(String id) {
addExpectedId((String) null);
}
// Listeners
/**
* Add a Jingle session listener to listen to incoming session requests.
*
* @param li The listener
* @see org.jivesoftware.smackx.jingle.listeners.JingleListener
*/
public void addListener(JingleListener li) {
synchronized (listeners) {
listeners.add(li);
}
}
/**
* Removes a Jingle session listener.
*
* @param li The jingle session listener to be removed
* @see org.jivesoftware.smackx.jingle.listeners.JingleListener
*/
public void removeListener(JingleListener li) {
synchronized (listeners) {
listeners.remove(li);
}
}
/**
* Get a copy of the listeners
*
* @return a copy of the listeners
*/
protected ArrayList getListenersList() {
ArrayList result;
synchronized (listeners) {
result = new ArrayList(listeners);
}
return result;
}
/**
* Dispatch an incomming packet. This method is responsible for recognizing
* the packet type and, depending on the current state, deliverying the
* packet to the right event handler and wait for a response.
*
* @param iq the packet received
* @param id the ID of the response that will be sent
* @return the new packet to send (either a Jingle or an IQ error).
* @throws XMPPException
*/
public abstract IQ dispatchIncomingPacket(IQ iq, String id)
throws XMPPException;
/**
* Close the negotiation.
*/
public void close() {
setState(null);
}
/**
* A Jingle exception.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public static class JingleException extends XMPPException {
private final JingleError error;
/**
* Default constructor.
*/
public JingleException() {
super();
error = null;
}
/**
* Constructor with an error message.
*
* @param msg The message.
*/
public JingleException(String msg) {
super(msg);
error = null;
}
/**
* Constructor with an error response.
*
* @param error The error message.
*/
public JingleException(JingleError error) {
super();
this.error = error;
}
/**
* Return the error message.
*
* @return the error
*/
public JingleError getError() {
return error;
}
}
/**
* Negotiation state and events.
* <p/>
* </p>
* <p/>
* Describes the negotiation stage.
*/
public static class State {
private JingleNegotiator neg; // The negotiator
/**
* Default constructor, with a reference to the negotiator.
*
* @param neg The negotiator instance.
*/
public State(JingleNegotiator neg) {
this.neg = neg;
}
/**
* Get the negotiator
*
* @return the negotiator.
*/
public JingleNegotiator getNegotiator() {
return neg;
}
/**
* Set the negotiator.
*
* @param neg the neg to set
*/
public void setNegotiator(JingleNegotiator neg) {
this.neg = neg;
}
// State transition events
public Jingle eventAck(IQ iq) throws XMPPException {
// We have received an Ack
return null;
}
public void eventError(IQ iq) throws XMPPException {
throw new JingleException(iq.getError().getMessage());
}
public Jingle eventInvite() throws XMPPException {
throw new IllegalStateException(
"Negotiation can not be started in this state.");
}
public Jingle eventInitiate(Jingle jin) throws XMPPException {
return null;
}
public Jingle eventAccept(Jingle jin) throws XMPPException {
return null;
}
public Jingle eventRedirect(Jingle jin) throws XMPPException {
return null;
}
public Jingle eventModify(Jingle jin) throws XMPPException {
return null;
}
public Jingle eventDecline(Jingle jin) throws XMPPException {
return null;
}
public Jingle eventInfo(Jingle jin) throws XMPPException {
return null;
}
public Jingle eventTerminate(Jingle jin) throws XMPPException {
if (neg != null) {
neg.close();
}
return null;
}
public void eventEnter() {
}
public void eventExit() {
if (neg != null) {
neg.removeExpectedId(null);
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.packet.Jingle;
import java.util.List;
/**
* A Jingle session request.
*
* This class is a facade of a received Jingle request. The user can have direct
* access to the Jingle packet (<i>JingleSessionRequest.getJingle() </i>) of
* the request or can use the convencience methods provided by this class.
*
* @author Alvaro Saurin
*/
public class JingleSessionRequest {
private final Jingle jingle; // The Jingle packet
private final JingleManager manager; // The manager associated to this
// request
/**
* A recieve request is constructed from the Jingle Initiation request
* received from the initator.
*
* @param manager The manager handling this request
* @param jingle The jingle IQ recieved from the initiator.
*/
public JingleSessionRequest(JingleManager manager, Jingle jingle) {
this.manager = manager;
this.jingle = jingle;
}
/**
* Returns the fully-qualified jabber ID of the user that requested this
* session.
*
* @return Returns the fully-qualified jabber ID of the user that requested
* this session.
*/
public String getFrom() {
return jingle.getFrom();
}
/**
* Returns the session ID that uniquely identifies this session.
*
* @return Returns the session ID that uniquely identifies this session
*/
public String getSessionID() {
return jingle.getSid();
}
/**
* Returns the Jingle packet that was sent by the requestor which contains
* the parameters of the session.
*/
public Jingle getJingle() {
return jingle;
}
/**
* Accepts this request and creates the incoming Jingle session.
*
* @param pts list of supported Payload Types
* @return Returns the <b><i>IncomingJingleSession</b></i> on which the
* negotiation can be carried out.
*/
public synchronized IncomingJingleSession accept(List<PayloadType> pts) throws XMPPException {
IncomingJingleSession session = null;
synchronized (manager) {
session = manager.createIncomingJingleSession(this,
pts);
session.setInitialSessionRequest(this);
}
return session;
}
/**
* Accepts this request and creates the incoming Jingle session.
*
* @return Returns the <b><i>IncomingJingleSession</b></i> on which the
* negotiation can be carried out.
*/
public synchronized IncomingJingleSession accept() throws XMPPException {
IncomingJingleSession session = null;
synchronized (manager) {
session = manager.createIncomingJingleSession(this);
session.setInitialSessionRequest(this);
}
return session;
}
/**
* Rejects the session request.
*/
public synchronized void reject() {
synchronized (manager) {
manager.rejectIncomingJingleSession(this);
}
}
}

View file

@ -0,0 +1,440 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.listeners.JingleMediaListener;
import org.jivesoftware.smackx.jingle.listeners.JingleTransportListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.MediaNegotiator;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import org.jivesoftware.smackx.jingle.nat.TransportNegotiator;
import org.jivesoftware.smackx.jingle.nat.TransportResolver;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleContentDescription;
import org.jivesoftware.smackx.packet.JingleContentDescription.JinglePayloadType;
import org.jivesoftware.smackx.packet.JingleError;
import java.util.List;
/**
* An outgoing Jingle session implementation.
* This class has especific bahavior to Request and establish a new Jingle Session.
*
* This class is not directly used by users. Instead, users should refer to the
* JingleManager class, that will create the appropiate instance...
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public class OutgoingJingleSession extends JingleSession {
// states
private final Inviting inviting;
private final Pending pending;
private final Active active;
/**
* Constructor for a Jingle outgoing session.
*
* @param conn the XMPP connection
* @param responder the other endpoint
* @param payloadTypes A list of payload types, in order of preference.
* @param resolver The transport resolver.
*/
protected OutgoingJingleSession(XMPPConnection conn, String responder,
List payloadTypes, TransportResolver resolver) {
super(conn, conn.getUser(), responder);
setSid(generateSessionId());
// Initialize the states.
inviting = new Inviting(this);
pending = new Pending(this);
active = new Active(this);
// Create description and transport negotiatiors...
setMediaNeg(new MediaNegotiator(this, payloadTypes));
if (resolver.getType().equals(TransportResolver.Type.rawupd)) {
setTransportNeg(new TransportNegotiator.RawUdp(this, resolver));
}
if (resolver.getType().equals(TransportResolver.Type.ice)) {
setTransportNeg(new TransportNegotiator.Ice(this, resolver));
}
}
/**
* Constructor for a Jingle outgoing session with a defined Media Manager
*
* @param conn the XMPP connection
* @param responder the other endpoint
* @param payloadTypes A list of payload types, in order of preference.
* @param resolver The transport resolver.
* @param jingleMediaManager The Media Manager for this Session
*/
protected OutgoingJingleSession(XMPPConnection conn, String responder,
List payloadTypes, TransportResolver resolver, JingleMediaManager jingleMediaManager) {
this(conn, responder, payloadTypes, resolver);
this.jingleMediaManager = jingleMediaManager;
}
/**
* Initiate the negotiation with an invitation. This method must be invoked
* for starting all negotiations. It is the initial starting point and,
* afterwards, any other packet processing is done with the packet listener
* callback...
*
* @throws IllegalStateException
*/
public void start(JingleSessionRequest req) throws IllegalStateException {
if (invalidState()) {
setState(inviting);
// Use the standard behavior, using a null Jingle packet
try {
updatePacketListener();
respond((Jingle) null);
} catch (XMPPException e) {
e.printStackTrace();
close();
}
} else {
throw new IllegalStateException("Starting session without null state.");
}
}
/**
* Initiate the negotiation with an invitation. This method must be invoked
* for starting all negotiations. It is the initial starting point and,
* afterwards, any other packet processing is done with the packet listener
* callback...
*
* @throws IllegalStateException
*/
public void start() throws IllegalStateException {
start(null);
}
// States
/**
* Current state when we want to invite the other endpoint.
*/
public class Inviting extends JingleNegotiator.State {
public Inviting(JingleNegotiator neg) {
super(neg);
}
/**
* Create an invitation packet.
*/
public Jingle eventInvite() {
// Create an invitation packet, saving the Packet ID, for any ACK
return new Jingle(Jingle.Action.SESSIONINITIATE);
}
/**
* The receiver has partially accepted our invitation. We go to the
* pending state while the content and transport negotiators work...
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
*/
public Jingle eventAck(IQ iq) {
setState(pending);
return null;
}
/**
* The other endpoint has declined the invitation with an error.
*
* @throws XMPPException
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventError(org.jivesoftware.smack.packet.IQ)
*/
public void eventError(IQ iq) throws XMPPException {
triggerSessionDeclined(null);
super.eventError(iq);
}
/**
* The other endpoint wants to redirect this connection.
*/
public Jingle eventRedirect(Jingle jin) {
String redirArg = null;
// TODO: parse the redirection parameters...
triggerSessionRedirect(redirArg);
return null;
}
}
/**
* "Pending" state: we are waiting for the transport and content
* negotiators.
* <p/>
* Note: the transition from/to this state is done with listeners...
*/
public class Pending extends JingleNegotiator.State {
JingleMediaListener jingleMediaListener;
JingleTransportListener jingleTransportListener;
public Pending(JingleNegotiator neg) {
super(neg);
// Create the listeners that will send a "session-accept" when
// the sub-negotiators are done.
jingleMediaListener = new JingleMediaListener() {
public void mediaClosed(PayloadType cand) {
}
public void mediaEstablished(PayloadType pt) {
checkFullyEstablished();
}
};
jingleTransportListener = new JingleTransportListener() {
public void transportEstablished(TransportCandidate local,
TransportCandidate remote) {
checkFullyEstablished();
}
public void transportClosed(TransportCandidate cand) {
}
public void transportClosedOnError(XMPPException e) {
}
};
}
/**
* Enter in the pending state: install the listeners.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventEnter() {
// Add the listeners to the sub-negotiators...
addMediaListener(jingleMediaListener);
addTransportListener(jingleTransportListener);
}
/**
* Exit of the state: remove the listeners.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventExit()
*/
public void eventExit() {
removeMediaListener(jingleMediaListener);
removeTransportListener(jingleTransportListener);
}
/**
* Check if the session has been fully accepted by all the
* sub-negotiators and, in that case, send an "accept" message...
*/
private void checkFullyEstablished() {
if (isFullyEstablished()) {
PayloadType.Audio bestCommonAudioPt = getMediaNeg()
.getBestCommonAudioPt();
TransportCandidate bestRemoteCandidate = getTransportNeg()
.getBestRemoteCandidate();
// Ok, send a packet saying that we accept this session
// with the audio payload type and the transport
// candidate
Jingle jout = new Jingle(Jingle.Action.SESSIONACCEPT);
jout.addDescription(new JingleContentDescription.Audio(
new JinglePayloadType(bestCommonAudioPt)));
jout.addTransport(getTransportNeg().getJingleTransport(
bestRemoteCandidate));
// Send the "accept" and wait for the ACK
addExpectedId(jout.getPacketID());
sendFormattedJingle(jout);
}
}
/**
* The other endpoint has finally accepted our invitation.
*
* @throws XMPPException
*/
public Jingle eventAccept(Jingle jin) throws XMPPException {
PayloadType acceptedPayloadType = null;
TransportCandidate acceptedLocalCandidate = null;
// We process the "accepted" if we have finished the
// sub-negotiators. Maybe this is not needed (ie, the other endpoint
// can take the first valid transport candidate), but otherwise we
// must cancel the negotiators...
//
if (isFullyEstablished()) {
acceptedPayloadType = getAcceptedAudioPayloadType(jin);
acceptedLocalCandidate = getAcceptedLocalCandidate(jin);
if (acceptedPayloadType != null && acceptedLocalCandidate != null) {
if (acceptedPayloadType.equals(getMediaNeg().getBestCommonAudioPt())
&& acceptedLocalCandidate.equals(getTransportNeg()
.getAcceptedLocalCandidate())) {
setState(active);
}
} else {
throw new JingleException(JingleError.NEGOTIATION_ERROR);
}
}
return null;
}
/**
* We have received the Ack of our "accept"
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
*/
public Jingle eventAck(IQ iq) {
setState(active);
return null;
}
/**
* The other endpoint wants to redirect this connection.
*/
public Jingle eventRedirect(Jingle jin) {
String redirArg = null;
// TODO: parse the redirection parameters...
triggerSessionRedirect(redirArg);
return null;
}
/**
* The other endpoint has rejected our invitation.
*
* @throws XMPPException
*/
public Jingle eventTerminate(Jingle jin) throws XMPPException {
triggerSessionDeclined(null);
return super.eventTerminate(jin);
}
/**
* An error has occurred.
*
* @throws XMPPException
*/
public void eventError(IQ iq) throws XMPPException {
triggerSessionClosedOnError(new XMPPException(iq.getError().getMessage()));
super.eventError(iq);
}
}
/**
* State when we have an established session.
*/
public class Active extends JingleNegotiator.State {
public Active(JingleNegotiator neg) {
super(neg);
}
/**
* We have a established session: notify the listeners
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventEnter() {
PayloadType.Audio bestCommonAudioPt = getMediaNeg().getBestCommonAudioPt();
TransportCandidate bestRemoteCandidate = getTransportNeg()
.getBestRemoteCandidate();
TransportCandidate acceptedLocalCandidate = getTransportNeg()
.getAcceptedLocalCandidate();
// Trigger the session established flag
System.out.println("eventEntered");
triggerSessionEstablished(bestCommonAudioPt, bestRemoteCandidate,
acceptedLocalCandidate);
super.eventEnter();
}
/**
* Terminate the connection.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventTerminate(org.jivesoftware.smackx.packet.Jingle)
*/
public Jingle eventTerminate(Jingle jin) throws XMPPException {
triggerSessionClosed("Closed Remotely");
return super.eventTerminate(jin);
}
/**
* An error has occurred.
*
* @throws XMPPException
*/
public void eventError(IQ iq) throws XMPPException {
triggerSessionClosedOnError(new XMPPException(iq.getError().getMessage()));
super.eventError(iq);
}
}
}

View file

@ -0,0 +1,32 @@
package org.jivesoftware.smackx.jingle.listeners;
import org.jivesoftware.smackx.jingle.JingleSession;
/**
* $RCSfile$
* $Revision: $
* $Date: 17/11/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Inteface used to dispatch a event when a Jingle session is created.
*/
public interface CreatedJingleSessionListener {
public void sessionCreated(JingleSession jingleSession);
}

View file

@ -0,0 +1,40 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.listeners;
/**
* Jingle listeners interface.
*
* This is the list of events that can be observed from a JingleSession and some
* sub negotiators. This listeners can be added to different elements of the
* Jingle model.
*
* For example, a JingleManager can notify any SessionRequestListenerListener
* listener when a new session request is received. In this case, the
* <i>sessionRequested()</i> of the listener will be executed, and the listener
* will be able to <i>accept()</i> or <i>decline()</i> the invitation.
*
* @author Alvaro Saurin
*/
public interface JingleListener {
}

View file

@ -0,0 +1,50 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.listeners;
/**
* Interface for listening to jmf info events.
*/
public interface JingleMediaInfoListener extends JingleListener {
/**
* The other end is busy.
*/
public void mediaInfoBusy();
/**
* We are on hold.
*/
public void mediaInfoHold();
/**
* The jmf is muted.
*/
public void mediaInfoMute();
/**
* We are queued.
*/
public void mediaInfoQueued();
/**
* We are ringing.
*/
public void mediaInfoRinging();
}

View file

@ -0,0 +1,42 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.listeners;
import org.jivesoftware.smackx.jingle.media.PayloadType;
/**
* Interface for listening to jmf events.
*/
public interface JingleMediaListener extends JingleListener {
/**
* Notification that the jmf has been negotiated and established.
*
* @param pt The payload type agreed.
*/
public void mediaEstablished(PayloadType pt);
/**
* Notification that a payload type must be cancelled
*
* @param cand The payload type that must be closed
*/
public void mediaClosed(PayloadType cand);
}

View file

@ -0,0 +1,75 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.listeners;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
/**
* Interface for listening for session events.
*/
public interface JingleSessionListener extends JingleListener {
/**
* Notification that the session has been established. Arguments specify
* the payload type and transport to use.
*
* @param pt the Payload tyep to use
* @param remoteCandidate the remote candidate to use for connecting to the remote
* service.
* @param localCandidate the local candidate where we must listen for connections
* @param jingleSession Session that called the method
*/
public void sessionEstablished(PayloadType pt, TransportCandidate remoteCandidate,
TransportCandidate localCandidate, JingleSession jingleSession);
/**
* Notification that the session was declined.
*
* @param reason the reason (if any).
* @param jingleSession Session that called the method
*/
public void sessionDeclined(String reason, JingleSession jingleSession);
/**
* Notification that the session was redirected.
*
* @param redirection
* @param jingleSession session that called the method
*/
public void sessionRedirected(String redirection, JingleSession jingleSession);
/**
* Notification that the session was closed normally.
*
* @param reason the reason (if any).
* @param jingleSession Session that called the method
*/
public void sessionClosed(String reason, JingleSession jingleSession);
/**
* Notification that the session was closed due to an exception.
*
* @param e the exception.
* @param jingleSession session that called the method
*/
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession);
}

View file

@ -0,0 +1,36 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.listeners;
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
/**
* Interface to listener Jingle session requests.
*
* @author Alvaro Saurin
*/
public interface JingleSessionRequestListener extends JingleListener {
/**
* A request to start a session has been recieved from another user.
*
* @param request The request from the other user.
*/
public void sessionRequested(JingleSessionRequest request);
}

View file

@ -0,0 +1,56 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.listeners;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
/**
* Interface for listening to transport events.
*/
public interface JingleTransportListener extends JingleListener {
/**
* Notification that the transport has been established.
*
* @param local The transport candidate that has been used for listening
* in the local machine
* @param remote The transport candidate that has been used for
* transmitting to the remote machine
*/
public void transportEstablished(TransportCandidate local,
TransportCandidate remote);
/**
* Notification that a transport must be cancelled.
*
* @param cand The transport candidate that must be cancelled. A value
* of "null" means all the transports for this session.
*/
public void transportClosed(TransportCandidate cand);
/**
* Notification that the transport was closed due to an exception.
*
* @param e the exception.
*/
public void transportClosedOnError(XMPPException e);
}

View file

@ -0,0 +1,77 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.media;
/**
* Content info. Content info messages are complementary messages that can be
* transmitted for informing of events like "busy", "ringtone", etc.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class ContentInfo {
/**
* Audio content info messages.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public static class Audio extends ContentInfo {
public static final ContentInfo.Audio BUSY = new ContentInfo.Audio("busy");
public static final ContentInfo.Audio HOLD = new ContentInfo.Audio("hold");
public static final ContentInfo.Audio MUTE = new ContentInfo.Audio("mute");
public static final ContentInfo.Audio QUEUED = new ContentInfo.Audio("queued");
public static final ContentInfo.Audio RINGING = new ContentInfo.Audio("ringing");
private String value;
public Audio(String value) {
this.value = value;
}
public String toString() {
return value;
}
/**
* Returns the MediaInfo constant associated with the String value.
*/
public static ContentInfo fromString(String value) {
value = value.toLowerCase();
if (value.equals("busy")) {
return BUSY;
} else if (value.equals("hold")) {
return HOLD;
} else if (value.equals("mute")) {
return MUTE;
} else if (value.equals("queued")) {
return QUEUED;
} else if (value.equals("ringing")) {
return RINGING;
} else {
return null;
}
}
}
}

View file

@ -0,0 +1,85 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.media;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import java.util.ArrayList;
import java.util.List;
/**
* This class provides necessary Jingle Session jmf methods and behavior.
*
* The goal of this class is to provide a flexible way to make JingleManager control jmf streaming APIs without implement them.
* For instance you can implement a file transfer using java sockets or a VOIP Media Manager using JMF.
* You can implement many JingleMediaManager according to you necessity.
*
* @author Thiago Camargo
*/
public abstract class JingleMediaManager {
private List<PayloadType> payloads = new ArrayList<PayloadType>();
/**
* Return all supported Payloads for this Manager
*
* @return The Payload List
*/
public List<PayloadType> getPayloads() {
return payloads;
}
/**
* Adds a supported Payload type to Manager
*
* @param payloadType
*/
public void addPayloadType(PayloadType payloadType) {
payloads.add(payloadType);
}
/**
* Removes a supported Payload type from Manager
*
* @param payloadType
*/
public void removePayloadType(PayloadType payloadType) {
payloads.remove(payloadType);
}
/**
* Get the preferred Payload Type
*/
public PayloadType getPreferredPayloadType() {
//TODO a better way to choose the preferred Payload
return payloads.size() > 0 ? payloads.get(0) : null;
}
/**
* Create a Media Session Implementation
*
* @param payloadType
* @param remote
* @param local
* @return
*/
public abstract JingleMediaSession createMediaSession(PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local);
}

View file

@ -0,0 +1,119 @@
/**
* $RCSfile$
* $Revision: $
* $Date: $11-07-2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.media;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
/**
* Public Abstract Class provides a clear interface between Media Session and Jingle API.
*
* When a Jingle Session is fully stablished, we will have a Payload Type and two transport candidates defined for it.
* Smack Jingle API don´t implement Media Transmit and Receive methods.
* But provides an interface to let the user implements it using another API. For instance: JMF.
*
* <i>The Class that implements this one, must have the support to transmit and receive the jmf.</i>
* <i>This interface let the user choose his own jmf API.</i>
*
* @author Thiago Camargo
*/
public abstract class JingleMediaSession {
// Payload Type of the Session
private PayloadType payloadType;
// Local Transport details
private TransportCandidate local;
// Remote Transport details
private TransportCandidate remote;
/**
* Creates a new JingleMediaSession Instance to handle Media methods.
*
* @param payloadType Payload Type of the transmittion
* @param remote Remote accepted Transport Candidate
* @param local Local accepted Transport Candidate
*/
public JingleMediaSession(PayloadType payloadType, TransportCandidate remote,
TransportCandidate local) {
this.local = local;
this.remote = remote;
this.payloadType = payloadType;
initialize();
}
/**
* Returns the PayloadType of the Media Session
*
* @return
*/
public PayloadType getPayloadType() {
return payloadType;
}
/**
* Returns the Media Session local Candidate
*
* @return
*/
public TransportCandidate getLocal() {
return local;
}
/**
* Returns the Media Session remote Candidate
*
* @return
*/
public TransportCandidate getRemote() {
return remote;
}
/**
* Initialize the RTP Channel preparing to transmit and receive.
*/
public abstract void initialize();
/**
* Starts a RTP / UDP / TCP Transmission to the remote Candidate
*/
public abstract void startTrasmit();
/**
* Starts a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate
*/
public abstract void startReceive();
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
* @param active
*/
public abstract void setTrasmit(boolean active);
/**
* Stops a RTP / UDP / TCP Transmission to the remote Candidate
*/
public abstract void stopTrasmit();
/**
* Stops a RTP / UDP / TCP Receiver from the remote Candidate to local Candidate
*/
public abstract void stopReceive();
}

View file

@ -0,0 +1,555 @@
package org.jivesoftware.smackx.jingle.media;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.JingleNegotiator;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.listeners.JingleListener;
import org.jivesoftware.smackx.jingle.listeners.JingleMediaListener;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleContentDescription;
import org.jivesoftware.smackx.packet.JingleContentDescription.JinglePayloadType;
import org.jivesoftware.smackx.packet.JingleError;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Manager for jmf descriptor negotiation.
*
*
* This class is responsible for managing the descriptor negotiation process,
* handling all the xmpp packets interchange and the stage control.
*
* @author Alvaro Saurin
*/
public class MediaNegotiator extends JingleNegotiator {
private final JingleSession session; // The session this negotiation
// Local and remote payload types...
private final List<PayloadType> localAudioPts = new ArrayList<PayloadType>();
private final List<PayloadType> remoteAudioPts = new ArrayList<PayloadType>();
private PayloadType.Audio bestCommonAudioPt;
// states
private final Inviting inviting;
private final Accepting accepting;
private final Pending pending;
private final Active active;
/**
* Default constructor. The constructor establishes some basic parameters,
* but it does not start the negotiation. For starting the negotiation, call
* startNegotiation.
*
* @param js The jingle session.
*/
public MediaNegotiator(JingleSession js, List<PayloadType> pts) {
super(js.getConnection());
session = js;
bestCommonAudioPt = null;
if (pts != null) {
if (pts.size() > 0) {
localAudioPts.addAll(pts);
}
}
// Create the states...
inviting = new Inviting(this);
accepting = new Accepting(this);
pending = new Pending(this);
active = new Active(this);
}
/**
* Dispatch an incomming packet. The medthod is responsible for recognizing
* the packet type and, depending on the current state, deliverying the
* packet to the right event handler and wait for a response.
*
* @param iq the packet received
* @return the new Jingle packet to send.
* @throws XMPPException
*/
public IQ dispatchIncomingPacket(IQ iq, String id) throws XMPPException {
IQ jout = null;
if (invalidState()) {
if (iq == null) {
// With a null packet, we are just inviting the other end...
setState(inviting);
jout = getState().eventInvite();
} else {
if (iq instanceof Jingle) {
// If there is no specific jmf action associated, then we
// are being invited to a new session...
setState(accepting);
jout = getState().eventInitiate((Jingle) iq);
} else {
throw new IllegalStateException(
"Invitation IQ received is not a Jingle packet in Media negotiator.");
}
}
} else {
if (iq == null) {
return null;
} else {
if (iq.getType().equals(IQ.Type.ERROR)) {
// Process errors
getState().eventError(iq);
} else if (iq.getType().equals(IQ.Type.RESULT)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {
jout = getState().eventAck(iq);
removeExpectedId(iq.getPacketID());
}
} else if (iq instanceof Jingle) {
// Get the action from the Jingle packet
Jingle jin = (Jingle) iq;
Jingle.Action action = jin.getAction();
if (action != null) {
if (action.equals(Jingle.Action.CONTENTACCEPT)) {
jout = getState().eventAccept(jin);
} else if (action.equals(Jingle.Action.CONTENTDECLINE)) {
jout = getState().eventDecline(jin);
} else if (action.equals(Jingle.Action.DESCRIPTIONINFO)) {
jout = getState().eventInfo(jin);
} else if (action.equals(Jingle.Action.CONTENTMODIFY)) {
jout = getState().eventModify(jin);
}
// Any unknown action will be ignored: it is not a msg
// to us...
}
}
}
}
// Save the Id for any ACK
if (id != null) {
addExpectedId(id);
} else {
if (jout != null) {
addExpectedId(jout.getPacketID());
}
}
return jout;
}
/**
* Return true if the content is negotiated.
*
* @return true if the content is negotiated.
*/
public boolean isEstablished() {
return getBestCommonAudioPt() != null;
}
/**
* Return true if the content is fully negotiated.
*
* @return true if the content is fully negotiated.
*/
public boolean isFullyEstablished() {
return isEstablished() && getState() == active;
}
// Payload types
private PayloadType.Audio calculateBestCommonAudioPt(List remoteAudioPts) {
final ArrayList commonAudioPtsHere = new ArrayList();
final ArrayList commonAudioPtsThere = new ArrayList();
PayloadType.Audio result = null;
if (!remoteAudioPts.isEmpty()) {
commonAudioPtsHere.addAll(localAudioPts);
commonAudioPtsHere.retainAll(remoteAudioPts);
commonAudioPtsThere.addAll(remoteAudioPts);
commonAudioPtsThere.retainAll(localAudioPts);
if (!commonAudioPtsHere.isEmpty() && !commonAudioPtsThere.isEmpty()) {
PayloadType.Audio bestPtHere = (PayloadType.Audio) commonAudioPtsHere
.get(0);
PayloadType.Audio bestPtThere = (PayloadType.Audio) commonAudioPtsThere
.get(0);
// If both match, use it
if (bestPtHere.equals(bestPtThere)) {
result = bestPtHere;
} else {
// Otherwise, use the one of the initiator...
// FIXME: this is an invented behavior!!!
String initiator = session.getInitiator();
String me = session.getConnection().getUser();
if (initiator.equals(me)) {
result = bestPtHere;
} else {
result = bestPtThere;
}
}
}
}
return result;
}
private List obtainPayloads(Jingle jin) {
List result = new ArrayList();
Iterator iDescr = jin.getDescriptions();
// Add the list of payloads: iterate over the descriptions...
while (iDescr.hasNext()) {
JingleContentDescription.Audio descr = (JingleContentDescription.Audio) iDescr
.next();
if (descr != null) {
// ...and, then, over the payloads.
// Note: we use the last "description" in the packet...
result.clear();
result.addAll(descr.getAudioPayloadTypesList());
}
}
return result;
}
/**
* Adds a payload type to the list of remote payloads.
*
* @param pt the remote payload type
*/
public void addRemoteAudioPayloadType(PayloadType.Audio pt) {
if (pt != null) {
synchronized (remoteAudioPts) {
remoteAudioPts.add(pt);
}
}
}
/**
* Create an offer for the list of audio payload types.
*
* @return a new Jingle packet with the list of audio Payload Types
*/
private Jingle getAudioPayloadTypesOffer() {
JingleContentDescription.Audio audioDescr = new JingleContentDescription.Audio();
// Add the list of payloads for audio and create a
// JingleContentDescription
// where we announce our payloads...
audioDescr.addAudioPayloadTypes(localAudioPts);
return new Jingle(audioDescr);
}
// Predefined messages and Errors
/**
* Create an IQ "accept" message.
*/
private Jingle createAcceptMessage() {
Jingle jout = null;
// If we hava a common best codec, send an accept right now...
jout = new Jingle(Jingle.Action.CONTENTACCEPT);
jout.addDescription(new JingleContentDescription.Audio(
new JinglePayloadType.Audio(bestCommonAudioPt)));
return jout;
}
// Payloads
/**
* Get the best common codec between both parts.
*
* @return The best common PayloadType codec.
*/
public PayloadType.Audio getBestCommonAudioPt() {
return bestCommonAudioPt;
}
// Events
/**
* Trigger a session established event.
*
* @param bestPt payload type that has been agreed.
*/
protected void triggerMediaEstablished(PayloadType bestPt) {
ArrayList listeners = getListenersList();
Iterator iter = listeners.iterator();
while (iter.hasNext()) {
JingleListener li = (JingleListener) iter.next();
if (li instanceof JingleMediaListener) {
JingleMediaListener mli = (JingleMediaListener) li;
mli.mediaEstablished(bestPt);
}
}
}
/**
* Trigger a jmf closed event.
*
* @param currPt current payload type that is cancelled.
*/
protected void triggerMediaClosed(PayloadType currPt) {
ArrayList listeners = getListenersList();
Iterator iter = listeners.iterator();
while (iter.hasNext()) {
JingleListener li = (JingleListener) iter.next();
if (li instanceof JingleMediaListener) {
JingleMediaListener mli = (JingleMediaListener) li;
mli.mediaClosed(currPt);
}
}
}
/**
* Terminate the jmf negotiator
*/
public void close() {
super.close();
}
// States
/**
* First stage when we send a session request.
*/
public class Inviting extends JingleNegotiator.State {
public Inviting(MediaNegotiator neg) {
super(neg);
}
/**
* Create an initial Jingle packet, with the list of payload types that
* we support. The list is in order of preference.
*/
public Jingle eventInvite() {
return getAudioPayloadTypesOffer();
}
/**
* We have received the ACK for our invitation.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
*/
public Jingle eventAck(IQ iq) {
setState(pending);
return null;
}
}
/**
* We are accepting connections.
*/
public class Accepting extends JingleNegotiator.State {
public Accepting(MediaNegotiator neg) {
super(neg);
}
/**
* We have received an invitation! Respond with a list of our payload
* types...
*/
public Jingle eventInitiate(Jingle jin) {
synchronized (remoteAudioPts) {
remoteAudioPts.addAll(obtainPayloads(jin));
}
return getAudioPayloadTypesOffer();
}
/**
* Process the ACK of our list of codecs (our offer).
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
*/
public Jingle eventAck(IQ iq) throws XMPPException {
Jingle response = null;
if (!remoteAudioPts.isEmpty()) {
// Calculate the best common codec
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
// and send an accept if we havee an agreement...
if (bestCommonAudioPt != null) {
response = createAcceptMessage();
} else {
throw new JingleException(JingleError.NO_COMMON_PAYLOAD);
}
setState(pending);
}
return response;
}
}
/**
* Pending class: we are waiting for the other enpoint, that must say if it
* accepts or not...
*/
public class Pending extends JingleNegotiator.State {
public Pending(MediaNegotiator neg) {
super(neg);
}
/**
* A content info has been received. This is done for publishing the
* list of payload types...
*
* @param jin The input packet
* @return a Jingle packet
* @throws JingleException
*/
public Jingle eventInfo(Jingle jin) throws JingleException {
PayloadType.Audio oldBestCommonAudioPt = bestCommonAudioPt;
List offeredPayloads = new ArrayList();
Jingle response = null;
boolean ptChange = false;
offeredPayloads = obtainPayloads(jin);
if (!offeredPayloads.isEmpty()) {
synchronized (remoteAudioPts) {
remoteAudioPts.clear();
remoteAudioPts.addAll(offeredPayloads);
}
// Calculate the best common codec
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
if (bestCommonAudioPt != null) {
// and send an accept if we have an agreement...
ptChange = !bestCommonAudioPt.equals(oldBestCommonAudioPt);
if (oldBestCommonAudioPt == null || ptChange) {
response = createAcceptMessage();
}
} else {
throw new JingleException(JingleError.NO_COMMON_PAYLOAD);
}
}
// Parse the Jingle and get the payload accepted
return response;
}
/**
* A jmf description has been accepted. In this case, we must save the
* accepted payload type and notify any listener...
*
* @param jin The input packet
* @return a Jingle packet
* @throws JingleException
*/
public Jingle eventAccept(Jingle jin) throws JingleException {
PayloadType.Audio agreedCommonAudioPt;
List offeredPayloads = new ArrayList();
Jingle response = null;
if (bestCommonAudioPt == null) {
// Update the best common audio PT
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
response = createAcceptMessage();
}
offeredPayloads = obtainPayloads(jin);
if (!offeredPayloads.isEmpty()) {
if (offeredPayloads.size() == 1) {
agreedCommonAudioPt = (PayloadType.Audio) offeredPayloads.get(0);
if (bestCommonAudioPt != null) {
// If the accepted PT matches the best payload
// everything is fine
if (!agreedCommonAudioPt.equals(bestCommonAudioPt)) {
throw new JingleException(JingleError.NEGOTIATION_ERROR);
}
}
} else if (offeredPayloads.size() > 1) {
throw new JingleException(JingleError.MALFORMED_STANZA);
}
}
return response;
}
/**
* The other part has declined the our codec...
*
* @throws JingleException
*/
public Jingle eventDecline(Jingle inJingle) throws JingleException {
triggerMediaClosed(getBestCommonAudioPt());
throw new JingleException();
}
/*
* (non-Javadoc)
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventError(org.jivesoftware.smack.packet.IQ)
*/
public void eventError(IQ iq) throws XMPPException {
triggerMediaClosed(getBestCommonAudioPt());
super.eventError(iq);
}
/**
* ACK received.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
*/
public Jingle eventAck(IQ iq) {
if (isEstablished()) {
setState(active);
}
return null;
}
}
/**
* "Active" state: we have an agreement about the codec...
*/
public class Active extends JingleNegotiator.State {
public Active(MediaNegotiator neg) {
super(neg);
}
/**
* We have an agreement.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventEnter() {
triggerMediaEstablished(getBestCommonAudioPt());
super.eventEnter();
}
/**
* We are breaking the contract...
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventExit()
*/
public void eventExit() {
triggerMediaClosed(getBestCommonAudioPt());
super.eventExit();
}
}
}

View file

@ -0,0 +1,289 @@
package org.jivesoftware.smackx.jingle.media;
/**
* Represents a payload type.
*
* @author Alvaro Saurin
*/
public class PayloadType {
public static int MAX_FIXED_PT = 95;
public static int INVALID_PT = 65535;
private int id;
private String name;
private int channels;
/**
* Constructor with Id, name and number of channels
*
* @param id The identifier
* @param name A name
* @param channels The number of channels
*/
public PayloadType(int id, final String name, int channels) {
super();
this.id = id;
this.name = name;
this.channels = channels;
}
/**
* Default constructor.
*/
public PayloadType() {
this(INVALID_PT, null, 1);
}
/**
* Constructor with Id and name
*
* @param id The identification
* @param name A name
*/
public PayloadType(int id, String name) {
this(id, name, 1);
}
/**
* Copy constructor
*
* @param pt The other payload type.
*/
public PayloadType(PayloadType pt) {
this(pt.getId(), pt.getName(), pt.getChannels());
}
/**
* Get the ID.
*
* @return the ID
*/
public int getId() {
return id;
}
/**
* Set the ID.
*
* @param id ID
*/
public void setId(int id) {
this.id = id;
}
/**
* Get the printable name.
*
* @return printable name for the payload type
*/
public String getName() {
return name;
}
/**
* Set the printable name.
*
* @param name the printable name
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the number of channels used by this payload type.
*
* @return the number of channels
*/
public int getChannels() {
return channels;
}
/**
* Set the numer of channels for a payload type.
*
* @param channels The number of channels
*/
public void setChannels(int channels) {
this.channels = channels;
}
/**
* Return true if the Payload type is not valid
*
* @return true if the payload type is invalid
*/
public boolean isNull() {
if (getId() == INVALID_PT) {
return true;
} else if (getName() == null) {
return true;
}
return false;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + getChannels();
result = PRIME * result + getId();
result = PRIME * result + (getName() == null ? 0 : getName().hashCode());
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final PayloadType other = (PayloadType) obj;
if (getChannels() != other.getChannels()) {
return false;
}
if (getId() != other.getId()) {
return false;
}
// Compare names only for dynamic payload types
if (getId() > MAX_FIXED_PT) {
if (getName() == null) {
if (other.getName() != null) {
return false;
}
} else if (!getName().equals(other.getName())) {
return false;
}
}
return true;
}
/**
* Audio payload type.
*/
public static class Audio extends PayloadType {
private int clockRate;
/**
* Constructor with all the attributes of an Audio payload type
*
* @param id The identifier
* @param name The name assigned to this payload type
* @param channels The number of channels
* @param rate The clock rate
*/
public Audio(int id, String name, int channels, int rate) {
super(id, name, channels);
clockRate = rate;
}
/**
* Empty constructor.
*/
public Audio() {
super();
clockRate = 0;
}
/**
* Constructor with Id and name
*
* @param id the Id for the payload type
* @param name the name of the payload type
*/
public Audio(int id, String name) {
super(id, name);
clockRate = 0;
}
/**
* Copy constructor
*
* @param pt the other payload type
*/
public Audio(PayloadType pt) {
super(pt);
clockRate = 0;
}
/**
* Copy constructor
*
* @param pt the other payload type
*/
public Audio(PayloadType.Audio pt) {
super(pt);
clockRate = pt.getClockRate();
}
/**
* Get the sampling clockRate for a payload type
*
* @return The sampling clockRate
*/
public int getClockRate() {
return clockRate;
}
/**
* Set tha sampling clockRate for a playload type.
*
* @param rate The sampling clockRate
*/
public void setClockRate(int rate) {
clockRate = rate;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
final int PRIME = 31;
int result = super.hashCode();
result = PRIME * result + getClockRate();
return result;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Audio other = (Audio) obj;
if (getClockRate() != other.getClockRate()) {
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,102 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* Basic Resolver takes all IP addresses of the interfaces and uses the
* first non-loopback address.
* A very simple and easy to use resolver.
*/
public class BasicResolver extends TransportResolver {
/**
* Constructor.
*/
public BasicResolver() {
super();
}
/**
* Resolve the IP address.
* <p/>
* The BasicResolver takes the IP addresses of the interfaces and uses the
* first non-loopback, non-linklocal and non-sitelocal address.
*/
public synchronized void resolve() throws XMPPException {
setResolveInit();
clearCandidates();
Enumeration ifaces = null;
try {
ifaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
while (ifaces.hasMoreElements()) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
Enumeration iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = (InetAddress) iaddresses.nextElement();
if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress() && !iaddress.isSiteLocalAddress()) {
TransportCandidate tr = new TransportCandidate.Fixed(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName(), getFreePort());
tr.setLocalIp(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName());
addCandidate(tr);
setResolveEnd();
return;
}
}
}
try {
ifaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
while (ifaces.hasMoreElements()) {
NetworkInterface iface = (NetworkInterface) ifaces.nextElement();
Enumeration iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = (InetAddress) iaddresses.nextElement();
if (!iaddress.isLoopbackAddress() && !iaddress.isLinkLocalAddress()) {
TransportCandidate tr = new TransportCandidate.Fixed(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName(), getFreePort());
tr.setLocalIp(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName());
addCandidate(tr);
setResolveEnd();
return;
}
}
}
try {
TransportCandidate tr = new TransportCandidate.Fixed(InetAddress.getLocalHost().getHostAddress() != null ? InetAddress.getLocalHost().getHostAddress() : InetAddress.getLocalHost().getHostName(), getFreePort());
tr.setLocalIp(InetAddress.getLocalHost().getHostAddress() != null ? InetAddress.getLocalHost().getHostAddress() : InetAddress.getLocalHost().getHostName());
addCandidate(tr);
} catch (Exception e) {
e.printStackTrace();
}
setResolveEnd();
}
public void initialize() throws XMPPException {
setInitialized();
}
public void cancel() throws XMPPException {
// Nothing to do here
}
}

View file

@ -0,0 +1,32 @@
package org.jivesoftware.smackx.jingle.nat;
/**
* $RCSfile$
* $Revision: $
* $Date: 15/11/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A Basic Jingle Transport Manager implementation.
*
*/
public class BasicTransportManager extends JingleTransportManager{
protected TransportResolver createResolver() {
return new BasicResolver();
}
}

View file

@ -0,0 +1,94 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import java.util.Random;
/**
* Bridged Resolver use a RTPBridge Service to add a relayed candidate.
* A very reliable solution for NAT Traversal.
*
* The resolver verify is the XMPP Server that the client is connected offer this service.
* If the server supports, a candidate is requested from the service.
* The resolver adds this candidate
*/
public class BridgedResolver extends TransportResolver{
XMPPConnection connection;
Random random = new Random();
long sid;
/**
* Constructor.
* A Bridged Resolver need a XMPPConnection to connect to a RTP Bridge.
*/
public BridgedResolver(XMPPConnection connection) {
super();
this.connection = connection;
}
/**
* Resolve Bridged Candidate.
* <p/>
* The BridgedResolver takes the IP addresse and ports of a jmf proxy service.
*/
public synchronized void resolve() throws XMPPException {
setResolveInit();
clearCandidates();
sid = Math.abs(random.nextLong());
RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, String.valueOf(sid));
BasicResolver basicResolver = new BasicResolver();
basicResolver.initializeAndWait();
basicResolver.resolve();
TransportCandidate localCandidate = new TransportCandidate.Fixed(
rtpBridge.getIp(), rtpBridge.getPortA());
localCandidate.setLocalIp(basicResolver.getCandidate(0).getLocalIp());
TransportCandidate remoteCandidate = new TransportCandidate.Fixed(
rtpBridge.getIp(), rtpBridge.getPortB());
remoteCandidate.setLocalIp(basicResolver.getCandidate(0).getLocalIp());
localCandidate.setSymmetric(remoteCandidate);
remoteCandidate.setSymmetric(localCandidate);
localCandidate.setPassword(rtpBridge.getPass());
remoteCandidate.setPassword(rtpBridge.getPass());
localCandidate.setSessionId(rtpBridge.getSid());
remoteCandidate.setSessionId(rtpBridge.getSid());
localCandidate.setConnection(this.connection);
remoteCandidate.setConnection(this.connection);
addCandidate(localCandidate);
setResolveEnd();
}
public void initialize() throws XMPPException {
clearCandidates();
if (!RTPBridge.serviceAvailable(connection)) {
setInitialized();
throw new XMPPException("No RTP Bridge service available");
}
setInitialized();
}
public void cancel() throws XMPPException {
// Nothing to do here
}
}

View file

@ -0,0 +1,75 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.listeners.CreatedJingleSessionListener;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingle.media.PayloadType;
/**
* $RCSfile$
* $Revision: $
* $Date: 15/11/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A Jingle Transport Manager implementation to be used for NAT Networks.
* This kind of transport needs that the connected XMPP Server provide a Bridge Service. (http://www.jivesoftware.com/protocol/rtpbridge)
* To relay the jmf outside the NAT.
*
* @author Thiago Camargo
*/
public class BridgedTransportManager extends JingleTransportManager implements JingleSessionListener, CreatedJingleSessionListener {
XMPPConnection xmppConnection;
public BridgedTransportManager(XMPPConnection xmppConnection) {
super();
this.xmppConnection = xmppConnection;
}
// Return the correspondent resolver
protected TransportResolver createResolver() {
BridgedResolver bridgedResolver = new BridgedResolver(this.xmppConnection);
return bridgedResolver;
}
// Implement a Session Listener to relay candidates after establishment
public void sessionEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) {
RTPBridge rtpBridge = RTPBridge.relaySession(lc.getConnection(), lc.getSessionId(), lc.getPassword(), rc, lc);
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
// Session Created
public void sessionCreated(JingleSession jingleSession) {
jingleSession.addListener(this);
}
}

View file

@ -0,0 +1,67 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
/**
* The FixedResolver is a resolver where
* the external address and port are previously known when the object is
* initialized.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public class FixedResolver extends TransportResolver {
TransportCandidate fixedCandidate;
/**
* Constructor.
*/
public FixedResolver(String ip, int port) {
super();
setFixedCandidate(ip, port);
}
/**
* Create a basic resolver, where we provide the IP and port.
*
* @param ip an IP address
* @param port a port
*/
public void setFixedCandidate(String ip, int port) {
System.out.println("FIXED");
fixedCandidate = new TransportCandidate.Fixed(ip, port);
}
/**
* Resolve the IP address.
*/
public synchronized void resolve() throws XMPPException {
if (!isResolving()) {
setResolveInit();
clearCandidates();
if (fixedCandidate.getLocalIp() == null)
fixedCandidate.setLocalIp(fixedCandidate.getIp());
if (fixedCandidate != null) {
addCandidate(fixedCandidate);
}
setResolveEnd();
}
}
/**
* Initialize the resolver.
*
* @throws XMPPException
*/
public void initialize() throws XMPPException {
setInitialized();
}
public void cancel() throws XMPPException {
// Nothing to do here
}
}

View file

@ -0,0 +1,85 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright 2003-2005 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.nat;
import de.javawi.jstun.test.demo.ice.Candidate;
import de.javawi.jstun.test.demo.ice.ICENegociator;
import de.javawi.jstun.util.UtilityException;
import org.jivesoftware.smack.XMPPException;
import java.net.UnknownHostException;
import java.util.List;
/**
* ICE Resolver for Jingle transport method that results in sending data between two entities using the Interactive Connectivity Establishment (ICE) methodology. (XEP-0176)
* The goal of this resolver is to make possible to establish and manage out-of-band connections between two XMPP entities, even if they are behind Network Address Translators (NATs) or firewalls.
* To use this resolver you must have a STUN Server and be in a non STUN blocked network.
*
* @author Thiago Camargo
*/
public class ICEResolver extends TransportResolver {
public ICEResolver() {
super();
this.setType(Type.ice);
}
public void initialize() throws XMPPException {
if (!isResolving() && !isResolved()) {
System.out.println("Initialized");
ICENegociator cc = new ICENegociator((short) 1);
// gather candidates
cc.gatherCandidateAddresses();
// priorize candidates
cc.prioritizeCandidates();
// get SortedCandidates
//List<Candidate> sortedCandidates = cc.getSortedCandidates();
for (Candidate candidate : cc.getSortedCandidates())
try {
TransportCandidate transportCandidate = new TransportCandidate.Ice(candidate.getAddress().getInetAddress().getHostAddress(), 1, candidate.getNetwork(), "1", candidate.getPort(), "1", candidate.getPriority());
transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress());
this.addCandidate(transportCandidate);
System.out.println("C: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " p:" + candidate.getPriority());
} catch (UtilityException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
this.setInitialized();
}
public void cancel() throws XMPPException {
}
/**
* Resolve the IP and obtain a valid transport method.
*/
public synchronized void resolve() throws XMPPException {
this.setResolveInit();
System.out.println("Resolve");
this.setResolveEnd();
}
}

View file

@ -0,0 +1,46 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
/**
* $RCSfile$
* $Revision: $
* $Date: 02/01/2007
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class ICETransportManager extends JingleTransportManager {
ICEResolver iceResolver = null;
public ICETransportManager() {
iceResolver = new ICEResolver();
try {
iceResolver.initializeAndWait();
} catch (XMPPException e) {
e.printStackTrace();
}
}
protected TransportResolver createResolver() {
try {
iceResolver.resolve();
} catch (XMPPException e) {
e.printStackTrace();
}
return iceResolver;
}
}

View file

@ -0,0 +1,54 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
/**
* Transport manager for Jingle.
*
* This class makes easier the use of transport resolvers by presenting a simple
* interface for algorithm selection. The transport manager also keeps the match
* between the resolution method and the &lt;transport&gt; element present in
* Jingle packets.
*
* As Jingle have many transport methods (official and unofficial methods),
* this abstract class helps us to extends the transport support of the API.
*
* This class must be used with a JingleManager instance in the following way:
*
* JingleManager jingleManager = new JingleManager(xmppConnection, new BasicTransportManager());
*
* @author Thiago Camargo
*/
public abstract class JingleTransportManager {
// This class implements the context of a Strategy pattern...
/**
* Deafult contructor.
*/
public JingleTransportManager() {
}
/**
* Get a new Transport Resolver to be used in a Jingle Session
*
* @return
*/
public TransportResolver getResolver() throws XMPPException {
TransportResolver resolver = createResolver();
if (resolver == null) {
resolver = new BasicResolver();
}
resolver.initializeAndWait();
return resolver;
}
/**
* Create a Transport Resolver instance according to the implementation.
*
* @return
*/
protected abstract TransportResolver createResolver();
}

View file

@ -0,0 +1,462 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright 2003-2005 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketIDFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smackx.ServiceDiscoveryManager;
import org.jivesoftware.smackx.packet.DiscoverItems;
import org.xmlpull.v1.XmlPullParser;
import java.util.Iterator;
/**
* RTPBridge IQ Packet used to request and retrieve a RTPBridge Candidates that can be used for a Jingle Media Transmission between two parties that are behind NAT.
* This Jingle Bridge has all the needed information to establish a full UDP Channel (Send and Receive) between two parties.
* <i>This transport method should be used only if other transport methods are not allowed. Or if you want a more reliable transport.</i>
* <p/>
* High Level Usage Example:
* <p/>
* RTPBridge rtpBridge = RTPBridge.getRTPBridge(xmppConnection, sessionID);
*
* @author Thiago Camargo
*/
public class RTPBridge extends IQ {
private String sid;
private String pass;
private String ip;
private String name;
private int portA = -1;
private int portB = -1;
private String hostA;
private String hostB;
private BridgeAction bridgeAction = BridgeAction.create;
private enum BridgeAction {
create, change
}
/**
* Element name of the packet extension.
*/
public static final String NAME = "rtpbridge";
/**
* Element name of the packet extension.
*/
public static final String ELEMENT_NAME = "rtpbridge";
/**
* Namespace of the packet extension.
*/
public static final String NAMESPACE = "http://www.jivesoftware.com/protocol/rtpbridge";
static {
ProviderManager.getInstance().addIQProvider(NAME, NAMESPACE, new Provider());
}
/**
* Creates a RTPBridge Instance with defined Session ID
*
* @param sid
*/
public RTPBridge(String sid) {
this.sid = sid;
}
/**
* Creates a RTPBridge Instance with defined Session ID
*
* @param sid
* @param bridgeAction
*/
public RTPBridge(String sid, BridgeAction bridgeAction) {
this.sid = sid;
this.bridgeAction = bridgeAction;
}
/**
* Creates a RTPBridge Packet without Session ID
*/
public RTPBridge() {
}
/**
* Get the attributes string
*/
public String getAttributes() {
StringBuilder str = new StringBuilder();
if (getSid() != null)
str.append(" sid='").append(getSid()).append("'");
if (getPass() != null)
str.append(" pass='").append(getPass()).append("'");
if (getPortA() != -1)
str.append(" porta='").append(getPortA()).append("'");
if (getPortB() != -1)
str.append(" portb='").append(getPortB()).append("'");
if (getHostA() != null)
str.append(" hosta='").append(getHostA()).append("'");
if (getHostB() != null)
str.append(" hostb='").append(getHostB()).append("'");
return str.toString();
}
/**
* Get the Session ID of the Packet (usually same as Jingle Session ID)
*
* @return
*/
public String getSid() {
return sid;
}
/**
* Set the Session ID of the Packet (usually same as Jingle Session ID)
*
* @param sid
*/
public void setSid(String sid) {
this.sid = sid;
}
/**
* Get the Host A IP Address
*
* @return
*/
public String getHostA() {
return hostA;
}
/**
* Set the Host A IP Address
*
* @param hostA
*/
public void setHostA(String hostA) {
this.hostA = hostA;
}
/**
* Get the Host B IP Address
*
* @return
*/
public String getHostB() {
return hostB;
}
/**
* Set the Host B IP Address
*
* @param hostB
*/
public void setHostB(String hostB) {
this.hostB = hostB;
}
/**
* Get Side A receive port
*
* @return
*/
public int getPortA() {
return portA;
}
/**
* Set Side A receive port
*
* @param portA
*/
public void setPortA(int portA) {
this.portA = portA;
}
/**
* Get Side B receive port
*
* @return
*/
public int getPortB() {
return portB;
}
/**
* Set Side B receive port
*
* @param portB
*/
public void setPortB(int portB) {
this.portB = portB;
}
/**
* Get the RTP Bridge IP
*
* @return
*/
public String getIp() {
return ip;
}
/**
* Set the RTP Bridge IP
*
* @param ip
*/
public void setIp(String ip) {
this.ip = ip;
}
/**
* Get the RTP Agent Pass
*
* @return
*/
public String getPass() {
return pass;
}
/**
* Set the RTP Agent Pass
*
* @param pass
*/
public void setPass(String pass) {
this.pass = pass;
}
/**
* Get the name of the Candidate
*
* @return
*/
public String getName() {
return name;
}
/**
* Set the name of the Candidate
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* Get the Child Element XML of the Packet
*
* @return
*/
public String getChildElementXML() {
StringBuilder str = new StringBuilder();
str.append("<" + ELEMENT_NAME + " xmlns='" + NAMESPACE + "' sid='").append(sid).append("'>");
if (bridgeAction.equals(BridgeAction.create))
str.append("<candidate/>");
else
str.append("<relay ").append(getAttributes()).append(" />");
str.append("</" + ELEMENT_NAME + ">");
return str.toString();
}
/**
* IQProvider for RTP Bridge packets.
* Parse receive RTPBridge packet to a RTPBridge instance
*
* @author Thiago Rocha
*/
public static class Provider implements IQProvider {
public Provider() {
super();
}
public IQ parseIQ(XmlPullParser parser) throws Exception {
boolean done = false;
int eventType;
String elementName;
String namespace;
if (!parser.getNamespace().equals(RTPBridge.NAMESPACE))
throw new Exception("Not a RTP Bridge packet");
RTPBridge iq = new RTPBridge();
for (int i = 0; i < parser.getAttributeCount(); i++) {
if (parser.getAttributeName(i).equals("sid"))
iq.setSid(parser.getAttributeValue(i));
}
// Start processing sub-elements
while (!done) {
eventType = parser.next();
elementName = parser.getName();
namespace = parser.getNamespace();
if (eventType == XmlPullParser.START_TAG) {
if (elementName.equals("candidate")) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
if (parser.getAttributeName(i).equals("ip"))
iq.setIp(parser.getAttributeValue(i));
else if (parser.getAttributeName(i).equals("pass"))
iq.setPass(parser.getAttributeValue(i));
else if (parser.getAttributeName(i).equals("name"))
iq.setName(parser.getAttributeValue(i));
else if (parser.getAttributeName(i).equals("porta"))
iq.setPortA(Integer.parseInt(parser.getAttributeValue(i)));
else if (parser.getAttributeName(i).equals("portb"))
iq.setPortB(Integer.parseInt(parser.getAttributeValue(i)));
}
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(RTPBridge.ELEMENT_NAME)) {
done = true;
}
}
}
return iq;
}
}
/**
* Get a new RTPBridge Candidate from the server.
* If a error occurs or the server don´t support RTPBridge Service, null is returned.
*
* @param xmppConnection
* @param sessionID
* @return
*/
public static RTPBridge getRTPBridge(XMPPConnection xmppConnection, String sessionID) {
if (!xmppConnection.isConnected()) {
return null;
}
RTPBridge rtpPacket = new RTPBridge(sessionID);
rtpPacket.setTo(RTPBridge.NAME + "." + xmppConnection.getServiceName());
PacketCollector collector = xmppConnection
.createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID()));
xmppConnection.sendPacket(rtpPacket);
RTPBridge response = (RTPBridge) collector
.nextResult(SmackConfiguration.getPacketReplyTimeout());
// Cancel the collector.
collector.cancel();
return response;
}
/**
* Check if the server support RTPBridge Service.
*
* @param xmppConnection
* @return
*/
public static boolean serviceAvailable(XMPPConnection xmppConnection) {
if (!xmppConnection.isConnected()) {
return false;
}
System.out.println("service listing");
ServiceDiscoveryManager disco = ServiceDiscoveryManager
.getInstanceFor(xmppConnection);
try {
DiscoverItems items = disco.discoverItems(xmppConnection.getServiceName());
Iterator iter = items.getItems();
while (iter.hasNext()) {
DiscoverItems.Item item = (DiscoverItems.Item) iter.next();
if (item.getEntityID().startsWith("rtpbridge.")) {
return true;
}
}
}
catch (XMPPException e) {
e.printStackTrace();
}
return false;
}
/**
* Check if the server support RTPBridge Service.
*
* @param xmppConnection
* @return
*/
public static RTPBridge relaySession(XMPPConnection xmppConnection, String sessionID, String pass, TransportCandidate proxyCandidate, TransportCandidate localCandidate) {
if (!xmppConnection.isConnected()) {
return null;
}
RTPBridge rtpPacket = new RTPBridge(sessionID, RTPBridge.BridgeAction.change);
rtpPacket.setTo(RTPBridge.NAME + "." + xmppConnection.getServiceName());
rtpPacket.setType(Type.SET);
rtpPacket.setPass(pass);
rtpPacket.setPortA(localCandidate.getPort());
rtpPacket.setPortB(proxyCandidate.getPort());
rtpPacket.setHostA(localCandidate.getIp());
rtpPacket.setHostB(proxyCandidate.getIp());
// System.out.println("Relayed to: " + candidate.getIp() + ":" + candidate.getPort());
PacketCollector collector = xmppConnection
.createPacketCollector(new PacketIDFilter(rtpPacket.getPacketID()));
xmppConnection.sendPacket(rtpPacket);
RTPBridge response = (RTPBridge) collector
.nextResult(SmackConfiguration.getPacketReplyTimeout());
// Cancel the collector.
collector.cancel();
return response;
}
}

View file

@ -0,0 +1,509 @@
package org.jivesoftware.smackx.jingle.nat;
import de.javawi.jstun.test.BindingLifetimeTest;
import de.javawi.jstun.test.DiscoveryInfo;
import de.javawi.jstun.test.DiscoveryTest;
import org.jivesoftware.smack.XMPPException;
import org.xmlpull.mxp1.MXParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
/**
* Transport resolver using the JSTUN library, to discover public IP and use it as a candidate.
*
* The goal of this resolver is to take possible to establish and manage out-of-band connections between two XMPP entities, even if they are behind Network Address Translators (NATs) or firewalls.
*
* @author Alvaro Saurin
*/
public class STUNResolver extends TransportResolver {
// The filename where the STUN servers are stored.
public final static String STUNSERVERS_FILENAME = "META-INF/stun-config.xml";
// Fallback values when we don't have any STUN server to use...
private final static String FALLBACKHOSTNAME = "stun.xten.net";
private final static int FALLBACKHOSTPORT = 3478;
// Current STUN server we are using
protected STUNService currentServer;
protected Thread resolverThread;
protected int defaultPort;
protected String resolvedPublicIP;
protected String resolvedLocalIP;
/**
* Constructor with default STUN server.
*/
public STUNResolver() {
super();
this.defaultPort = 0;
this.currentServer = new STUNService();
}
/**
* Constructor with a default port.
*
* @param defaultPort Port to use by default.
*/
public STUNResolver(int defaultPort) {
this();
this.defaultPort = defaultPort;
}
/**
* Return true if the service is working.
*
* @see TransportResolver#isResolving()
*/
public boolean isResolving() {
return super.isResolving() && resolverThread != null;
}
/**
* Set the STUN server name and port
*
* @param ip the STUN server name
* @param port the STUN server port
*/
public void setSTUNService(String ip, int port) {
currentServer = new STUNService(ip, port);
}
/**
* Get the name of the current STUN server.
*
* @return the name of the STUN server
*/
public String getCurrentServerName() {
if (!currentServer.isNull()) {
return currentServer.getHostname();
} else {
return null;
}
}
/**
* Get the port of the current STUN server.
*
* @return the port of the STUN server
*/
public int getCurrentServerPort() {
if (!currentServer.isNull()) {
return currentServer.getPort();
} else {
return 0;
}
}
/**
* Load the STUN configuration from a stream.
*
* @param stunConfigStream An InputStream with the configuration file.
* @return A list of loaded servers
*/
public ArrayList loadSTUNServers(java.io.InputStream stunConfigStream) {
ArrayList serversList = new ArrayList();
String serverName;
int serverPort;
try {
XmlPullParser parser = new MXParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(stunConfigStream, "UTF-8");
int eventType = parser.getEventType();
do {
if (eventType == XmlPullParser.START_TAG) {
// Parse a STUN server definition
if (parser.getName().equals("stunServer")) {
serverName = null;
serverPort = -1;
// Parse the hostname
parser.next();
parser.next();
serverName = parser.nextText();
// Parse the port
parser.next();
parser.next();
try {
serverPort = Integer.parseInt(parser.nextText());
}
catch (Exception e) {
}
// If we have a valid hostname and port, add
// it to the list.
if (serverName != null && serverPort != -1) {
STUNService service = new STUNService(serverName, serverPort);
serversList.add(service);
}
}
}
eventType = parser.next();
}
while (eventType != XmlPullParser.END_DOCUMENT);
}
catch (XmlPullParserException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
currentServer = bestSTUNServer(serversList);
return serversList;
}
/**
* Load a list of services: STUN servers and ports. Some public STUN servers
* are:
* <p/>
* <pre>
* iphone-stun.freenet.de:3478
* larry.gloo.net:3478
* stun.xten.net:3478
* stun.fwdnet.net
* stun.fwd.org (no DNS SRV record)
* stun01.sipphone.com (no DNS SRV record)
* stun.softjoys.com (no DNS SRV record)
* stun.voipbuster.com (no DNS SRV record)
* stun.voxgratia.org (no DNS SRV record)
* stun.noc.ams-ix.net
* </pre>
* <p/>
* This list should be contained in a file in the "META-INF" directory
*
* @return a list of services
*/
public ArrayList loadSTUNServers() {
ArrayList serversList = new ArrayList();
// Load the STUN configuration
try {
// Get an array of class loaders to try loading the config from.
ClassLoader[] classLoaders = new ClassLoader[2];
classLoaders[0] = new STUNResolver() {
}.getClass().getClassLoader();
classLoaders[1] = Thread.currentThread().getContextClassLoader();
for (int i = 0; i < classLoaders.length; i++) {
Enumeration stunConfigEnum = classLoaders[i]
.getResources(STUNSERVERS_FILENAME);
while (stunConfigEnum.hasMoreElements() && serversList.isEmpty()) {
URL url = (URL) stunConfigEnum.nextElement();
java.io.InputStream stunConfigStream = null;
stunConfigStream = url.openStream();
serversList.addAll(loadSTUNServers(stunConfigStream));
stunConfigStream.close();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
// If the list of candidates is empty, add at least one default server
if (serversList.isEmpty()) {
currentServer = new STUNService(FALLBACKHOSTNAME, FALLBACKHOSTPORT);
serversList.add(currentServer);
}
return serversList;
}
/**
* Get the best usable STUN server from a list.
*
* @return the best STUN server that can be used.
*/
private STUNService bestSTUNServer(ArrayList listServers) {
if (listServers.isEmpty()) {
return null;
} else {
// TODO: this should use some more advanced criteria...
return (STUNService) listServers.get(0);
}
}
/**
* Resolve the IP and obtain a valid transport method.
*/
public synchronized void resolve() throws XMPPException {
setResolveInit();
clearCandidates();
TransportCandidate candidate = new TransportCandidate.Fixed(
resolvedPublicIP, getFreePort());
candidate.setLocalIp(resolvedLocalIP);
System.out.println("RESOLVING : " + resolvedPublicIP + ":" + candidate.getPort());
addCandidate(candidate);
setResolveEnd();
}
/**
* Initialize the resolver.
*
* @throws XMPPException
*/
public void initialize() throws XMPPException {
System.out.println("Initialized");
if (!isResolving()&&!isResolved()) {
// Get the best STUN server available
if (currentServer.isNull()) {
loadSTUNServers();
}
// We should have a valid STUN server by now...
if (!currentServer.isNull()) {
clearCandidates();
resolverThread = new Thread(new Runnable() {
public void run() {
// Iterate through the list of interfaces, and ask
// to the STUN server for our address.
try {
Enumeration ifaces = NetworkInterface.getNetworkInterfaces();
String candAddress;
int candPort;
while (ifaces.hasMoreElements()) {
NetworkInterface iface = (NetworkInterface) ifaces
.nextElement();
Enumeration iaddresses = iface.getInetAddresses();
while (iaddresses.hasMoreElements()) {
InetAddress iaddress = (InetAddress) iaddresses
.nextElement();
if (!iaddress.isLoopbackAddress()
&& !iaddress.isLinkLocalAddress()) {
// Reset the candidate
candAddress = null;
candPort = -1;
DiscoveryTest test = new DiscoveryTest(iaddress,
currentServer.getHostname(),
currentServer.getPort());
try {
// Run the tests and get the
// discovery
// information, where all the
// info is stored...
DiscoveryInfo di = test.test();
candAddress = di.getPublicIP() != null ?
di.getPublicIP().getHostAddress() : null;
// Get a valid port
if (defaultPort == 0) {
candPort = getFreePort();
} else {
candPort = defaultPort;
}
// If we have a valid candidate,
// add it to the list.
if (candAddress != null && candPort >= 0) {
TransportCandidate candidate = new TransportCandidate.Fixed(
candAddress, candPort);
candidate.setLocalIp(iaddress.getHostAddress() != null ? iaddress.getHostAddress() : iaddress.getHostName());
addCandidate(candidate);
resolvedPublicIP = candidate.getIp();
resolvedLocalIP = candidate.getLocalIp();
return;
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
catch (SocketException e) {
e.printStackTrace();
}
finally {
setInitialized();
}
}
}, "Waiting for all the transport candidates checks...");
resolverThread.setName("STUN resolver");
resolverThread.start();
} else {
throw new IllegalStateException("No valid STUN server found.");
}
}
}
/**
* Cancel any operation.
*
* @see TransportResolver#cancel()
*/
public synchronized void cancel() throws XMPPException {
if (isResolving()) {
resolverThread.interrupt();
setResolveEnd();
}
}
/**
* Clear the list of candidates and start the resolution again.
*
* @see TransportResolver#clear()
*/
public synchronized void clear() throws XMPPException {
this.defaultPort = 0;
super.clear();
}
/**
* STUN service definition.
*/
protected class STUNService {
private String hostname; // The hostname of the service
private int port; // The port number
/**
* Basic constructor, with the hostname and port
*
* @param hostname The hostname
* @param port The port
*/
public STUNService(String hostname, int port) {
super();
this.hostname = hostname;
this.port = port;
}
/**
* Default constructor, without name and port.
*/
public STUNService() {
this(null, -1);
}
/**
* Get the host name of the STUN service.
*
* @return The host name
*/
public String getHostname() {
return hostname;
}
/**
* Set the hostname of the STUN service.
*
* @param hostname The host name of the service.
*/
public void setHostname(String hostname) {
this.hostname = hostname;
}
/**
* Get the port of the STUN service
*
* @return The port number where the STUN server is waiting.
*/
public int getPort() {
return port;
}
/**
* Set the port number for the STUN service.
*
* @param port The port number.
*/
public void setPort(int port) {
this.port = port;
}
/**
* Basic format test: the service is not null.
*
* @return true if the hostname and port are null
*/
public boolean isNull() {
if (hostname == null) {
return true;
} else if (hostname.length() == 0) {
return true;
} else if (port < 0) {
return true;
} else {
return false;
}
}
/**
* Check a binding with the STUN currentServer.
* <p/>
* Note: this function blocks for some time, waiting for a response.
*
* @return true if the currentServer is usable.
*/
public boolean checkBinding() {
boolean result = false;
try {
BindingLifetimeTest binding = new BindingLifetimeTest(hostname, port);
binding.test();
while (true) {
Thread.sleep(5000);
if (binding.getLifetime() != -1) {
if (binding.isCompleted()) {
return true;
}
} else {
break;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
}

View file

@ -0,0 +1,51 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
/**
* $RCSfile$
* $Revision: $
* $Date: 15/11/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A Jingle Transport Manager implementation to be used on NAT networks with STUN Service NOT Blocked.
*
* @author Thiago Camargo
*/
public class STUNTransportManager extends JingleTransportManager {
STUNResolver stunResolver = null;
public STUNTransportManager() {
stunResolver = new STUNResolver() {
};
try {
stunResolver.initializeAndWait();
} catch (XMPPException e) {
e.printStackTrace();
}
}
protected TransportResolver createResolver() {
try {
stunResolver.resolve();
} catch (XMPPException e) {
e.printStackTrace();
}
return stunResolver;
}
}

View file

@ -0,0 +1,855 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPConnection;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
/**
* Transport candidate.
*
* A candidate represents the possible transport for data interchange between
* the two endpoints.
*
* @author Thiago Camargo
* @author Alvaro Saurin
*/
public abstract class TransportCandidate {
private String name;
private String ip; // IP address
private int port; // Port to use, or 0 for any port
private String localIp;
private int generation;
protected String password;
private String sessionId;
private XMPPConnection connection;
private TransportCandidate symmetric;
// Listeners for events
private final List<TransportResolverListener.Checker> listeners = new ArrayList();
public String getIp() {
return ip;
}
/**
* Set the IP address.
*
* @param ip the IP address
*/
public void setIp(String ip) {
this.ip = ip;
}
/**
* Get local IP to bind to this candidate
*
* @return
*/
public String getLocalIp() {
return localIp == null ? ip : localIp;
}
/**
* Set local IP to bind to this candidate
*
* @param localIp
*/
public void setLocalIp(String localIp) {
this.localIp = localIp;
}
/**
* Get the symetric candidate for this candidate if it exists.
*
* @return
*/
public TransportCandidate getSymmetric() {
return symmetric;
}
/**
* Set the symetric candidate for this candidate.
*
* @param symetric
*/
public void setSymmetric(TransportCandidate symetric) {
this.symmetric = symetric;
}
/**
* Get the password used by ICE or relayed candidate
*
* @return a password
*/
public String getPassword() {
return password;
}
/**
* Set the password used by ICE or relayed candidate
*
* @param password a password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Get the XMPPConnection use to send or receive this candidate
*
* @return
*/
public XMPPConnection getConnection() {
return connection;
}
/**
* Set the XMPPConnection use to send or receive this candidate
*
* @param connection
*/
public void setConnection(XMPPConnection connection) {
this.connection = connection;
}
/**
* Get the jingle´s sessionId that is using this candidate
*
* @return
*/
public String getSessionId() {
return sessionId;
}
/**
* Set the jingle´s sessionId that is using this candidate
*
* @param sessionId
*/
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
/**
* Empty constructor
*/
public TransportCandidate() {
this(null, 0, 0);
}
/**
* Constructor with IP address and port
*
* @param ip The IP address.
* @param port The port number.
*/
public TransportCandidate(String ip, int port) {
this(ip, port, 0);
}
/**
* Constructor with IP address and port
*
* @param ip The IP address.
* @param port The port number.
* @param generation The generation
*/
public TransportCandidate(String ip, int port, int generation) {
this.ip = ip;
this.port = port;
this.generation = generation;
}
/**
* Return true if the candidate is not valid.
*
* @return true if the candidate is null.
*/
public boolean isNull() {
if (ip == null) {
return true;
} else if (ip.length() == 0) {
return true;
} else if (port < 0) {
return true;
} else {
return false;
}
}
/**
* Get the port, or 0 for any port.
*
* @return the port or 0
*/
public int getPort() {
return port;
}
/**
* Set the port, using 0 for any port
*
* @param port the port
*/
public void setPort(int port) {
this.port = port;
}
/**
* Get the generation for a transportElement definition
*
* @return the generation
*/
public int getGeneration() {
return generation;
}
/**
* Set the generation for a transportElement definition.
*
* @param generation the generation number
*/
public void setGeneration(int generation) {
this.generation = generation;
}
/**
* Get the name used for identifying this transportElement method (optional)
*
* @return a name used for identifying this transportElement (ie,
* "myrtpvoice1")
*/
public String getName() {
return name;
}
/**
* Set a name for identifying this transportElement.
*
* @param name the name used for the transportElement
*/
public void setName(String name) {
this.name = name;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TransportCandidate other = (TransportCandidate) obj;
if (generation != other.generation) {
return false;
}
if (getIp() == null) {
if (other.getIp() != null) {
return false;
}
} else if (!getIp().equals(other.getIp())) {
return false;
}
if (getName() == null) {
if (other.getName() != null) {
return false;
}
} else if (!getName().equals(other.getName())) {
return false;
}
if (getPort() != other.getPort()) {
return false;
}
return true;
}
/**
* Check if a transport candidate is usable. The transport resolver should
* check if the transport candidate the other endpoint has provided is
* usable.
* <p/>
* This method provides a basic check where it sends a "ping" to the remote
* address provided in the candidate. If the "ping" succedess, the candidate
* is accepted. Subclasses should provide better methods if they can...
*/
public void check() {
//TODO candidate is being checked trigger
//candidatesChecking.add(cand);
Thread checkThread = new Thread(new Runnable() {
public void run() {
boolean isUsable;
InetAddress candAddress;
try {
candAddress = InetAddress.getByName(getIp());
isUsable = true;//candAddress.isReachable(CHECK_TIMEOUT);
} catch (Exception e) {
isUsable = false;
}
triggerCandidateChecked(isUsable);
//TODO candidate is being checked trigger
//candidatesChecking.remove(cand);
}
}, "Transport candidate check");
checkThread.setName("Transport candidate test");
checkThread.start();
}
/**
* Trigger a new candidate checked event.
*
* @param result The result.
*/
private void triggerCandidateChecked(boolean result) {
for (TransportResolverListener.Checker trl : getListenersList()) {
trl.candidateChecked(this, result);
}
}
/**
* Get the list of listeners
*
* @return the list of listeners
*/
public List<TransportResolverListener.Checker> getListenersList() {
synchronized (listeners) {
return new ArrayList(listeners);
}
}
/**
* Add a transport resolver listener.
*
* @param li The transport resolver listener to be added.
*/
public void addListener(TransportResolverListener.Checker li) {
synchronized (listeners) {
listeners.add(li);
}
}
/**
* Fixed transport candidate
*/
public static class Fixed extends TransportCandidate {
public Fixed() {
super();
}
/**
* Constructor with IP address and port
*
* @param ip The IP address.
* @param port The port number.
*/
public Fixed(String ip, int port) {
super(ip, port);
}
/**
* Constructor with IP address and port
*
* @param ip The IP address.
* @param port The port number.
* @param generation The generation
*/
public Fixed(String ip, int port, int generation) {
super(ip, port, generation);
}
}
/**
* Ice candidate.
*/
public static class Ice extends TransportCandidate implements Comparable {
private String id; // An identification
private String username;
private int preference;
private Protocol proto;
private Channel channel;
private int network;
public Ice() {
super();
}
/**
* Constructor with the basic elements of a transport definition.
*
* @param ip the IP address to use as a local address
* @param generation used to keep track of the candidates
* @param network used for diagnostics (used when the machine has
* several NICs)
* @param password user name, as it is used in ICE
* @param port the port at the candidate IP address
* @param username user name, as it is used in ICE
* @param preference preference for this transportElement, as it is used
* in ICE
*/
public Ice(String ip, int generation, int network,
String password, int port, String username,
int preference) {
super(ip, port, generation);
proto = Protocol.UDP;
channel = Channel.MYRTPVOICE;
this.network = network;
this.password = password;
this.username = username;
this.preference = preference;
}
/**
* Get the ID
*
* @return the id
*/
public String getId() {
return id;
}
/**
* Set the ID
*
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* Get the protocol used for the transmission
*
* @return the protocol used for transmission
*/
public Protocol getProto() {
return proto;
}
/**
* Set the protocol for the transmission
*
* @param proto the protocol to use
*/
public void setProto(Protocol proto) {
this.proto = proto;
}
/**
* Get the network interface used for this connection
*
* @return the interface number
*/
public int getNetwork() {
return network;
}
/**
* Set the interface for this connection
*
* @param network the interface number
*/
public void setNetwork(int network) {
this.network = network;
}
/**
* Get the username for this transportElement in ICE
*
* @return a username string
*/
public String getUsername() {
return username;
}
/**
* Get the channel
*
* @return the channel associated
*/
public Channel getChannel() {
return channel;
}
/**
* Set the channel for this transportElement
*
* @param channel the new channel
*/
public void setChannel(Channel channel) {
this.channel = channel;
}
/**
* Set the username for this transportElement in ICE
*
* @param username the username used in ICE
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Get the preference number for this transportElement
*
* @return the preference for this transportElement
*/
public int getPreference() {
return preference;
}
/**
* Set the preference order for this transportElement
*
* @param preference a number identifying the preference (as defined in
* ICE)
*/
public void setPreference(int preference) {
this.preference = preference;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Ice other = (Ice) obj;
if (getChannel() == null) {
if (other.getChannel() != null) {
return false;
}
} else if (!getChannel().equals(other.getChannel())) {
return false;
}
if (getId() == null) {
if (other.getId() != null) {
return false;
}
} else if (!getId().equals(other.getId())) {
return false;
}
if (getNetwork() != other.getNetwork()) {
return false;
}
if (getPassword() == null) {
if (other.getPassword() != null) {
return false;
}
} else if (!getPassword().equals(other.password)) {
return false;
}
if (getPreference() != other.getPreference()) {
return false;
}
if (getProto() == null) {
if (other.getProto() != null) {
return false;
}
} else if (!getProto().equals(other.getProto())) {
return false;
}
if (getUsername() == null) {
if (other.getUsername() != null) {
return false;
}
} else if (!getUsername().equals(other.getUsername())) {
return false;
}
return true;
}
public boolean isNull() {
if (super.isNull()) {
return true;
} else if (getProto().isNull()) {
return true;
} else if (getChannel().isNull()) {
return true;
}
return false;
}
/**
* Compare the to other Transport candidate.
*
* @param arg another Transport candidate
* @return a negative integer, zero, or a positive integer as this
* object is less than, equal to, or greater than the specified
* object
*/
public int compareTo(Object arg) {
if (arg instanceof TransportCandidate.Ice) {
TransportCandidate.Ice tc = (TransportCandidate.Ice) arg;
if (getPreference() < tc.getPreference()) {
return -1;
} else if (getPreference() > tc.getPreference()) {
return 1;
}
}
return 0;
}
}
/**
* Type-safe enum for the transportElement protocol
*/
public static class Protocol {
public static final Protocol UDP = new Protocol("udp");
public static final Protocol TCP = new Protocol("tcp");
public static final Protocol TCPACT = new Protocol("tcp-act");
public static final Protocol TCPPASS = new Protocol("tcp-pass");
public static final Protocol SSLTCP = new Protocol("ssltcp");
private String value;
public Protocol(String value) {
this.value = value;
}
public String toString() {
return value;
}
/**
* Returns the Protocol constant associated with the String value.
*/
public static Protocol fromString(String value) {
if (value == null) {
return UDP;
}
value = value.toLowerCase();
if (value.equals("udp")) {
return UDP;
} else if (value.equals("tcp")) {
return TCP;
} else if (value.equals("tcp-act")) {
return TCPACT;
} else if (value.equals("tcp-pass")) {
return TCPPASS;
} else if (value.equals("ssltcp")) {
return SSLTCP;
} else {
return UDP;
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Protocol other = (Protocol) obj;
if (value == null) {
if (other.value != null) {
return false;
}
} else if (!value.equals(other.value)) {
return false;
}
return true;
}
/**
* Return true if the protocol is not valid.
*
* @return true if the protocol is null
*/
public boolean isNull() {
if (value == null) {
return true;
} else if (value.length() == 0) {
return true;
} else {
return false;
}
}
}
/**
* Type-safe enum for the transportElement channel
*/
public static class Channel {
public static final Channel MYRTPVOICE = new Channel("myrtpvoice");
public static final Channel MYRTCPVOICE = new Channel("myrtcpvoice");
private String value;
public Channel(String value) {
this.value = value;
}
public String toString() {
return value;
}
/**
* Returns the MediaChannel constant associated with the String value.
*/
public static Channel fromString(String value) {
if (value == null) {
return MYRTPVOICE;
}
value = value.toLowerCase();
if (value.equals("myrtpvoice")) {
return MYRTPVOICE;
} else if (value.equals("tcp")) {
return MYRTCPVOICE;
} else {
return MYRTPVOICE;
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Channel other = (Channel) obj;
if (value == null) {
if (other.value != null) {
return false;
}
} else if (!value.equals(other.value)) {
return false;
}
return true;
}
/**
* Return true if the channel is not valid.
*
* @return true if the channel is null
*/
public boolean isNull() {
if (value == null) {
return true;
} else if (value.length() == 0) {
return true;
} else {
return false;
}
}
}
}

View file

@ -0,0 +1,842 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingle.JingleNegotiator;
import org.jivesoftware.smackx.jingle.JingleSession;
import org.jivesoftware.smackx.jingle.listeners.JingleListener;
import org.jivesoftware.smackx.jingle.listeners.JingleTransportListener;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleTransport.JingleTransportCandidate;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Transport negotiator.
*
*
* This class is responsible for managing the transport negotiation process,
* handling all the packet interchange and the stage control.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class TransportNegotiator extends JingleNegotiator {
// The time we give to the candidates check before we accept or decline the
// transport (in milliseconds)
private final static int CANDIDATES_ACCEPT_PERIOD = 4000;
// The session this nenotiator belongs to
private final JingleSession session;
// The transport manager
private final TransportResolver resolver;
// Transport candidates we have offered
private final List offeredCandidates = new ArrayList();
// List of remote transport candidates
private final List remoteCandidates = new ArrayList();
// Valid remote candidates
private final List validRemoteCandidates = new ArrayList();
// The best local candidate we have offered (and accepted by the other part)
private TransportCandidate acceptedLocalCandidate;
// The thread that will report the result to the other end
private Thread resultThread;
// Listener for the resolver
private TransportResolverListener.Resolver resolverListener;
// states
private final Inviting inviting;
private final Accepting accepting;
private final Pending pending;
private final Active active;
/**
* Default constructor.
*
* @param js The Jingle session
* @param transResolver The JingleTransportManager to use
*/
public TransportNegotiator(JingleSession js,
TransportResolver transResolver) {
super(js.getConnection());
session = js;
resolver = transResolver;
resultThread = null;
// Create the states...
inviting = new Inviting(this);
accepting = new Accepting(this);
pending = new Pending(this);
active = new Active(this);
}
/**
* Get a new instance of the right TransportNegotiator class with this
* candidate.
*
* @return A TransportNegotiator instance
*/
public abstract org.jivesoftware.smackx.packet.JingleTransport getJingleTransport(TransportCandidate cand);
/**
* Return true if the transport candidate is acceptable for the current
* negotiator.
*
* @return true if the transport candidate is acceptable
*/
public abstract boolean acceptableTransportCandidate(TransportCandidate tc);
/**
* Obtain the best local candidate we want to offer.
*
* @return the best local candidate
*/
public final TransportCandidate getBestLocalCandidate() {
return resolver.getPreferredCandidate();
}
/**
* Set the best local transport candidate we have offered and accepted by
* the other endpoint.
*
* @param bestLocalCandidate the acceptedLocalCandidate to set
*/
private void setAcceptedLocalCandidate(TransportCandidate bestLocalCandidate)
throws XMPPException {
for (int i = 0; i < resolver.getCandidateCount(); i++) {
//TODO FIX The EQUAL Sentence
if (resolver.getCandidate(i).getIp().equals(bestLocalCandidate.getIp())) {
acceptedLocalCandidate = resolver.getCandidate(i);
return;
}
}
//System.out.println("BEST: " + bestLocalCandidate.getIp());
throw new XMPPException("Local transport candidate has not be offered.");
}
/**
* Get the best accepted local candidate we have offered.
*
* @return a transport candidate we have offered.
*/
public TransportCandidate getAcceptedLocalCandidate() {
return acceptedLocalCandidate;
}
/**
* Obtain the best common transport candidate obtained in the negotiation.
*
* @return the bestRemoteCandidate
*/
public abstract TransportCandidate getBestRemoteCandidate();
/**
* Get the list of remote candidates.
*
* @return the remoteCandidates
*/
private List getRemoteCandidates() {
return remoteCandidates;
}
/**
* Add a remote candidate to the list. The candidate will be checked in
* order to verify if it is usable.
*
* @param rc a remote candidate to add and check.
*/
private void addRemoteCandidate(TransportCandidate rc) {
// Add the candidate to the list
if (rc != null) {
if (acceptableTransportCandidate(rc)) {
synchronized (remoteCandidates) {
remoteCandidates.add(rc);
}
// Check if the new candidate can be used.
checkRemoteCandidate(rc);
}
}
}
/**
* Add a offered candidate to the list.
*
* @param rc a remote candidate we have offered.
*/
private void addOfferedCandidate(TransportCandidate rc) {
// Add the candidate to the list
if (rc != null) {
synchronized (offeredCandidates) {
offeredCandidates.add(rc);
}
}
}
/**
* Check asynchronously the new transport candidate.
*
* @param offeredCandidate a transport candidates to check
*/
private void checkRemoteCandidate(final TransportCandidate offeredCandidate) {
System.out.println("CHECK");
offeredCandidate.addListener(new TransportResolverListener.Checker() {
public void candidateChecked(TransportCandidate cand,
final boolean validCandidate) {
if (validCandidate) {
addValidRemoteCandidate(offeredCandidate);
}
}
public void candidateChecking(TransportCandidate cand) {
}
});
offeredCandidate.check();
}
/**
* Return true if the transport is established.
*
* @return true if the transport is established.
*/
private boolean isEstablished() {
return getBestRemoteCandidate() != null && getAcceptedLocalCandidate() != null;
}
/**
* Return true if the transport is fully established.
*
* @return true if the transport is fully established.
*/
public final boolean isFullyEstablished() {
return isEstablished() && getState() == active;
}
/**
* Launch a thread that checks, after some time, if any of the candidates
* offered by the other endpoint is usable. The thread does not check the
* candidates: it just checks if we have got a valid one and sends an Accept
* in that case.
*/
private void delayedCheckBestCandidate(final JingleSession js, final Jingle jin) {
//
// If this is the first insertion in the list, start the thread that
// will send the result of our checks...
//
if (resultThread == null && !getRemoteCandidates().isEmpty()) {
resultThread = new Thread(new Runnable() {
public void run() {
// Sleep for some time, waiting for the candidates checks
try {
Thread.sleep(CANDIDATES_ACCEPT_PERIOD
+ TransportResolver.CHECK_TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Once we are in pending state, look for any valid remote
// candidate, and send an "accept" if we have one...
TransportCandidate bestRemote = getBestRemoteCandidate();
State state = getState();
if (bestRemote != null && (state == pending || state == active)) {
// Accepting the remote candidate
Jingle jout = new Jingle(Jingle.Action.TRANSPORTACCEPT);
jout.addTransport(getJingleTransport(bestRemote));
// Send the packet
js.sendFormattedJingle(jin, jout);
if (isEstablished()) {
setState(active);
}
}
}
}, "Waiting for all the transport candidates checks...");
resultThread.setName("Transport Resolver Result");
resultThread.start();
}
}
/**
* Add a valid remote candidate to the list. The remote candidate has been
* checked, and the remote
*
* @param remoteCandidate a remote candidate to add
*/
private void addValidRemoteCandidate(TransportCandidate remoteCandidate) {
// Add the candidate to the list
if (remoteCandidate != null) {
synchronized (validRemoteCandidates) {
System.out.println("ADDED Valid Cand: " + remoteCandidate.getIp());
validRemoteCandidates.add(remoteCandidate);
}
}
}
/**
* Get the list of valid (ie, checked) remote candidates.
*
* @return The list of valid (ie, already checked) remote candidates.
*/
final ArrayList getValidRemoteCandidatesList() {
synchronized (validRemoteCandidates) {
return new ArrayList(validRemoteCandidates);
}
}
/**
* Get an iterator for the list of valid (ie, checked) remote candidates.
*
* @return The iterator for the list of valid (ie, already checked) remote
* candidates.
*/
public final Iterator getValidRemoteCandidates() {
return Collections.unmodifiableList(getRemoteCandidates()).iterator();
}
/**
* Add an offered remote candidate. The transport candidate can be unusable:
* we must check if we can use it.
*
* @param rc the remote candidate to add.
*/
private void addRemoteCandidates(List rc) {
if (rc != null) {
System.out.println("SIZE OF LISTA: " + rc.size());
if (rc.size() > 0) {
for (Object aRc : rc) {
addRemoteCandidate((TransportCandidate) aRc);
}
}
}
}
/**
* Parse the list of transport candidates from a Jingle packet.
*
* @param jin The input jingle packet
*/
private static ArrayList obtainCandidatesList(Jingle jin) {
ArrayList result = new ArrayList();
if (jin != null) {
// Get the list of candidates from the packet
Iterator iTrans = jin.getTransports();
while (iTrans.hasNext()) {
org.jivesoftware.smackx.packet.JingleTransport trans = (org.jivesoftware.smackx.packet.JingleTransport) iTrans.next();
System.out.println("LISTA SIZE: " + trans.getCandidatesCount());
Iterator iCand = trans.getCandidates();
while (iCand.hasNext()) {
JingleTransportCandidate cand = (JingleTransportCandidate) iCand
.next();
TransportCandidate transCand = cand.getMediaTransport();
result.add(transCand);
}
}
}
return result;
}
private boolean isOfferStarted() {
return resolver.isResolving() || resolver.isResolved();
}
/**
* Send an offer for a transport candidate
*
* @param cand
*/
private synchronized void sendTransportCandidateOffer(TransportCandidate cand) {
if (!cand.isNull()) {
// Offer our new candidate...
addOfferedCandidate(cand);
session.sendFormattedJingle(new Jingle(getJingleTransport(cand)));
}
}
/**
* Create a Jingle packet where we announce our transport candidates.
*
* @throws XMPPException
*/
private void sendTransportCandidatesOffer() throws XMPPException {
List notOffered = resolver.getCandidatesList();
notOffered.removeAll(offeredCandidates);
// Send any unset candidate
for (Object aNotOffered : notOffered) {
sendTransportCandidateOffer((TransportCandidate) aNotOffered);
}
// .. and start a listener that will send any future candidate
if (resolverListener == null) {
// Add a listener that sends the offer when the resolver finishes...
resolverListener = new TransportResolverListener.Resolver() {
public void candidateAdded(TransportCandidate cand) {
sendTransportCandidateOffer(cand);
}
public void end() {
}
public void init() {
}
};
resolver.addListener(resolverListener);
}
if (!(resolver.isResolving() || resolver.isResolved())) {
// Resolve our IP and port
System.out.println("RESOLVER CALLED");
resolver.resolve();
}
}
/**
* Dispatch an incoming packet. The method is responsible for recognizing
* the packet type and, depending on the current state, deliverying the
* packet to the right event handler and wait for a response.
*
* @param iq the packet received
* @return the new Jingle packet to send.
* @throws XMPPException
*/
public final IQ dispatchIncomingPacket(IQ iq, String id) throws XMPPException {
IQ jout = null;
if (invalidState()) {
if (iq == null) {
// With a null packet, we are just inviting the other end...
setState(inviting);
jout = getState().eventInvite();
} else {
if (iq instanceof Jingle) {
// If there is no specific jmf action associated, then we
// are being invited to a new session...
setState(accepting);
jout = getState().eventInitiate((Jingle) iq);
} else {
throw new IllegalStateException(
"Invitation IQ received is not a Jingle packet in Transport negotiator.");
}
}
} else {
if (iq == null) {
return null;
} else {
if (iq.getType().equals(IQ.Type.ERROR)) {
// Process errors
getState().eventError(iq);
} else if (iq.getType().equals(IQ.Type.RESULT)) {
// Process ACKs
if (isExpectedId(iq.getPacketID())) {
jout = getState().eventAck(iq);
removeExpectedId(iq.getPacketID());
}
} else if (iq instanceof Jingle) {
// Get the action from the Jingle packet
Jingle jin = (Jingle) iq;
Jingle.Action action = jin.getAction();
if (action != null) {
if (action.equals(Jingle.Action.TRANSPORTACCEPT)) {
jout = getState().eventAccept(jin);
} else if (action.equals(Jingle.Action.TRANSPORTDECLINE)) {
jout = getState().eventDecline(jin);
} else if (action.equals(Jingle.Action.TRANSPORTINFO)) {
jout = getState().eventInfo(jin);
} else if (action.equals(Jingle.Action.TRANSPORTMODIFY)) {
jout = getState().eventModify(jin);
}
}
}
}
}
// Save the Id for any ACK
if (id != null) {
addExpectedId(id);
} else {
if (jout != null) {
addExpectedId(jout.getPacketID());
}
}
return jout;
}
/**
* Trigger a Transport session established event.
*
* @param local TransportCandidate that has been agreed.
* @param remote TransportCandidate that has been agreed.
*/
private void triggerTransportEstablished(TransportCandidate local,
TransportCandidate remote) {
ArrayList listeners = getListenersList();
for (Object listener : listeners) {
JingleListener li = (JingleListener) listener;
if (li instanceof JingleTransportListener) {
JingleTransportListener mli = (JingleTransportListener) li;
System.out.println("triggerTransportEstablished " + local.getLocalIp());
mli.transportEstablished(local, remote);
}
}
}
/**
* Trigger a Transport closed event.
*
* @param cand current TransportCandidate that is cancelled.
*/
private void triggerTransportClosed(TransportCandidate cand) {
ArrayList listeners = getListenersList();
for (Object listener : listeners) {
JingleListener li = (JingleListener) listener;
if (li instanceof JingleTransportListener) {
JingleTransportListener mli = (JingleTransportListener) li;
mli.transportClosed(cand);
}
}
}
// States
/**
* First stage when we send a session request.
*/
public final class Inviting extends JingleNegotiator.State {
public Inviting(TransportNegotiator neg) {
super(neg);
}
/**
* Create an initial Jingle packet with an empty transport.
*/
public Jingle eventInvite() {
return new Jingle(getJingleTransport(null));
}
/**
* We have received some candidates. This can happen _before_ the ACK
* has been recieved...
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventInfo(org.jivesoftware.smackx.packet.Jingle)
*/
public Jingle eventInfo(Jingle jin) throws XMPPException {
// Parse the Jingle and get any proposed transport candidates
addRemoteCandidates(obtainCandidatesList(jin));
// Wait for some time and check if we have a valid candidate to
// use...
delayedCheckBestCandidate(session, jin);
return null;// super.eventInfo(jin);
}
/**
* The other endpoint has partially accepted our invitation: start
* offering a list of candidates.
*
* @return an IQ packet
* @throws XMPPException
*/
public Jingle eventAck(IQ iq) throws XMPPException {
sendTransportCandidatesOffer();
setState(pending);
return super.eventAck(iq);
}
}
/**
* We are accepting connections. This is the starting state when we accept a
* connection...
*/
public final class Accepting extends JingleNegotiator.State {
public Accepting(TransportNegotiator neg) {
super(neg);
}
/**
* We have received an invitation. The packet will be ACKed by lower
* levels...
*/
public Jingle eventInitiate(Jingle jin) throws XMPPException {
// Parse the Jingle and get any proposed transport candidates
//addRemoteCandidates(obtainCandidatesList(jin));
// Start offering candidates
sendTransportCandidatesOffer();
// All these candidates will be checked asyncronously. Wait for some
// time and check if we have a valid candidate to use...
delayedCheckBestCandidate(session, jin);
// Set the next state
setState(pending);
return super.eventInitiate(jin);
}
}
/**
* We are still receiving candidates
*/
public final class Pending extends JingleNegotiator.State {
public Pending(TransportNegotiator neg) {
super(neg);
}
/**
* One of our transport candidates has been accepted.
*
* @param jin The input packet
* @return a Jingle packet
* @throws XMPPException an exception
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAccept(org.jivesoftware.smackx.packet.Jingle)
*/
public Jingle eventAccept(Jingle jin) throws XMPPException {
Jingle response = null;
// Parse the Jingle and get the accepted candidate
ArrayList accepted = obtainCandidatesList(jin);
if (!accepted.isEmpty()) {
for (TransportCandidate cand : (List<TransportCandidate>) accepted) {
System.out.println("Cand: " + cand.getIp());
}
TransportCandidate cand = (TransportCandidate) accepted.get(0);
setAcceptedLocalCandidate(cand);
if (isEstablished()) {
System.out.println("SET ACTIVE");
setState(active);
}
}
return response;
}
/**
* We have received another remote transport candidates.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventInfo(org.jivesoftware.smackx.packet.Jingle)
*/
public Jingle eventInfo(Jingle jin) throws XMPPException {
sendTransportCandidatesOffer();
// Parse the Jingle and get any proposed transport candidates
addRemoteCandidates(obtainCandidatesList(jin));
// Wait for some time and check if we have a valid candidate to
// use...
delayedCheckBestCandidate(session, jin);
return null;//super.eventInfo(jin);
}
/**
* None of our transport candidates has been accepted...
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventDecline(org.jivesoftware.smackx.packet.Jingle)
*/
public Jingle eventDecline(Jingle inJingle) throws JingleException {
throw new JingleException("No common payload found.");
}
}
/**
* "Active" state: we have an agreement about the codec...
*/
public final class Active extends JingleNegotiator.State {
public Active(TransportNegotiator neg) {
super(neg);
}
/**
* We have an agreement.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventEnter() {
System.out.println("Transport stabilished");
triggerTransportEstablished(getAcceptedLocalCandidate(),
getBestRemoteCandidate());
super.eventEnter();
}
/**
* We have finished the transport.
*
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
*/
public void eventExit() {
triggerTransportClosed(null);
super.eventExit();
}
}
// Subclasses
/**
* Raw-UDP transport negotiator
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public static final class RawUdp extends TransportNegotiator {
/**
* Default constructor, with a JingleSession and transport manager.
*
* @param js The Jingle session this negotiation belongs to.
* @param res The transport resolver to use.
*/
public RawUdp(JingleSession js, final TransportResolver res) {
super(js, res);
}
/**
* Get a TransportNegotiator instance.
*/
public org.jivesoftware.smackx.packet.JingleTransport getJingleTransport(TransportCandidate bestRemote) {
org.jivesoftware.smackx.packet.JingleTransport.RawUdp jt = new org.jivesoftware.smackx.packet.JingleTransport.RawUdp();
jt.addCandidate(new org.jivesoftware.smackx.packet.JingleTransport.RawUdp.Candidate(bestRemote));
return jt;
}
/**
* Obtain the best common transport candidate obtained in the
* negotiation.
*
* @return the bestRemoteCandidate
*/
public TransportCandidate getBestRemoteCandidate() {
// Hopefully, we only have one validRemoteCandidate
ArrayList cands = getValidRemoteCandidatesList();
if (!cands.isEmpty()) {
return (TransportCandidate) cands.get(0);
} else {
System.out.println("No Remote Candidate");
return null;
}
}
/**
* Return true for fixed candidates.
*/
public boolean acceptableTransportCandidate(TransportCandidate tc) {
return tc instanceof TransportCandidate.Fixed;
}
}
/**
* Ice transport negotiator.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public static final class Ice extends TransportNegotiator {
/**
* Default constructor, with a JingleSession and transport manager.
*
* @param js The Jingle session this negotiation belongs to.
* @param res The transport manager to use.
*/
public Ice(JingleSession js, final TransportResolver res) {
super(js, res);
}
/**
* Get a TransportNegotiator instance.
*
* @param candidate
*/
public org.jivesoftware.smackx.packet.JingleTransport getJingleTransport(TransportCandidate candidate) {
org.jivesoftware.smackx.packet.JingleTransport.Ice jt = new org.jivesoftware.smackx.packet.JingleTransport.Ice();
jt.addCandidate(new org.jivesoftware.smackx.packet.JingleTransport.Ice.Candidate(candidate));
return jt;
}
/**
* Obtain the best remote candidate obtained in the negotiation so far.
*
* @return the bestRemoteCandidate
*/
public TransportCandidate getBestRemoteCandidate() {
TransportCandidate.Ice result = null;
ArrayList<TransportCandidate.Ice> cands = getValidRemoteCandidatesList();
if (!cands.isEmpty()) {
int lowest = 65560;
TransportCandidate.Ice chose = null;
for (TransportCandidate.Ice transportCandidate : cands) {
System.out.println("Pref: " + transportCandidate.getPreference() + " :" + transportCandidate.getIp());
if (transportCandidate.getPreference() < lowest) {
chose = transportCandidate;
lowest = transportCandidate.getPreference();
}
}
result = chose;
}
return result;
}
/**
* Return true for ICE candidates.
*/
public boolean acceptableTransportCandidate(TransportCandidate tc) {
try {
InetAddress.getByName(tc.getIp()).isReachable(3000);
DatagramSocket socket = new DatagramSocket(0);
socket.connect(InetAddress.getByName(tc.getIp()), tc.getPort());
return true;
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
}

View file

@ -0,0 +1,433 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A TransportResolver is used for obtaining a list of valid transport
* candidates. A transport candidate is composed by an IP address and a port number.
* It is called candidate, because it can be elected or not.
*
* @author Thiago Camargo
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class TransportResolver {
public enum Type {
rawupd, ice
}
;
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Type type = Type.rawupd;
// the time, in milliseconds, before a check aborts
public static final int CHECK_TIMEOUT = 3000;
// Listeners for events
private final ArrayList listeners = new ArrayList();
// TRue if the resolver is working
private boolean resolving;
// This will be true when all the transport candidates have been gathered...
private boolean resolved;
// This indicates that a transport resolver is initialized
private boolean initialized = false;
// We store a list of candidates internally, just in case there are several
// possibilities. When the user asks for a transport, we return the best
// one.
protected final List candidates = new ArrayList();
// Remote candidates that are being checked
private final static ArrayList candidatesChecking = new ArrayList();
/**
* Default constructor.
*/
protected TransportResolver() {
super();
resolving = false;
resolved = false;
}
/**
* Initialize the Resolver
*/
public abstract void initialize() throws XMPPException;
/**
* Start a the resolution.
*/
public abstract void resolve() throws XMPPException;
/**
* Clear the list of candidates and start a new resolution process.
*
* @throws XMPPException
*/
public void clear() throws XMPPException {
cancel();
candidates.clear();
//resolve();
}
/**
* Cancel any asynchronous resolution operation.
*/
public abstract void cancel() throws XMPPException;
/**
* Return true if the resolver is working.
*
* @return true if the resolver is working.
*/
public boolean isResolving() {
return resolving;
}
/**
* Return true if the resolver has finished the search for transport
* candidates.
*
* @return true if the search has finished
*/
public boolean isResolved() {
return resolved;
}
/**
* Set the Transport Resolver as initialized.
*/
public synchronized void setInitialized() {
initialized = true;
}
/**
* Chack if the Transport Resolver is initialized
*
* @return
*/
public synchronized boolean isInitialized() {
return initialized;
}
/**
* Indicate the beggining of the resolution process. This method must be
* used by subclasses at the begining of their resolve() method.
*/
protected synchronized void setResolveInit() {
resolved = false;
resolving = true;
triggerResolveInit();
}
/**
* Indicate the end of the resolution process. This method must be used by
* subclasses at the begining of their resolve() method.
*/
protected synchronized void setResolveEnd() {
resolved = true;
resolving = false;
triggerResolveEnd();
}
// Listeners management
/**
* Add a transport resolver listener.
*
* @param li The transport resolver listener to be added.
*/
public void addListener(TransportResolverListener li) {
synchronized (listeners) {
listeners.add(li);
}
}
/**
* Removes a transport resolver listener.
*
* @param li The transport resolver listener to be removed
*/
public void removeListener(TransportResolverListener li) {
synchronized (listeners) {
listeners.remove(li);
}
}
/**
* Get the list of listeners
*
* @return the list of listeners
*/
public ArrayList getListenersList() {
synchronized (listeners) {
return new ArrayList(listeners);
}
}
/**
* Trigger a new candidate added event.
*
* @param cand The candidate added to the list of candidates.
*/
protected void triggerCandidateAdded(TransportCandidate cand) {
Iterator iter = getListenersList().iterator();
while (iter.hasNext()) {
TransportResolverListener trl = (TransportResolverListener) iter.next();
if (trl instanceof TransportResolverListener.Resolver) {
TransportResolverListener.Resolver li = (TransportResolverListener.Resolver) trl;
System.out.println("triggerCandidateAdded : " + cand.getLocalIp());
li.candidateAdded(cand);
}
}
}
/**
* Trigger a event notifying the initialization of the resolution process.
*/
private void triggerResolveInit() {
Iterator iter = getListenersList().iterator();
while (iter.hasNext()) {
TransportResolverListener trl = (TransportResolverListener) iter.next();
if (trl instanceof TransportResolverListener.Resolver) {
TransportResolverListener.Resolver li = (TransportResolverListener.Resolver) trl;
li.init();
}
}
}
/**
* Trigger a event notifying the obtention of all the candidates.
*/
private void triggerResolveEnd() {
Iterator iter = getListenersList().iterator();
while (iter.hasNext()) {
TransportResolverListener trl = (TransportResolverListener) iter.next();
if (trl instanceof TransportResolverListener.Resolver) {
TransportResolverListener.Resolver li = (TransportResolverListener.Resolver) trl;
li.end();
}
}
}
// Candidates management
/**
* Clear the list of candidate
*/
protected void clearCandidates() {
synchronized (candidates) {
candidates.clear();
}
}
/**
* Add a new transport candidate
*
* @param cand The candidate to add
*/
protected void addCandidate(TransportCandidate cand) {
synchronized (candidates) {
if (!candidates.contains(cand))
candidates.add(cand);
}
// Notify the listeners
triggerCandidateAdded(cand);
}
/**
* Get an iterator for the list of candidates
*
* @return an iterator
*/
public Iterator getCandidates() {
synchronized (candidates) {
System.out.println("CNUM: " + candidates.size());
return Collections.unmodifiableList(new ArrayList(candidates)).iterator();
}
}
/**
* Get the candididate with the highest preference.
*
* @return The best candidate, according to the preference order.
*/
public TransportCandidate getPreferredCandidate() {
TransportCandidate result = null;
ArrayList cands = (ArrayList) getCandidatesList();
if (cands.size() > 0) {
Collections.sort(cands);
// Return the last candidate
result = (TransportCandidate) cands.get(cands.size() - 1);
System.out.println("Result: " + result.getIp());
}
return result;
}
/**
* Get the numer of transport candidates.
*
* @return The length of the transport candidates list.
*/
public int getCandidateCount() {
synchronized (candidates) {
return candidates.size();
}
}
/**
* Get the list of candidates
*
* @return the list of transport candidates
*/
public List getCandidatesList() {
ArrayList result = null;
synchronized (candidates) {
result = new ArrayList(candidates);
}
return result;
}
/**
* Get the n-th candidate
*
* @return a transport candidate
*/
public TransportCandidate getCandidate(int i) {
TransportCandidate cand;
synchronized (candidates) {
cand = (TransportCandidate) candidates.get(i);
}
return cand;
}
/**
* Initialize Transport Resolver and wait until it is complete unitialized.
*/
public void initializeAndWait() throws XMPPException {
this.initialize();
try {
System.out.print("Initializing...");
while (!this.isInitialized()) {
System.out.print(".");
Thread.sleep(1000);
}
System.out.print("Resolved\n");
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* Obtain a free port we can use.
*
* @return A free port number.
*/
protected int getFreePort() {
ServerSocket ss;
int freePort = 0;
for (int i = 0; i < 10; i++) {
freePort = (int) (10000 + Math.round(Math.random() * 10000));
freePort = freePort % 2 == 0 ? freePort : freePort + 1;
try {
ss = new ServerSocket(freePort);
freePort = ss.getLocalPort();
ss.close();
return freePort;
}
catch (IOException e) {
e.printStackTrace();
}
}
try {
ss = new ServerSocket(0);
freePort = ss.getLocalPort();
ss.close();
}
catch (IOException e) {
e.printStackTrace();
}
return freePort;
}
}

View file

@ -0,0 +1,48 @@
package org.jivesoftware.smackx.jingle.nat;
/**
* Transport resolver Interface
*/
public abstract interface TransportResolverListener {
/**
* Resolver listener.
*/
public interface Resolver extends TransportResolverListener {
/**
* The resolution process has been started.
*/
public void init();
/**
* A transport candidate has been added
*
* @param cand The transport candidate.
*/
public void candidateAdded(TransportCandidate cand);
/**
* All the transport candidates have been obtained.
*/
public void end();
}
/**
* Resolver checker.
*/
public interface Checker extends TransportResolverListener {
/**
* A transport candidate has been checked.
*
* @param cand The transport candidate that has been checked.
* @param result True if the candidate is usable.
*/
public void candidateChecked(TransportCandidate cand, boolean result);
/**
* A transport candidate is being checked.
*
* @param cand The transport candidate that is being checked.
*/
public void candidateChecking(TransportCandidate cand);
}
}

View file

@ -0,0 +1 @@
<body>Smack extensions API.</body>

View file

@ -0,0 +1,504 @@
/**
* $RCSfile$
* $Revision: 2407 $
* $Date: 2004-11-02 23:37:00 +0000 (Tue, 02 Nov 2004) $
*
* Copyright 2003-2004 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.packet;
import org.jivesoftware.smack.packet.IQ;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* An Jingle sub-packet, which is used by XMPP clients to exchange info like
* descriptions and transports. <p/> The following link summarizes the
* requirements of Jingle IM: <a
* href="http://www.jabber.org/jeps/jep-0166.html">Valid tags</a>.
* <p/>
* <p/> Warning: this is an non-standard protocol documented by <a
* href="http://www.jabber.org/jeps/jep-0166.html">JEP-166</a>. Because this is
* a non-standard protocol, it is subject to change.
*
* @author Alvaro Saurin
*/
public class Jingle extends IQ {
// static
public static final String NAMESPACE = "http://jabber.org/protocol/jingle";
public static final String NODENAME = "jingle";
// non-static
private String sid; // The session id
private Action action; // The action associated to the Jingle
private String initiator; // The initiator as a "user@host/resource"
private String responder; // The responder
// Sub-elements of a Jingle object.
private final List descriptions = new ArrayList();
private final List transports = new ArrayList();
private JingleContentInfo contentInfo;
/**
* A constructor where the main components can be initialized.
*/
public Jingle(final List descs, final List trans, final JingleContentInfo mi,
final String sid) {
super();
if (descs != null) {
descriptions.addAll(descs);
}
if (trans != null) {
transports.addAll(trans);
}
setContentInfo(mi);
setSid(sid);
// Set null all other fields in the packet
initiator = null;
responder = null;
action = null;
}
/**
* Constructor with a description.
*
* @param descr a description
*/
public Jingle(final JingleContentDescription descr) {
super();
addDescription(descr);
// Set null all other fields in the packet
initiator = null;
responder = null;
// Some default values for the most common situation...
action = Jingle.Action.DESCRIPTIONINFO;
this.setType(IQ.Type.SET);
}
/**
* Constructor with a transport.
*
* @param trans a transport
*/
public Jingle(final JingleTransport trans) {
super();
addTransport(trans);
// Set null all other fields in the packet
initiator = null;
responder = null;
// Some default values for the most common situation...
action = Jingle.Action.TRANSPORTINFO;
this.setType(IQ.Type.SET);
}
/**
* Constructor with a content info.
*
* @param info The content info
*/
public Jingle(final JingleContentInfo info) {
super();
setContentInfo(info);
// Set null all other fields in the packet
initiator = null;
responder = null;
// Some default values for the most common situation...
action = Jingle.Action.DESCRIPTIONINFO;
this.setType(IQ.Type.SET);
}
/**
* A constructor where the action can be specified.
*
* @param action The action.
*/
public Jingle(final Jingle.Action action) {
this(null, null, null, null);
this.action = action;
// In general, a Jingle with an action is used in a SET packet...
this.setType(IQ.Type.SET);
}
/**
* A constructor where the session ID can be specified.
*
* @param sid The session ID related to the negotiation.
* @see #setSid(String)
*/
public Jingle(final String sid) {
this(null, null, null, sid);
}
/**
* The default constructor
*/
public Jingle() {
super();
}
/**
* Set the session ID related to this session. The session ID is a unique
* identifier generated by the initiator. This should match the XML Nmtoken
* production so that XML character escaping is not needed for characters
* such as &.
*
* @param sid the session ID
*/
public final void setSid(final String sid) {
this.sid = sid;
}
/**
* Returns the session ID related to the session. The session ID is a unique
* identifier generated by the initiator. This should match the XML Nmtoken
* production so that XML character escaping is not needed for characters
* such as &.
*
* @return Returns the session ID related to the session.
* @see #setSid(String)
*/
public String getSid() {
return sid;
}
/**
* Returns the XML element name of the extension sub-packet root element.
* Always returns "jingle"
*
* @return the XML element name of the packet extension.
*/
public static String getElementName() {
return NODENAME;
}
/**
* Returns the XML namespace of the extension sub-packet root element.
* According the specification the namespace is always
* "http://jabber.org/protocol/jingle"
*
* @return the XML namespace of the packet extension.
*/
public static String getNamespace() {
return NAMESPACE;
}
/**
* @return the audioInfo
*/
public JingleContentInfo getContentInfo() {
return contentInfo;
}
/**
* @param contentInfo the audioInfo to set
*/
public void setContentInfo(final JingleContentInfo contentInfo) {
this.contentInfo = contentInfo;
}
/**
* Get an iterator for the content descriptions
*
* @return the descriptions
*/
public Iterator getDescriptions() {
synchronized (descriptions) {
return Collections.unmodifiableList(new ArrayList(descriptions)).iterator();
}
}
/**
* Get an iterator for the content descriptions
*
* @return the descriptions
*/
public ArrayList getDescriptionsList() {
synchronized (descriptions) {
return new ArrayList(descriptions);
}
}
/**
* Add a new content description.
*
* @param desc the descriptions to add
*/
public void addDescription(final JingleContentDescription desc) {
if (desc != null) {
synchronized (descriptions) {
descriptions.add(desc);
}
}
}
/**
* Add a list of JingleContentDescription elements
*
* @param descsList the list of transports to add
*/
public void addDescriptions(final List descsList) {
if (descsList != null) {
synchronized (descriptions) {
descriptions.addAll(descsList);
}
}
}
/**
* Get an iterator for the transport.
*
* @return the transports
*/
public Iterator getTransports() {
synchronized (transports) {
return Collections.unmodifiableList(new ArrayList(transports)).iterator();
}
}
/**
* Get the list of transports.
*
* @return the transports list.
*/
public ArrayList getTransportsList() {
synchronized (transports) {
return new ArrayList(transports);
}
}
/**
* Add a new TransportNegotiator element
*
* @param trans the transports to add
*/
public void addTransport(final JingleTransport trans) {
if (trans != null) {
synchronized (transports) {
transports.add(trans);
}
}
}
/**
* Add a list of TransportNegotiator elements
*
* @param transList the list of transports to add
*/
public void addTransports(final List transList) {
if (transList != null) {
synchronized (transports) {
transports.addAll(transList);
}
}
}
/**
* Get the action specified in the packet
*
* @return the action
*/
public Action getAction() {
return action;
}
/**
* Set the action in the packet
*
* @param action the action to set
*/
public void setAction(final Action action) {
this.action = action;
}
/**
* Get the initiator. The initiator will be the full JID of the entity that
* has initiated the flow (which may be different to the "from" address in
* the IQ)
*
* @return the initiator
*/
public String getInitiator() {
return initiator;
}
/**
* Set the initiator. The initiator must be the full JID of the entity that
* has initiated the flow (which may be different to the "from" address in
* the IQ)
*
* @param initiator the initiator to set
*/
public void setInitiator(final String initiator) {
this.initiator = initiator;
}
/**
* Get the responder. The responder is the full JID of the entity that has
* replied to the initiation (which may be different to the "to" addresss in
* the IQ).
*
* @return the responder
*/
public String getResponder() {
return responder;
}
/**
* Set the responder. The responder must be the full JID of the entity that
* has replied to the initiation (which may be different to the "to"
* addresss in the IQ).
*
* @param resp the responder to set
*/
public void setResponder(final String resp) {
responder = resp;
}
/**
* Get a hash key for the session this packet belongs to.
*
* @param sid The session id
* @param initiator The initiator
* @return A hash key
*/
public static int getSessionHash(final String sid, final String initiator) {
final int PRIME = 31;
int result = 1;
result = PRIME * result + (initiator == null ? 0 : initiator.hashCode());
result = PRIME * result + (sid == null ? 0 : sid.hashCode());
return result;
}
/**
* Return the XML representation of the packet.
*
* @return the XML string
*/
public String getChildElementXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName());
buf.append(" xmlns=\"").append(getNamespace()).append("\"");
if (getInitiator() != null) {
buf.append(" initiator=\"").append(getInitiator()).append("\"");
}
if (getResponder() != null) {
buf.append(" responder=\"").append(getResponder()).append("\"");
}
if (getAction() != null) {
buf.append(" action=\"").append(getAction()).append("\"");
}
if (getSid() != null) {
buf.append(" sid=\"").append(getSid()).append("\"");
}
buf.append(">");
//TODO Update to accept more than one content per session (XEP-0166)
buf.append("<content name='Audio-Content'>");
// Look for possible payload types, and dump them.
synchronized (descriptions) {
for (int i = 0; i < descriptions.size(); i++) {
JingleContentDescription desc = (JingleContentDescription) descriptions
.get(i);
buf.append(desc.toXML());
}
}
// If the packet has transports, dump them.
synchronized (transports) {
for (int i = 0; i < transports.size(); i++) {
JingleTransport trans = (JingleTransport) transports.get(i);
buf.append(trans.toXML());
}
}
buf.append("</content>");
// and the same for audio jmf info
if (contentInfo != null) {
buf.append(contentInfo.toXML());
}
buf.append("</").append(getElementName()).append(">");
return buf.toString();
}
/**
* The "action" in the jingle packet, as an enum.
*/
public static enum Action {
CONTENTACCEPT, CONTENTADD, CONTENTDECLINE, CONTENTMODIFY,
CONTENTREMOVE, DESCRIPTIONADD, DESCRIPTIONDECLINE,
DESCRIPTIONINFO, DESCRIPTIONMODIFY, SESSIONACCEPT,
SESSIONINFO, SESSIONINITIATE, SESSIONREDIRECT,
SESSIONTERMINATE, TRANSPORTACCEPT, TRANSPORTDECLINE,
TRANSPORTINFO, TRANSPORTMODIFY;
private static String names[] = {"content-accept", "content-add", "content-decline", "content-modify",
"content-remove", "description-accept", "description-decline", "description-info",
"description-modify", "session-accept", "session-info", "session-initiate",
"session-redirect", "session-terminate", "transport-accept", "transport-decline",
"transport-info", "transport-modify"};
/**
* Returns the String value for an Action.
*/
public String toString() {
return names[this.ordinal()];
}
/**
* Returns the Action for a String value.
*/
public static Action getAction(String str) {
for (int i = 0; i < names.length; i++) {
if (names[i].equals(str)) return Action.values()[i];
}
return null;
}
}
}

View file

@ -0,0 +1,278 @@
package org.jivesoftware.smackx.packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* Jingle content description.
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class JingleContentDescription implements PacketExtension {
// static
public static final String NODENAME = "description";
// non-static
private final List payloads = new ArrayList();
/**
* Creates a content description..
*/
public JingleContentDescription() {
super();
}
/**
* Returns the XML element name of the element.
*
* @return the XML element name of the element.
*/
public String getElementName() {
return NODENAME;
}
/**
* Return the namespace.
*
* @return The namespace
*/
public abstract String getNamespace();
/**
* Adds a audio payload type to the packet.
*
* @param pt the audio payload type to add.
*/
public void addJinglePayloadType(final JinglePayloadType pt) {
synchronized (payloads) {
payloads.add(pt);
}
}
/**
* Adds a list of payloads to the packet.
*
* @param pts the payloads to add.
*/
public void addAudioPayloadTypes(final List pts) {
synchronized (payloads) {
Iterator ptIter = pts.iterator();
while (ptIter.hasNext()) {
PayloadType.Audio pt = (PayloadType.Audio) ptIter.next();
addJinglePayloadType(new JinglePayloadType.Audio(pt));
}
}
}
/**
* Returns an Iterator for the audio payloads in the packet.
*
* @return an Iterator for the audio payloads in the packet.
*/
public Iterator getJinglePayloadTypes() {
return Collections.unmodifiableList(getJinglePayloadTypesList()).iterator();
}
/**
* Returns a list for the audio payloads in the packet.
*
* @return a list for the audio payloads in the packet.
*/
public ArrayList getJinglePayloadTypesList() {
synchronized (payloads) {
return new ArrayList(payloads);
}
}
/**
* Return the list of Payload types contained in the description.
*
* @return a list of PayloadType.Audio
*/
public ArrayList getAudioPayloadTypesList() {
ArrayList result = new ArrayList();
Iterator jinglePtsIter = getJinglePayloadTypes();
while (jinglePtsIter.hasNext()) {
JinglePayloadType jpt = (JinglePayloadType) jinglePtsIter.next();
if (jpt instanceof JinglePayloadType.Audio) {
JinglePayloadType.Audio jpta = (JinglePayloadType.Audio) jpt;
result.add(jpta.getPayloadType());
}
}
return result;
}
/**
* Returns a count of the audio payloads in the Jingle packet.
*
* @return the number of audio payloads in the Jingle packet.
*/
public int getJinglePayloadTypesCount() {
synchronized (payloads) {
return payloads.size();
}
}
/**
* Convert a Jingle description to XML.
*
* @return a string with the XML representation
*/
public String toXML() {
StringBuilder buf = new StringBuilder();
synchronized (payloads) {
if (payloads.size() > 0) {
buf.append("<").append(getElementName());
buf.append(" xmlns=\"").append(getNamespace()).append("\" >");
Iterator pt = payloads.listIterator();
while (pt.hasNext()) {
JinglePayloadType pte = (JinglePayloadType) pt.next();
buf.append(pte.toXML());
}
buf.append("</").append(getElementName()).append(">");
}
}
return buf.toString();
}
/**
* Jingle audio description
*/
public static class Audio extends JingleContentDescription {
public static final String NAMESPACE = "http://jabber.org/protocol/jingle/description/audio";
public Audio() {
super();
}
/**
* Utility constructor, with a JinglePayloadType
*/
public Audio(final JinglePayloadType pt) {
super();
addJinglePayloadType(pt);
}
public String getNamespace() {
return NAMESPACE;
}
}
/**
* A payload type, contained in a descriptor.
*
* @author Alvaro Saurin
*/
public static class JinglePayloadType {
public static final String NODENAME = "payload-type";
private PayloadType payload;
/**
* Create a payload type.
*
* @param payload the payload
*/
public JinglePayloadType(final PayloadType payload) {
super();
this.payload = payload;
}
/**
* Create an empty payload type.
*/
public JinglePayloadType() {
this(null);
}
/**
* Returns the XML element name of the element.
*
* @return the XML element name of the element.
*/
public static String getElementName() {
return NODENAME;
}
/**
* Get the payload represented.
*
* @return the payload
*/
public PayloadType getPayloadType() {
return payload;
}
/**
* Set the payload represented.
*
* @param payload the payload to set
*/
public void setPayload(final PayloadType payload) {
this.payload = payload;
}
protected String getChildAttributes() {
return null;
}
public String toXML() {
StringBuilder buf = new StringBuilder();
if (payload != null) {
buf.append("<").append(getElementName()).append(" ");
// We covert here the payload type to XML
if (payload.getId() != PayloadType.INVALID_PT) {
buf.append(" id=\"").append(payload.getId()).append("\"");
}
if (payload.getName() != null) {
buf.append(" name=\"").append(payload.getName()).append("\"");
}
if (payload.getChannels() != 0) {
buf.append(" channels=\"").append(payload.getChannels()).append("\"");
}
if (getChildAttributes() != null) {
buf.append(getChildAttributes());
}
buf.append("/>");
}
return buf.toString();
}
/**
* Audio payload type element
*/
public static class Audio extends JinglePayloadType {
public Audio(final PayloadType.Audio audio) {
super(audio);
}
protected String getChildAttributes() {
StringBuilder buf = new StringBuilder();
PayloadType pt = getPayloadType();
if (pt instanceof PayloadType.Audio) {
PayloadType.Audio pta = (PayloadType.Audio) pt;
buf.append(" clockrate=\"").append(pta.getClockRate()).append("\" ");
}
return buf.toString();
}
}
}
}

View file

@ -0,0 +1,138 @@
package org.jivesoftware.smackx.packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.jingle.media.ContentInfo;
/**
* Jingle content info
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public class JingleContentInfo implements PacketExtension {
protected ContentInfo mediaInfoElement;
private String namespace;
/**
* Empty constructor, with no jmf info.
*/
public JingleContentInfo() {
this(null);
}
/**
* Constructor with a jmf info
*
* @param mediaInfoElement MediaInfo element
*/
public JingleContentInfo(final ContentInfo mediaInfoElement) {
super();
this.mediaInfoElement = mediaInfoElement;
}
/**
* Get the jmf info element.
*
* @return the mediaInfoElement
*/
public ContentInfo getMediaInfo() {
return mediaInfoElement;
}
/**
* Get the element name
*/
public String getElementName() {
// Media info is supposed to be just a single-word command...
return getMediaInfo().toString();
}
/**
* Set the name space.
*
* @param ns the namespace
*/
protected void setNamespace(final String ns) {
namespace = ns;
}
/**
* Get the publilc namespace
*/
public String getNamespace() {
return namespace;
}
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName()).append(" xmlns=\"");
buf.append(getNamespace()).append("\" ");
buf.append("/>");
return buf.toString();
}
/**
* Transport part of a Jingle packet.
*/
public static class Audio extends JingleContentInfo {
public static final String NAMESPACE = "http://jabber.org/protocol/jingle/info/audio";
public Audio(final ContentInfo mi) {
super(mi);
setNamespace(NAMESPACE);
}
public String getNamespace() {
return NAMESPACE;
}
// Subclasses: specialize the Audio jmf info...
/**
* Busy jmf info.
*/
public static class Busy extends Audio {
public Busy() {
super(ContentInfo.Audio.BUSY);
}
}
/**
* Hold jmf info.
*/
public static class Hold extends Audio {
public Hold() {
super(ContentInfo.Audio.HOLD);
}
}
/**
* Mute jmf info.
*/
public static class Mute extends Audio {
public Mute() {
super(ContentInfo.Audio.MUTE);
}
}
/**
* Queued jmf info.
*/
public static class Queued extends Audio {
public Queued() {
super(ContentInfo.Audio.QUEUED);
}
}
/**
* Ringing jmf info.
*/
public static class Ringing extends Audio {
public Ringing() {
super(ContentInfo.Audio.RINGING);
}
}
}
}

View file

@ -0,0 +1,102 @@
package org.jivesoftware.smackx.packet;
import org.jivesoftware.smack.packet.PacketExtension;
public class JingleError implements PacketExtension {
public static String NAMESPACE = "http://jabber.org/protocol/jingle#error";
public static final JingleError OUT_OF_ORDER = new JingleError("out-of-order");
public static final JingleError UNKNOWN_SESSION = new JingleError("unknown-session");
public static final JingleError UNSUPPORTED_CONTENT = new JingleError(
"unsupported-content");
public static final JingleError UNSUPPORTED_TRANSPORTS = new JingleError(
"unsupported-transports");
// Non standard error messages
public static final JingleError NO_COMMON_PAYLOAD = new JingleError(
"no-common-payload");
public static final JingleError NEGOTIATION_ERROR = new JingleError(
"negotiation-error");
public static final JingleError MALFORMED_STANZA = new JingleError("malformed-stanza");
private String message;
/**
* Creates a new error with the specified code and message.
*
* @param message a message describing the error.
*/
public JingleError(final String message) {
this.message = message;
}
/**
* Returns the message describing the error, or null if there is no message.
*
* @return the message describing the error, or null if there is no message.
*/
public String getMessage() {
return message;
}
/**
* Returns the error as XML.
*
* @return the error as XML.
*/
public String toXML() {
StringBuilder buf = new StringBuilder();
if (message != null) {
buf.append("<error type=\"cancel\">");
buf.append("<").append(message).append(" xmlns=\"").append(NAMESPACE).append(
"\"/>");
buf.append("</error>");
}
return buf.toString();
}
/**
* Returns a Action instance associated with the String value.
*/
public static JingleError fromString(String value) {
if (value != null) {
value = value.toLowerCase();
if (value.equals("out-of-order")) {
return OUT_OF_ORDER;
} else if (value.equals("unknown-session")) {
return UNKNOWN_SESSION;
} else if (value.equals("unsupported-content")) {
return UNSUPPORTED_CONTENT;
} else if (value.equals("unsupported-transports")) {
return UNSUPPORTED_TRANSPORTS;
} else if (value.equals("no-common-payload")) {
return NO_COMMON_PAYLOAD;
} else if (value.equals("negotiation-error")) {
return NEGOTIATION_ERROR;
} else if (value.equals("malformed-stanza")) {
return MALFORMED_STANZA;
}
}
return null;
}
public String toString() {
return getMessage();
}
public String getElementName() {
return message;
}
public String getNamespace() {
return NAMESPACE;
}
}

View file

@ -0,0 +1,409 @@
package org.jivesoftware.smackx.packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A jingle transport extension
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public class JingleTransport implements PacketExtension {
// static
public static final String NODENAME = "transport";
// non-static
protected String namespace;
protected final List candidates = new ArrayList();
/**
* Default constructor.
*/
public JingleTransport() {
super();
}
/**
* Utility constructor, with a transport candidate element.
*
* @param candidate A transport candidate element to add.
*/
public JingleTransport(final JingleTransportCandidate candidate) {
super();
addCandidate(candidate);
}
/**
* Copy constructor.
*
* @param tr the other jingle transport.
*/
public JingleTransport(final JingleTransport tr) {
if (tr != null) {
namespace = tr.namespace;
if (tr.candidates.size() > 0) {
candidates.addAll(tr.candidates);
}
}
}
/**
* Adds a transport candidate.
*
* @param candidate the candidate
*/
public void addCandidate(final JingleTransportCandidate candidate) {
if (candidate != null) {
synchronized (candidates) {
candidates.add(candidate);
}
}
}
/**
* Get an iterator for the candidates
*
* @return an iterator
*/
public Iterator getCandidates() {
return Collections.unmodifiableList(getCandidatesList()).iterator();
}
/**
* Get the list of candidates.
*
* @return The candidates list.
*/
public ArrayList getCandidatesList() {
ArrayList res = null;
synchronized (candidates) {
res = new ArrayList(candidates);
}
return res;
}
/**
* Get the number of transport candidates.
*
* @return The number of transport candidates contained.
*/
public int getCandidatesCount() {
return getCandidatesList().size();
}
/**
* Returns the XML element name of the element.
*
* @return the XML element name of the element.
*/
public String getElementName() {
return NODENAME;
}
/**
* Set the namespace.
*
* @param ns The namespace
*/
protected void setNamespace(final String ns) {
namespace = ns;
}
/**
* Get the namespace.
*
* @return The namespace
*/
public String getNamespace() {
return namespace;
}
/**
* Return the XML representation for this element.
*/
public String toXML() {
StringBuilder buf = new StringBuilder();
buf.append("<").append(getElementName()).append(" xmlns=\"");
buf.append(getNamespace()).append("\" ");
synchronized (candidates) {
if (getCandidatesCount() > 0) {
buf.append(">");
Iterator iter = getCandidates();
while (iter.hasNext()) {
JingleTransportCandidate candidate = (JingleTransportCandidate) iter
.next();
buf.append(candidate.toXML());
}
buf.append("</").append(getElementName()).append(">");
} else {
buf.append("/>");
}
}
return buf.toString();
}
/**
* Candidate element in the transport. This class acts as a view of the
* "TransportCandidate" in the Jingle space.
*
* @author Alvaro Saurin
* @see TransportCandidate
*/
public static abstract class JingleTransportCandidate {
public static final String NODENAME = "candidate";
// The transport candidate contained in the element.
protected TransportCandidate transportCandidate;
/**
* Creates a new TransportNegotiator child.
*/
public JingleTransportCandidate() {
super();
}
/**
* Creates a new TransportNegotiator child.
*
* @param candidate the jmf transport candidate
*/
public JingleTransportCandidate(final TransportCandidate candidate) {
super();
setMediaTransport(candidate);
}
/**
* Returns the XML element name of the element.
*
* @return the XML element name of the element.
*/
public static String getElementName() {
return NODENAME;
}
/**
* Get the current transportElement candidate.
*
* @return the transportElement candidate
*/
public TransportCandidate getMediaTransport() {
return transportCandidate;
}
/**
* Set the transportElement candidate.
*
* @param cand the transportElement candidate
*/
public void setMediaTransport(final TransportCandidate cand) {
if (cand != null) {
transportCandidate = cand;
}
}
/**
* Get the list of attributes.
*
* @return a string with the list of attributes.
*/
protected String getChildElements() {
return null;
}
/**
* Obtain a valid XML representation of a trancport candidate
*
* @return A string containing the XML dump of the transport candidate.
*/
public String toXML() {
StringBuilder buf = new StringBuilder();
String childElements = getChildElements();
if (transportCandidate != null && childElements != null) {
buf.append("<").append(getElementName()).append(" ");
buf.append(childElements);
buf.append("/>");
}
return buf.toString();
}
}
// Subclasses
/**
* RTP-ICE profile
*/
public static class Ice extends JingleTransport {
public static final String NAMESPACE = "http://jabber.org/protocol/jingle/transport/ice";
public Ice() {
super();
setNamespace(NAMESPACE);
}
/**
* Add a transport candidate
*
* @see org.jivesoftware.smackx.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.packet.JingleTransport.JingleTransportCandidate)
*/
public void addCandidate(final JingleTransportCandidate candidate) {
super.addCandidate(candidate);
}
/**
* Get the list of candidates. As a "raw-udp" transport can only contain
* one candidate, we use the first in the list...
*
* @see org.jivesoftware.smackx.packet.JingleTransport#getCandidates()
*/
public ArrayList getCandidatesList() {
ArrayList copy = new ArrayList();
ArrayList superCandidatesList = super.getCandidatesList();
for (int i = 0; i < superCandidatesList.size(); i++) {
copy.add(superCandidatesList.get(i));
}
return copy;
}
public static class Candidate extends JingleTransportCandidate {
/**
* Default constructor
*/
public Candidate() {
super();
}
/**
* Constructor with a transport candidate.
*/
public Candidate(final TransportCandidate tc) {
super(tc);
}
/**
* Get the elements of this candidate.
*/
protected String getChildElements() {
StringBuilder buf = new StringBuilder();
if (transportCandidate != null) {// && transportCandidate instanceof TransportCandidate.Ice) {
TransportCandidate.Ice tci = (TransportCandidate.Ice) transportCandidate;
// We convert the transportElement candidate to XML here...
buf.append(" generation=\"").append(tci.getGeneration()).append("\"");
buf.append(" ip=\"").append(tci.getIp()).append("\"");
buf.append(" port=\"").append(tci.getPort()).append("\"");
buf.append(" network=\"").append(tci.getNetwork()).append("\"");
buf.append(" username=\"").append(tci.getUsername()).append("\"");
buf.append(" password=\"").append(tci.getPassword()).append("\"");
buf.append(" preference=\"").append(tci.getPreference()).append("\"");
// Optional elements
if (transportCandidate.getName() != null) {
buf.append(" name=\"").append(tci.getName()).append("\"");
}
}
return buf.toString();
}
}
}
/**
* Raw UDP profile.
*/
public static class RawUdp extends JingleTransport {
public static final String NAMESPACE = "http://jabber.org/protocol/jingle/transport/raw-udp";
public RawUdp() {
super();
setNamespace(NAMESPACE);
}
/**
* Add a transport candidate
*
* @see org.jivesoftware.smackx.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.packet.JingleTransport.JingleTransportCandidate)
*/
public void addCandidate(final JingleTransportCandidate candidate) {
candidates.clear();
super.addCandidate(candidate);
}
/**
* Get the list of candidates. As a "raw-udp" transport can only contain
* one candidate, we use the first in the list...
*
* @see org.jivesoftware.smackx.packet.JingleTransport#getCandidates()
*/
public ArrayList getCandidatesList() {
ArrayList copy = new ArrayList();
ArrayList superCandidatesList = super.getCandidatesList();
if (superCandidatesList.size() > 0) {
copy.add(superCandidatesList.get(0));
}
return copy;
}
/**
* Raw-udp transport candidate.
*/
public static class Candidate extends JingleTransportCandidate {
/**
* Default constructor
*/
public Candidate() {
super();
}
/**
* Constructor with a transport candidate.
*/
public Candidate(final TransportCandidate tc) {
super(tc);
}
/**
* Get the elements of this candidate.
*/
protected String getChildElements() {
StringBuilder buf = new StringBuilder();
if (transportCandidate != null && transportCandidate instanceof TransportCandidate.Fixed) {
TransportCandidate.Fixed tcf = (TransportCandidate.Fixed) transportCandidate;
buf.append(" generation=\"").append(tcf.getGeneration()).append("\"");
buf.append(" ip=\"").append(tcf.getIp()).append("\"");
buf.append(" port=\"").append(tcf.getPort()).append("\"");
// Optional parameters
String name = tcf.getName();
if (name != null) {
buf.append(" name=\"").append(name).append("\"");
}
}
return buf.toString();
}
}
}
}

View file

@ -0,0 +1 @@
<body>XML packets that are part of the XMPP extension protocols.</body>

View file

@ -0,0 +1,125 @@
package org.jivesoftware.smackx.provider;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.packet.JingleContentDescription;
import org.jivesoftware.smackx.packet.JingleContentDescription.JinglePayloadType;
import org.xmlpull.v1.XmlPullParser;
/**
* Parser for a Jingle description
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class JingleContentDescriptionProvider implements PacketExtensionProvider {
/**
* Default constructor
*/
public JingleContentDescriptionProvider() {
super();
}
/**
* Parse a iq/jingle/description/payload-type element.
*
* @param parser the input to parse
* @return a payload type element
* @throws Exception
*/
protected JinglePayloadType parsePayload(final XmlPullParser parser)
throws Exception {
int ptId = 0;
String ptName;
int ptChannels = 0;
try {
ptId = Integer.parseInt(parser.getAttributeValue("", "id"));
} catch (Exception e) {
}
ptName = parser.getAttributeValue("", "name");
try {
ptChannels = Integer.parseInt(parser.getAttributeValue("", "channels"));
} catch (Exception e) {
}
return new JinglePayloadType(new PayloadType(ptId, ptName, ptChannels));
}
/**
* Parse a iq/jingle/description element.
*
* @param parser the input to parse
* @return a description element
* @throws Exception
*/
public PacketExtension parseExtension(final XmlPullParser parser) throws Exception {
boolean done = false;
JingleContentDescription desc = getInstance();
while (!done) {
int eventType = parser.next();
String name = parser.getName();
if (eventType == XmlPullParser.START_TAG) {
if (name.equals(JingleContentDescription.JinglePayloadType.NODENAME)) {
desc.addJinglePayloadType(parsePayload(parser));
} else {
throw new Exception("Unknow element \"" + name + "\" in content.");
}
} else if (eventType == XmlPullParser.END_TAG) {
if (name.equals(JingleContentDescription.NODENAME)) {
done = true;
}
}
}
return desc;
}
/**
* Return a new instance of this class. Subclasses must overwrite this
* method.
*/
protected abstract JingleContentDescription getInstance();
/**
* Jingle audio
*/
public static class Audio extends JingleContentDescriptionProvider {
/**
* Default constructor
*/
public Audio() {
super();
}
/**
* Parse an audio payload type.
*/
public JinglePayloadType parsePayload(final XmlPullParser parser)
throws Exception {
JinglePayloadType pte = super.parsePayload(parser);
PayloadType.Audio pt = new PayloadType.Audio(pte.getPayloadType());
int ptClockRate = 0;
try {
ptClockRate = Integer.parseInt(parser.getAttributeValue("", "clockrate"));
} catch (Exception e) {
}
pt.setClockRate(ptClockRate);
return new JinglePayloadType.Audio(pt);
}
/**
* Get a new instance of this object.
*/
protected JingleContentDescription getInstance() {
return new JingleContentDescription.Audio();
}
}
}

View file

@ -0,0 +1,106 @@
package org.jivesoftware.smackx.provider;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smackx.jingle.media.ContentInfo;
import org.jivesoftware.smackx.packet.JingleContentInfo;
import org.xmlpull.v1.XmlPullParser;
/**
* Jingle Audio jmf-info provider
*
* @author Alvaro Saurin
*/
public class JingleContentInfoProvider implements PacketExtensionProvider {
/**
* Creates a new provider. ProviderManager requires that every
* PacketExtensionProvider has a public, no-argument constructor
*/
public JingleContentInfoProvider() {
super();
}
public PacketExtension parseExtension(final XmlPullParser parser) throws Exception {
// This method must be overwritten by subclasses...
return null;
}
/**
* JingleContentDescription.Audio info provider
*/
public static class Audio extends JingleContentInfoProvider {
private PacketExtension audioInfo;
/**
* Empty constructor.
*/
public Audio() {
this(null);
}
/**
* Constructor with an audio info.
*
* @param audioInfo the jmf info
*/
public Audio(final PacketExtension audioInfo) {
super();
this.audioInfo = audioInfo;
}
/**
* Parse a JingleContentDescription.Audio extension.
*/
public PacketExtension parseExtension(final XmlPullParser parser)
throws Exception {
PacketExtension result = null;
if (audioInfo != null) {
result = audioInfo;
} else {
String elementName = parser.getName();
// Try to get an Audio content info
ContentInfo mi = ContentInfo.Audio.fromString(elementName);
if (mi != null) {
result = new JingleContentInfo.Audio(mi);
}
}
return result;
}
// Sub-elements
public static class Busy extends Audio {
public Busy() {
super(new JingleContentInfo.Audio.Busy());
}
}
public static class Hold extends Audio {
public Hold() {
super(new JingleContentInfo.Audio.Hold());
}
}
public static class Mute extends Audio {
public Mute() {
super(new JingleContentInfo.Audio.Mute());
}
}
public static class Queued extends Audio {
public Queued() {
super(new JingleContentInfo.Audio.Queued());
}
}
public static class Ringing extends Audio {
public Ringing() {
super(new JingleContentInfo.Audio.Ringing());
}
}
}
}

View file

@ -0,0 +1,128 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright 2003-2005 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.provider;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smackx.packet.Jingle;
import org.jivesoftware.smackx.packet.JingleContentDescription;
import org.jivesoftware.smackx.packet.JingleContentInfo;
import org.jivesoftware.smackx.packet.JingleTransport;
import org.xmlpull.v1.XmlPullParser;
/**
* The JingleProvider parses Jingle packets.
*
* @author Alvaro Saurin
*/
public class JingleProvider implements IQProvider {
/**
* Creates a new provider. ProviderManager requires that every
* PacketExtensionProvider has a public, no-argument constructor
*/
public JingleProvider() {
super();
}
/**
* Parse a iq/jingle element.
*/
public IQ parseIQ(final XmlPullParser parser) throws Exception {
Jingle jingle = new Jingle();
String sid = "";
Jingle.Action action;
String initiator = "";
String responder = "";
boolean done = false;
// Sub-elements providers
JingleContentDescriptionProvider jdpAudio = new JingleContentDescriptionProvider.Audio();
JingleTransportProvider jtpRawUdp = new JingleTransportProvider.RawUdp();
JingleTransportProvider jtpIce = new JingleTransportProvider.Ice();
JingleContentInfoProvider jmipAudio = new JingleContentInfoProvider.Audio();
int eventType;
String elementName;
String namespace;
// Get some attributes for the <jingle> element
sid = parser.getAttributeValue("", "sid");
action = Jingle.Action.getAction(parser.getAttributeValue("", "action"));
initiator = parser.getAttributeValue("", "initiator");
responder = parser.getAttributeValue("", "responder");
jingle.setSid(sid);
jingle.setAction(action);
jingle.setInitiator(initiator);
jingle.setResponder(responder);
// Start processing sub-elements
while (!done) {
eventType = parser.next();
elementName = parser.getName();
namespace = parser.getNamespace();
if (eventType == XmlPullParser.START_TAG) {
// Parse some well know subelements, depending on the namespaces
// and element names...
if (elementName.equals(JingleContentDescription.NODENAME)
&& namespace.equals(JingleContentDescription.Audio.NAMESPACE)) {
jingle.addDescription((JingleContentDescription) jdpAudio
.parseExtension(parser));
} else if (elementName.equals(JingleTransport.NODENAME)) {
// Parse the possible transport namespaces
if (namespace.equals(JingleTransport.RawUdp.NAMESPACE)) {
jingle.addTransport((JingleTransport) jtpRawUdp
.parseExtension(parser));
} else if (namespace.equals(JingleTransport.Ice.NAMESPACE)) {
jingle.addTransport((JingleTransport) jtpIce
.parseExtension(parser));
} else {
throw new XMPPException("Unknown transport namespace \""
+ namespace + "\" in Jingle packet.");
}
} else if (namespace.equals(JingleContentInfo.Audio.NAMESPACE)) {
jingle.setContentInfo((JingleContentInfo) jmipAudio
.parseExtension(parser));
} else if (elementName.equals("content")) {
//TODO Separate Contents (XEP-0166)
} else {
throw new XMPPException("Unknown combination of namespace \""
+ namespace + "\" and element name \"" + elementName
+ "\" in Jingle packet.");
}
} else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(Jingle.getElementName())) {
done = true;
}
}
}
return jingle;
}
}

View file

@ -0,0 +1,231 @@
package org.jivesoftware.smackx.provider;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import org.jivesoftware.smackx.packet.JingleTransport;
import org.jivesoftware.smackx.packet.JingleTransport.JingleTransportCandidate;
import org.xmlpull.v1.XmlPullParser;
/**
* Provider for a Jingle transport element
*
* @author Alvaro Saurin <alvaro.saurin@gmail.com>
*/
public abstract class JingleTransportProvider implements PacketExtensionProvider {
/**
* Creates a new provider. ProviderManager requires that every
* PacketExtensionProvider has a public, no-argument constructor
*/
public JingleTransportProvider() {
super();
}
/**
* Obtain the corresponding TransportNegotiator instance.
*
* @return a new TransportNegotiator instance
*/
protected JingleTransport getInstance() {
return new JingleTransport();
}
/**
* Parse a iq/jingle/transport element.
*
* @param parser the structure to parse
* @return a transport element.
* @throws Exception
*/
public PacketExtension parseExtension(final XmlPullParser parser) throws Exception {
boolean done = false;
JingleTransport trans = getInstance();
while (!done) {
int eventType = parser.next();
String name = parser.getName();
if (eventType == XmlPullParser.START_TAG) {
if (name.equals(JingleTransportCandidate.NODENAME)) {
JingleTransportCandidate jtc = parseCandidate(parser);
if (jtc != null) trans.addCandidate(jtc);
} else {
throw new Exception("Unknown tag \"" + name + "\" in transport element.");
}
} else if (eventType == XmlPullParser.END_TAG) {
if (name.equals(JingleTransport.NODENAME)) {
done = true;
}
}
}
return trans;
}
protected abstract JingleTransportCandidate parseCandidate(final XmlPullParser parser)
throws Exception;
/**
* RTP-ICE profile
*/
public static class Ice extends JingleTransportProvider {
/**
* Defauls constructor.
*/
public Ice() {
super();
}
/**
* Obtain the corresponding TransportNegotiator.Ice instance.
*
* @return a new TransportNegotiator.Ice instance
*/
protected JingleTransport getInstance() {
return new JingleTransport.Ice();
}
/**
* Parse a iq/jingle/transport/candidate element.
*
* @param parser the structure to parse
* @return a candidate element
* @throws Exception
*/
protected JingleTransportCandidate parseCandidate(XmlPullParser parser) throws Exception {
TransportCandidate.Ice mt = new TransportCandidate.Ice();
String channel = parser.getAttributeValue("", "channel");
String generation = parser.getAttributeValue("", "generation");
String ip = parser.getAttributeValue("", "ip");
String name = parser.getAttributeValue("", "name");
String network = parser.getAttributeValue("", "network");
String username = parser.getAttributeValue("", "username");
String password = parser.getAttributeValue("", "password");
String port = parser.getAttributeValue("", "port");
String preference = parser.getAttributeValue("", "preference");
String proto = parser.getAttributeValue("", "proto");
if (channel != null) {
mt.setChannel(new TransportCandidate.Channel(channel));
}
if (generation != null) {
try {
mt.setGeneration(Integer.parseInt(generation));
} catch (Exception e) {
}
}
if (ip != null) {
mt.setIp(ip);
} else {
return null;
}
if (name != null) {
mt.setName(name);
}
if (network != null) {
try {
mt.setNetwork(Integer.parseInt(network));
} catch (Exception e) {
}
}
if (username != null) {
mt.setUsername(username);
}
if (password != null) {
mt.setPassword(password);
}
if (port != null) {
try {
mt.setPort(Integer.parseInt(port));
} catch (Exception e) {
}
}
if (preference != null) {
try {
mt.setPreference(Integer.parseInt(preference));
} catch (Exception e) {
}
}
if (proto != null) {
mt.setProto(new TransportCandidate.Protocol(proto));
}
return new JingleTransport.Ice.Candidate(mt);
}
}
/**
* Raw UDP profile
*/
public static class RawUdp extends JingleTransportProvider {
/**
* Defauls constructor.
*/
public RawUdp() {
super();
}
/**
* Obtain the corresponding TransportNegotiator.RawUdp instance.
*
* @return a new TransportNegotiator.RawUdp instance
*/
protected JingleTransport getInstance() {
return new JingleTransport.RawUdp();
}
/**
* Parse a iq/jingle/transport/candidate element.
*
* @param parser the structure to parse
* @return a candidate element
* @throws Exception
*/
protected JingleTransportCandidate parseCandidate(XmlPullParser parser) throws Exception {
TransportCandidate.Fixed mt = new TransportCandidate.Fixed();
String generation = parser.getAttributeValue("", "generation");
String ip = parser.getAttributeValue("", "ip");
String name = parser.getAttributeValue("", "name");
String port = parser.getAttributeValue("", "port");
//System.out.println();
if (generation != null) {
try {
mt.setGeneration(Integer.parseInt(generation));
} catch (Exception e) {
}
}
if (ip != null) {
mt.setIp(ip);
}
if (name != null) {
mt.setName(name);
}
if (port != null) {
try {
mt.setPort(Integer.parseInt(port));
} catch (Exception e) {
}
}
return new JingleTransport.RawUdp.Candidate(mt);
}
}
}

View file

@ -0,0 +1 @@
<body>Provides pluggable parsing logic for Smack extensions.</body>

View file

@ -0,0 +1,4 @@
<body>API specification for <a href="http://www.jivesoftware.org/smack">Smack</a>, an Open Source XMPP client library.
<p>
The {@link org.jivesoftware.smack.XMPPConnection} class is the main entry point for the API.
</body>

View file

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- Default configuration for Smack test cases. -->
<testcase>
<!-- Host and port of the XMPP server to use -->
<host>localhost</host>
<port>5222</port>
<!-- Chat and MUC domain names to use -->
<chat>chat</chat>
<muc>conference</muc>
</testcase>

View file

@ -0,0 +1,368 @@
/**
* $RCSfile$
* $Revision: 5367 $
* $Date: 2006-09-14 16:16:40 -0300 (qui, 14 set 2006) $
*
* Copyright 2003-2005 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.test;
import junit.framework.TestCase;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.xmlpull.mxp1.MXParser;
import org.xmlpull.v1.XmlPullParser;
import javax.net.SocketFactory;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
/**
* Base class for all the test cases which provides a pre-configured execution context. This
* means that any test case that subclassifies this base class will have access to a pool of
* connections and to the user of each connection. The maximum number of connections in the pool
* can be controlled by the message {@link #getMaxConnections()} which every subclass must
* implement.<p>
*
* This base class defines a default execution context (i.e. host, port, chat domain and muc
* domain) which can be found in the file "config/test-case.xml". However, each subclass could
* redefine the default configuration by providing its own configuration file (if desired). The
* name of the configuration file must be of the form <test class name>.xml (e.g. RosterTest.xml).
* The file must be placed in the folder "config". This folder is where the default configuration
* file is being held.
*
* @author Gaston Dombiak
*/
public abstract class SmackTestCase extends TestCase {
private String host = "localhost";
private String serviceName = "localhost";
private int port = 5222;
private String chatDomain = "chat";
private String mucDomain = "conference";
private XMPPConnection[] connections = null;
/**
* Constructor for SmackTestCase.
* @param arg0
*/
public SmackTestCase(String arg0) {
super(arg0);
}
/**
* Returns the maximum number of connections to initialize for this test case. All the
* initialized connections will be connected to the server using a new test account for
* each conection.
*
* @return the maximum number of connections to initialize for this test case.
*/
protected abstract int getMaxConnections();
/**
* Returns a SocketFactory that will be used to create the socket to the XMPP server. By
* default no SocketFactory is used but subclasses my want to redefine this method.<p>
*
* A custom SocketFactory allows fine-grained control of the actual connection to the XMPP
* server. A typical use for a custom SocketFactory is when connecting through a SOCKS proxy.
*
* @return a SocketFactory that will be used to create the socket to the XMPP server.
*/
protected SocketFactory getSocketFactory() {
return null;
}
/**
* Returns the XMPPConnection located at the requested position. Each test case holds a
* pool of connections which is initialized while setting up the test case. The maximum
* number of connections is controlled by the message {@link #getMaxConnections()} which
* every subclass must implement.<p>
*
* If the requested position is greater than the connections size then an
* IllegalArgumentException will be thrown.
*
* @param index the position in the pool of the connection to look for.
* @return the XMPPConnection located at the requested position.
*/
protected XMPPConnection getConnection(int index) {
if (index > getMaxConnections()) {
throw new IllegalArgumentException("Index out of bounds");
}
return connections[index];
}
/**
* Returns the name of the user (e.g. johndoe) that is using the connection
* located at the requested position.
*
* @param index the position in the pool of the connection to look for.
* @return the user of the user (e.g. johndoe).
*/
protected String getUsername(int index) {
if (index > getMaxConnections()) {
throw new IllegalArgumentException("Index out of bounds");
}
return "user" + index;
}
/**
* Returns the bare XMPP address of the user (e.g. johndoe@jabber.org) that is
* using the connection located at the requested position.
*
* @param index the position in the pool of the connection to look for.
* @return the bare XMPP address of the user (e.g. johndoe@jabber.org).
*/
protected String getBareJID(int index) {
return getUsername(index) + "@" + getConnection(index).getServiceName();
}
/**
* Returns the full XMPP address of the user (e.g. johndoe@jabber.org/Smack) that is
* using the connection located at the requested position.
*
* @param index the position in the pool of the connection to look for.
* @return the full XMPP address of the user (e.g. johndoe@jabber.org/Smack).
*/
protected String getFullJID(int index) {
return getBareJID(index) + "/Smack";
}
protected String getHost() {
return host;
}
protected int getPort() {
return port;
}
protected String getServiceName() {
return serviceName;
}
/**
* Returns the default groupchat service domain.
*
* @return the default groupchat service domain.
*/
protected String getChatDomain() {
return chatDomain;
}
/**
* Returns the default MUC service domain.
*
* @return the default MUC service domain.
*/
protected String getMUCDomain() {
return mucDomain + "." + serviceName;
}
protected void setUp() throws Exception {
super.setUp();
init();
if (getMaxConnections() < 1) {
return;
}
connections = new XMPPConnection[getMaxConnections()];
try {
// Connect to the server
for (int i = 0; i < getMaxConnections(); i++) {
// Create the configuration for this new connection
ConnectionConfiguration config = new ConnectionConfiguration(host, port);
config.setTLSEnabled(true);
config.setCompressionEnabled(Boolean.getBoolean("test.compressionEnabled"));
config.setSASLAuthenticationEnabled(true);
if (getSocketFactory() == null) {
connections[i] = new XMPPConnection(config);
}
else {
connections[i] = new XMPPConnection(config, getSocketFactory());
}
connections[i].connect();
}
// Use the host name that the server reports. This is a good idea in most
// cases, but could fail if the user set a hostname in their XMPP server
// that will not resolve as a network connection.
host = connections[0].getHost();
serviceName = connections[0].getServiceName();
// Create the test accounts
if (!getConnection(0).getAccountManager().supportsAccountCreation())
fail("Server does not support account creation");
for (int i = 0; i < getMaxConnections(); i++) {
// Create the test account
try {
getConnection(i).getAccountManager().createAccount("user" + i, "user" + i);
} catch (XMPPException e) {
// Do nothing if the accout already exists
if (e.getXMPPError() == null || e.getXMPPError().getCode() != 409) {
throw e;
}
}
// Login with the new test account
getConnection(i).login("user" + i, "user" + i);
}
// Let the server process the available presences
Thread.sleep(150);
}
catch (Exception e) {
e.printStackTrace();
fail(e.getMessage());
}
}
protected void tearDown() throws Exception {
super.tearDown();
for (int i = 0; i < getMaxConnections(); i++) {
if (getConnection(i).isConnected()) {
// Delete the created account for the test
getConnection(i).getAccountManager().deleteAccount();
// Close the connection
getConnection(i).disconnect();
}
}
}
/**
* Initializes the context of the test case. We will first try to load the configuration from
* a file whose name is conformed by the test case class name plus an .xml extension
* (e.g RosterTest.xml). If no file was found under that name then we will try to load the
* default configuration for all the test cases from the file "config/test-case.xml".
*
*/
private void init() {
try {
boolean found = false;
// Try to load the configutation from an XML file specific for this test case
Enumeration resources =
ClassLoader.getSystemClassLoader().getResources(getConfigurationFilename());
while (resources.hasMoreElements()) {
found = parseURL((URL) resources.nextElement());
}
// If none was found then try to load the configuration from the default configuration
// file (i.e. "config/test-case.xml")
if (!found) {
resources = ClassLoader.getSystemClassLoader().getResources("config/test-case.xml");
while (resources.hasMoreElements()) {
found = parseURL((URL) resources.nextElement());
}
}
if (!found) {
System.err.println("File config/test-case.xml not found. Using default config.");
}
}
catch (Exception e) {
}
}
/**
* Returns true if the given URL was found and parsed without problems. The file provided
* by the URL must contain information useful for the test case configuration, such us,
* host and port of the server.
*
* @param url the url of the file to parse.
* @return true if the given URL was found and parsed without problems.
*/
private boolean parseURL(URL url) {
boolean parsedOK = false;
InputStream systemStream = null;
try {
systemStream = url.openStream();
XmlPullParser parser = new MXParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(systemStream, "UTF-8");
int eventType = parser.getEventType();
do {
if (eventType == XmlPullParser.START_TAG) {
if (parser.getName().equals("host")) {
host = parser.nextText();
}
else if (parser.getName().equals("port")) {
port = parseIntProperty(parser, port);
}
else if (parser.getName().equals("serviceName")) {
serviceName = parser.nextText();
}
else if (parser.getName().equals("chat")) {
chatDomain = parser.nextText();
}
else if (parser.getName().equals("muc")) {
mucDomain = parser.nextText();
}
}
eventType = parser.next();
}
while (eventType != XmlPullParser.END_DOCUMENT);
parsedOK = true;
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
systemStream.close();
}
catch (Exception e) {
}
}
return parsedOK;
}
private static int parseIntProperty(XmlPullParser parser, int defaultValue) throws Exception {
try {
return Integer.parseInt(parser.nextText());
}
catch (NumberFormatException nfe) {
nfe.printStackTrace();
return defaultValue;
}
}
/**
* Returns the name of the configuration file related to <b>this</b> test case. By default all
* the test cases will use the same configuration file. However, it's possible to override the
* default configuration by providing a file of the form <test case class name>.xml
* (e.g. RosterTest.xml).
*
* @return the name of the configuration file related to this test case.
*/
private String getConfigurationFilename() {
String fullClassName = this.getClass().getName();
int firstChar = fullClassName.lastIndexOf('.') + 1;
return "config/" + fullClassName.substring(firstChar) + ".xml";
}
/**
* Compares two contents of two byte arrays to make sure that they are equal
*
* @param message The message to show in the case of failure
* @param byteArray1 The first byte array.
* @param byteArray2 The second byte array.
*/
public static void assertEquals(String message, byte [] byteArray1, byte [] byteArray2) {
if(byteArray1.length != byteArray2.length) {
fail(message);
}
for(int i = 0; i < byteArray1.length; i++) {
assertEquals(message, byteArray1[i], byteArray2[i]);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
package org.jivesoftware.smackx.jingle;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.nat.BasicResolver;
public class JingleSessionTest extends SmackTestCase {
public JingleSessionTest(final String name) {
super(name);
}
public void testEqualsObject() {
JingleSession js1 = new OutgoingJingleSession(getConnection(0), "res1", null, new BasicResolver());
JingleSession js2 = new OutgoingJingleSession(getConnection(1), "res1", null, new BasicResolver());
JingleSession js3 = new OutgoingJingleSession(getConnection(2), "res2", null, new BasicResolver());
System.out.println(js1.getSid());
System.out.println(js2.getSid());
js1.setInitiator("js1");
js2.setInitiator("js1");
js1.setSid("10");
js2.setSid("10");
assertEquals(js1, js2);
assertEquals(js2, js1);
assertFalse(js1.equals(js3));
}
public void testGetInstanceFor() {
String ini1 = "initiator1";
String sid1 = "sid1";
String ini2 = "initiator2";
String sid2 = "sid2";
JingleSession js1 = new OutgoingJingleSession(getConnection(0), sid1, null, new BasicResolver());
JingleSession js2 = new OutgoingJingleSession(getConnection(1), sid2, null, new BasicResolver());
// For a packet, we should be able to get a session that handles that...
assertNotNull(JingleSession.getInstanceFor(getConnection(0)));
assertNotNull(JingleSession.getInstanceFor(getConnection(1)));
assertEquals(JingleSession.getInstanceFor(getConnection(0)), js1);
assertEquals(JingleSession.getInstanceFor(getConnection(1)), js2);
}
protected int getMaxConnections() {
return 3;
}
}

View file

@ -0,0 +1,74 @@
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2002-2006 Jive Software. All rights reserved.
* ====================================================================
* The Jive Software License (based on Apache Software License, Version 1.1)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* Jive Software (http://www.jivesoftware.com)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Smack" and "Jive Software" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please
* contact webmaster@jivesoftware.com.
*
* 5. Products derived from this software may not be called "Smack",
* nor may "Smack" appear in their name, without prior written
* permission of Jive Software.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JIVE SOFTWARE OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package org.jivesoftware.smackx.jingle;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Test suite that runs all the Jingle support tests
*
* @author Alvaro Saurin
*/
public class JingleSupportTests {
public static Test suite() {
TestSuite suite = new TestSuite("High and low level API tests for Jingle support");
// $JUnit-BEGIN$
suite.addTest(new TestSuite(JingleManagerTest.class));
// $JUnit-END$
return suite;
}
}

View file

@ -0,0 +1,89 @@
package org.jivesoftware.smackx.jingle;
import java.util.ArrayList;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.media.PayloadType;
public class PayloadTypeTest extends SmackTestCase {
public PayloadTypeTest(final String arg0) {
super(arg0);
}
public void testEqualsObject() {
PayloadType p1 = new PayloadType(0, "pt1", 2);
PayloadType p2 = new PayloadType(0, "pt1", 2);
assertTrue(p1.equals(p2));
}
/**
* Test for the difference of payloads.
*/
public void testDifference() {
ArrayList set1 = new ArrayList();
ArrayList set2 = new ArrayList();
PayloadType.Audio common1 = new PayloadType.Audio(34, "supercodec-1", 2, 14000);
PayloadType.Audio common2 = new PayloadType.Audio(56, "supercodec-2", 1, 44000);
set1.add(common1);
set1.add(common2);
set1.add(new PayloadType.Audio(36, "supercodec-3", 2, 28000));
set1.add(new PayloadType.Audio(45, "supercodec-4", 1, 98000));
set2.add(new PayloadType.Audio(27, "supercodec-3", 2, 28000));
set2.add(common2);
set2.add(new PayloadType.Audio(32, "supercodec-4", 1, 98000));
set2.add(common1);
// Get the difference
ArrayList commonSet = new ArrayList();
commonSet.addAll(set1);
commonSet.retainAll(set2);
assertTrue(commonSet.size() == 2);
System.out.println("Codec " + ((PayloadType.Audio)commonSet.get(0)).getId());
System.out.println("Codec " + ((PayloadType.Audio)commonSet.get(1)).getId());
assertTrue(commonSet.contains(common1));
assertTrue(commonSet.contains(common2));
}
/**
* Test for the difference of payloads when we are handling the same sets.
*/
public void testDifferenceSameSet() {
ArrayList set1 = new ArrayList();
ArrayList set2 = new ArrayList();
PayloadType.Audio common1 = new PayloadType.Audio(34, "supercodec-1", 2, 14000);
PayloadType.Audio common2 = new PayloadType.Audio(56, "supercodec-2", 1, 44000);
PayloadType.Audio common3 = new PayloadType.Audio(0, "supercodec-3", 1, 44000);
PayloadType.Audio common4 = new PayloadType.Audio(120, "supercodec-4", 2, 66060);
set1.add(common1);
set1.add(common2);
set1.add(common3);
set1.add(common4);
set2.add(common1);
set2.add(common2);
set2.add(common3);
set2.add(common4);
// Get the difference
ArrayList commonSet = new ArrayList();
commonSet.addAll(set1);
commonSet.retainAll(set2);
assertTrue(commonSet.size() == 4);
assertTrue(commonSet.contains(common1));
assertTrue(commonSet.contains(common2));
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,101 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.test.SmackTestCase;
public class BasicResolverTest extends SmackTestCase {
private int counter;
private final Object mutex = new Object();
public BasicResolverTest(String arg) {
super(arg);
}
// Counter management
private void resetCounter() {
synchronized (mutex) {
counter = 0;
}
}
private void incCounter() {
synchronized (mutex) {
counter++;
}
}
private int valCounter() {
int val;
synchronized (mutex) {
val = counter;
}
return val;
}
public void testCheckValidHostname() {
String validHostname = new String("slashdot.org");
BasicResolver br = new BasicResolver();
TransportCandidate tc = new TransportCandidate.Fixed(validHostname, 0);
resetCounter();
tc.addListener(new TransportResolverListener.Checker() {
public void candidateChecked(TransportCandidate cand, boolean result) {
if(result == true) {
System.out.println(cand.getIp() + " is reachable (as expected)");
incCounter();
}
}
public void candidateChecking(TransportCandidate cand) {
}
});
tc.check();
try {
Thread.sleep(TransportResolver.CHECK_TIMEOUT);
} catch (Exception e) {
}
assertTrue(valCounter() > 0);
}
public void testCheckInvalidHostname() {
String invalidHostname = new String("camupilosupino.org");
BasicResolver br = new BasicResolver();
TransportCandidate tc = new TransportCandidate.Fixed(invalidHostname, 0);
resetCounter();
tc.addListener(new TransportResolverListener.Checker() {
public void candidateChecked(TransportCandidate cand, boolean result) {
if(result == false) {
System.out.println(cand.getIp() + " is _not_ reachable (as expected)");
incCounter();
}
}
public void candidateChecking(TransportCandidate cand) {
}
});
tc.check();
try {
Thread.sleep(TransportResolver.CHECK_TIMEOUT);
} catch (Exception e) {
}
assertTrue(valCounter() > 0);
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,172 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.IncomingJingleSession;
import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
import org.jivesoftware.smackx.jingle.OutgoingJingleSession;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.JingleMediaManager;
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
public class BridgedResolverTest extends SmackTestCase {
private int counter;
private final Object mutex = new Object();
public BridgedResolverTest(String arg) {
super(arg);
}
// Counter management
private void resetCounter() {
synchronized (mutex) {
counter = 0;
}
}
private void incCounter() {
synchronized (mutex) {
counter++;
}
}
private int valCounter() {
int val;
synchronized (mutex) {
val = counter;
}
return val;
}
public void testCheckService() {
assertTrue(RTPBridge.serviceAvailable(getConnection(0)));
}
public void testGetBridge() {
resetCounter();
RTPBridge rtpBridge = RTPBridge.getRTPBridge(getConnection(0), "001");
System.out.println(rtpBridge.getIp() + " portA:" + rtpBridge.getPortA() + " portB:" + rtpBridge.getPortB());
if (rtpBridge != null) {
if (rtpBridge.getIp() != null) incCounter();
if (rtpBridge.getPortA() != -1) incCounter();
if (rtpBridge.getPortB() != -1) incCounter();
}
assertTrue(valCounter() == 3);
}
public void testFullBridge() {
resetCounter();
try {
//XMPPConnection.DEBUG_ENABLED = true;
XMPPConnection x0 = new XMPPConnection("thiago");
XMPPConnection x1 = new XMPPConnection("thiago");
x0.connect();
x0.login("barata7", "barata7");
x1.connect();
x1.login("barata6", "barata6");
final JingleManager jm0 = new JingleManager(
x0, new BridgedResolver(x0));
final JingleManager jm1 = new JingleManager(
x1, new BridgedResolver(x1));
JingleMediaManager jingleMediaManager = new JingleMediaManager() {
// Media Session Implementation
public JingleMediaSession createMediaSession(final PayloadType payloadType, final TransportCandidate remote, final TransportCandidate local) {
return new JingleMediaSession(payloadType, remote, local) {
public void initialize() {
}
public void startTrasmit() {
incCounter();
System.out.print("IPs:");
System.out.println(local.getSymmetric().getIp());
System.out.println(local.getIp());
System.out.println("Transmit");
}
public void startReceive() {
incCounter();
System.out.println("Receive");
}
public void setTrasmit(boolean active) {
}
public void stopTrasmit() {
incCounter();
System.out.println("Stop Transmit");
}
public void stopReceive() {
incCounter();
System.out.println("Stop Receive");
}
};
}
};
jingleMediaManager.addPayloadType(new PayloadType.Audio(3, "GSM", 1, 16000));
jm0.setMediaManager(jingleMediaManager);
jm1.setMediaManager(jingleMediaManager);
jm1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(final JingleSessionRequest request) {
try {
IncomingJingleSession session = request.accept(jm1.getMediaManager().getPayloads());
session.start(request);
} catch (XMPPException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
});
OutgoingJingleSession js0 = jm0.createOutgoingJingleSession("barata6@thiago/Smack");
js0.start();
Thread.sleep(10000);
js0.terminate();
Thread.sleep(3000);
System.out.println(valCounter());
assertTrue(valCounter() == 8);
//Thread.sleep(15000);
} catch (Exception e) {
e.printStackTrace();
}
}
protected int getMaxConnections() {
return 1;
}
}

View file

@ -0,0 +1,342 @@
package org.jivesoftware.smackx.jingle.nat;
import de.javawi.jstun.test.demo.ice.Candidate;
import de.javawi.jstun.test.demo.ice.ICENegociator;
import de.javawi.jstun.util.UtilityException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.jingle.*;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import java.net.UnknownHostException;
import java.util.ArrayList;
/**
* Test the STUN IP resolver.
*
* @author alvaro
*/
public class STUNResolverTest extends SmackTestCase {
// Counter management
public STUNResolverTest(final String arg) {
super(arg);
}
private int counter;
private final Object mutex = new Object();
private void resetCounter() {
synchronized (mutex) {
counter = 0;
}
}
private void incCounter() {
synchronized (mutex) {
counter++;
}
}
private int valCounter() {
int val;
synchronized (mutex) {
val = counter;
}
return val;
}
/**
* Test for getPreferredCandidate()
*
* @throws Exception
*/
public void testGetPreferredCandidate() throws Exception {
int highestPref = 100;
TransportCandidate cand1 = new TransportCandidate.Ice("192.168.2.1", 3, 2,
"password", 3468, "username1", 1);
TransportCandidate cand2 = new TransportCandidate.Ice("192.168.5.1", 2, 10,
"password", 3469, "username2", 15);
TransportCandidate candH = new TransportCandidate.Ice("192.168.2.11", 1, 2,
"password", 3468, "usernameH", highestPref);
TransportCandidate cand3 = new TransportCandidate.Ice("192.168.2.10", 2, 10,
"password", 3469, "username3", 2);
TransportCandidate cand4 = new TransportCandidate.Ice("192.168.4.1", 3, 2,
"password", 3468, "username4", 78);
STUNResolver stunResolver = new STUNResolver() {
};
stunResolver.addCandidate(cand1);
stunResolver.addCandidate(cand2);
stunResolver.addCandidate(candH);
stunResolver.addCandidate(cand3);
stunResolver.addCandidate(cand4);
assertEquals(stunResolver.getPreferredCandidate(), candH);
}
/**
* Test for getPreferredCandidate()
*
* @throws Exception
*/
public void testGetPreferredCandidateICE() throws Exception {
int highestPref = 100;
TransportCandidate cand1 = new TransportCandidate.Ice("192.168.2.1", 3, 2,
"password", 3468, "username1", 1);
TransportCandidate cand2 = new TransportCandidate.Ice("192.168.5.1", 2, 10,
"password", 3469, "username2", 15);
TransportCandidate candH = new TransportCandidate.Ice("192.168.2.11", 1, 2,
"password", 3468, "usernameH", highestPref);
TransportCandidate cand3 = new TransportCandidate.Ice("192.168.2.10", 2, 10,
"password", 3469, "username3", 2);
TransportCandidate cand4 = new TransportCandidate.Ice("192.168.4.1", 3, 2,
"password", 3468, "username4", 78);
ICEResolver iceResolver = new ICEResolver() {
};
iceResolver.addCandidate(cand1);
iceResolver.addCandidate(cand2);
iceResolver.addCandidate(candH);
iceResolver.addCandidate(cand3);
iceResolver.addCandidate(cand4);
assertEquals(iceResolver.getPreferredCandidate(), candH);
}
/**
* Test priority generated by STUN lib
*
* @throws Exception
*/
public void testICEPriority() throws Exception {
String first = "";
for (int i = 0; i < 100; i++) {
ICENegociator cc = new ICENegociator((short) 1);
// gather candidates
cc.gatherCandidateAddresses();
// priorize candidates
cc.prioritizeCandidates();
// get SortedCandidates
//List<Candidate> sortedCandidates = cc.getSortedCandidates();
for (Candidate candidate : cc.getSortedCandidates())
try {
TransportCandidate transportCandidate = new TransportCandidate.Ice(candidate.getAddress().getInetAddress().getHostAddress(), 1, candidate.getNetwork(), "1", candidate.getPort(), "1", candidate.getPriority());
transportCandidate.setLocalIp(candidate.getBase().getAddress().getInetAddress().getHostAddress());
System.out.println("C: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " p:" + candidate.getPriority());
} catch (UtilityException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
Candidate candidate = cc.getSortedCandidates().get(0);
String temp = "C: " + candidate.getAddress().getInetAddress() + "|" + candidate.getBase().getAddress().getInetAddress() + " p:" + candidate.getPriority();
if (first.equals(""))
first = temp;
assertEquals(first, temp);
first = temp;
}
}
/**
* Test for loadSTUNServers()
*
* @throws Exception
*/
public void testLoadSTUNServers() throws Exception {
STUNResolver stunResolver = new STUNResolver() {
};
ArrayList stunServers = stunResolver.loadSTUNServers();
assertTrue(stunServers.size() > 0);
System.out.println(stunServers.size() + " servers loaded");
}
/**
* Test for resolve()
*
* @throws Exception
*/
public void testResolve() throws Exception {
final STUNResolver stunResolver = new STUNResolver() {
};
stunResolver.addListener(new TransportResolverListener.Resolver() {
public void candidateAdded(final TransportCandidate cand) {
incCounter();
String addr = cand.getIp();
int port = cand.getPort();
System.out.println("Addr: " + addr + " port:" + port);
}
public void init() {
System.out.println("Resolution started");
}
public void end() {
System.out.println("Resolution finished");
}
});
try {
stunResolver.initialize();
Thread.sleep(55000);
assertTrue(valCounter() > 0);
stunResolver.resolve();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Generate a list of payload types
*
* @return A testing list
*/
private ArrayList getTestPayloads1() {
ArrayList result = new ArrayList();
result.add(new PayloadType.Audio(34, "supercodec-1", 2, 14000));
result.add(new PayloadType.Audio(56, "supercodec-2", 1, 44000));
result.add(new PayloadType.Audio(36, "supercodec-3", 2, 28000));
result.add(new PayloadType.Audio(45, "supercodec-4", 1, 98000));
return result;
}
private ArrayList getTestPayloads2() {
ArrayList result = new ArrayList();
result.add(new PayloadType.Audio(27, "supercodec-3", 2, 28000));
result.add(new PayloadType.Audio(56, "supercodec-2", 1, 44000));
result.add(new PayloadType.Audio(32, "supercodec-4", 1, 98000));
result.add(new PayloadType.Audio(34, "supercodec-1", 2, 14000));
return result;
}
/**
* This is a simple test where the user_2 rejects the Jingle session.
*/
public void testSTUNJingleSession() {
resetCounter();
try {
TransportResolver tr1 = new STUNResolver() {
};
TransportResolver tr2 = new STUNResolver() {
};
// Explicit resolution
tr1.resolve();
tr2.resolve();
final JingleManager man0 = new JingleManager(getConnection(0), tr1);
final JingleManager man1 = new JingleManager(getConnection(1), tr2);
man1.addJingleSessionRequestListener(new JingleSessionRequestListener() {
/**
* Called when a new session request is detected
*/
public void sessionRequested(final JingleSessionRequest request) {
System.out.println("Session request detected, from "
+ request.getFrom() + ": accepting.");
// We accept the request
IncomingJingleSession session1;
try {
session1 = request.accept(getTestPayloads2());
session1.addListener(new JingleSessionListener() {
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionEstablished(PayloadType pt,
TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) {
incCounter();
System.out
.println("Responder: the session is fully established.");
System.out.println("+ Payload Type: " + pt.getId());
System.out.println("+ Local IP/port: " + lc.getIp() + ":"
+ lc.getPort());
System.out.println("+ Remote IP/port: " + rc.getIp() + ":"
+ rc.getPort());
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
});
session1.start(request);
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
// Session 0 starts a request
System.out.println("Starting session request, to " + getFullJID(1) + "...");
OutgoingJingleSession session0 = man0.createOutgoingJingleSession(
getFullJID(1), getTestPayloads1());
session0.addListener(new JingleSessionListener() {
public void sessionClosed(String reason, JingleSession jingleSession) {
}
public void sessionClosedOnError(XMPPException e, JingleSession jingleSession) {
}
public void sessionDeclined(String reason, JingleSession jingleSession) {
}
public void sessionEstablished(PayloadType pt,
TransportCandidate rc, TransportCandidate lc, JingleSession jingleSession) {
incCounter();
System.out.println("Initiator: the session is fully established.");
System.out.println("+ Payload Type: " + pt.getId());
System.out.println("+ Local IP/port: " + lc.getIp() + ":"
+ lc.getPort());
System.out.println("+ Remote IP/port: " + rc.getIp() + ":"
+ rc.getPort());
}
public void sessionRedirected(String redirection, JingleSession jingleSession) {
}
});
session0.start(null);
Thread.sleep(60000);
assertTrue(valCounter() == 2);
} catch (Exception e) {
e.printStackTrace();
fail("An error occured with Jingle");
}
}
protected int getMaxConnections() {
return 2;
}
}

View file

@ -0,0 +1,59 @@
package org.jivesoftware.smackx.jingle.nat;
import java.util.ArrayList;
import java.util.Collections;
import org.jivesoftware.smack.test.SmackTestCase;
public class TransportCandidateTest extends SmackTestCase {
public TransportCandidateTest(final String arg0) {
super(arg0);
}
/**
* Test for equals()
*/
public void testEqualsObject() {
TransportCandidate cand1 = new TransportCandidate.Ice("192.168.2.1", 1, 2,
"password", 3468, "username", 25);
TransportCandidate cand2 = new TransportCandidate.Ice("192.168.2.1", 1, 2,
"password", 3468, "username", 25);
TransportCandidate cand3 = new TransportCandidate.Ice("192.168.2.1", 1, 2,
"password", 3469, "username", 25);
assertEquals(cand1, cand2);
assertFalse(cand1.equals(cand3));
}
/**
* Test for compareTo()
*/
public void testCompareTo() {
int highestPref = 100;
TransportCandidate cand1 = new TransportCandidate.Ice("192.168.2.1", 3, 2,
"password", 3468, "username", 1);
TransportCandidate cand2 = new TransportCandidate.Ice("192.168.5.1", 2, 10,
"password", 3469, "username", 15);
TransportCandidate candH = new TransportCandidate.Ice("192.168.2.1", 1, 2,
"password", 3468, "username", highestPref);
TransportCandidate cand3 = new TransportCandidate.Ice("192.168.2.10", 2, 10,
"password", 3469, "username", 2);
TransportCandidate cand4 = new TransportCandidate.Ice("192.168.4.1", 3, 2,
"password", 3468, "username", 78);
ArrayList candList = new ArrayList();
candList.add(cand1);
candList.add(cand2);
candList.add(candH);
candList.add(cand3);
candList.add(cand4);
Collections.sort(candList);
assertEquals(candList.get(candList.size() - 1), candH);
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,50 @@
package org.jivesoftware.smackx.jingle.nat;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.test.SmackTestCase;
public class TransportResolverTest extends SmackTestCase {
public TransportResolverTest(final String arg) {
super(arg);
}
public void testIsResolving() {
final TransportResolver tr = new BasicResolver();
tr.addListener(
new TransportResolverListener.Resolver() {
public void candidateAdded(final TransportCandidate cand) {
System.out.println("candidateAdded() called.");
assertTrue(tr.isResolving() || (!tr.isResolving() && tr.isResolved()));
}
public void end() {
System.out.println("end() called.");
assertFalse(tr.isResolving());
assertTrue(tr.isResolved());
}
public void init() {
System.out.println("init() called.");
assertTrue(tr.isResolving());
assertFalse(tr.isResolved());
}
});
assertFalse(tr.isResolving());
assertFalse(tr.isResolved());
try {
tr.resolve();
} catch (XMPPException e) {
e.printStackTrace();
fail("Error resolving");
}
}
protected int getMaxConnections() {
return 0;
}
}

View file

@ -0,0 +1,112 @@
package org.jivesoftware.smackx.provider;
import org.jivesoftware.smack.PacketCollector;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.provider.IQProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.test.SmackTestCase;
import org.jivesoftware.smackx.packet.Jingle;
public class JingleProviderTest extends SmackTestCase {
public JingleProviderTest(final String name) {
super(name);
}
public void testProviderManager() {
IQProvider iqProv;
String elementNamee = Jingle.getElementName();
String nameSpace = Jingle.getNamespace();
System.out.println("Testing if the Jingle IQ provider is registered...");
// Verify that the Jingle IQProvider is registered.
iqProv = (IQProvider)ProviderManager.getInstance().getIQProvider(elementNamee, nameSpace);
assertNotNull(iqProv);
}
/**
* Test for parsing a Jingle
*/
public void testParseIQSimple() {
// Create a dummy packet for testing...
IQfake iqSent = new IQfake (
" <jingle xmlns='http://jabber.org/protocol/jingle'" +
" initiator=\"gorrino@viejo.com\"" +
" responder=\"colico@hepatico.com\"" +
" action=\"transport-info\" sid=\"\">" +
" <transport xmlns='http://jabber.org/protocol/jingle/transport/ice'>" +
" <candidate generation=\"1\"" +
" ip=\"192.168.1.1\"" +
" password=\"secret\"" +
" port=\"8080\"" +
" username=\"username\"" +
" preference=\"1\"/>" +
" </transport>" +
"</jingle>");
iqSent.setTo(getFullJID(0));
iqSent.setFrom(getFullJID(0));
iqSent.setType(IQ.Type.GET);
// Create a filter and a collector...
PacketFilter filter = new PacketTypeFilter(IQ.class);
PacketCollector collector = getConnection(0).createPacketCollector(filter);
System.out.println("Testing if a Jingle IQ can be sent and received...");
// Send the iq packet with an invalid namespace
getConnection(0).sendPacket(iqSent);
// Receive the packet
IQ iqReceived = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
// Stop queuing results
collector.cancel();
if (iqReceived == null) {
fail("No response from server");
}
else if (iqReceived.getType() == IQ.Type.ERROR) {
fail("The server did reply with an error packet: " + iqReceived.getError().getCode());
}
else {
assertTrue(iqReceived instanceof Jingle);
Jingle jin = (Jingle) iqReceived;
System.out.println("Sent: " + iqSent.toXML());
System.out.println("Received: " + jin.toXML());
}
}
/**
* Simple class for testing an IQ...
* @author Alvaro Saurin
*/
private class IQfake extends IQ {
private String s;
public IQfake(final String s) {
super();
this.s = s;
}
public String getChildElementXML() {
StringBuffer buf = new StringBuffer();
buf.append(s);
return buf.toString();
}
}
protected int getMaxConnections() {
return 2;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4" relativePaths="true" type="JAVA_MODULE">
<component name="ModuleRootManager" />
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/../../classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../lib/windows" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../test" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="JingleExtension" />
<orderEntry type="module" module-name="Smack" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/jmf.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/commons-logging-adapters-1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/jspeex-0.9.7-jfcom.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/commons-logging-api-1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/Speex.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$MODULE_DIR$/../lib/commons-logging-1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntryProperties />
</component>
</module>

View file

@ -0,0 +1,157 @@
package org.jivesoftware.demo;
import org.jivesoftware.jingleaudio.jmf.JmfMediaManager;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smackx.jingle.IncomingJingleSession;
import org.jivesoftware.smackx.jingle.JingleManager;
import org.jivesoftware.smackx.jingle.JingleSessionRequest;
import org.jivesoftware.smackx.jingle.OutgoingJingleSession;
import org.jivesoftware.smackx.jingle.listeners.JingleSessionRequestListener;
import org.jivesoftware.smackx.jingle.nat.BridgedTransportManager;
import org.jivesoftware.smackx.jingle.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingle.nat.RTPBridge;
import org.jivesoftware.smackx.jingle.nat.STUNTransportManager;
import javax.swing.*;
import java.awt.event.ActionEvent;
/**
* $RCSfile$
* $Revision: $
* $Date: 28/12/2006
*
* Copyright 2003-2006 Jive Software.
*
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class Demo extends JFrame {
private JingleTransportManager transportManager = null;
private XMPPConnection xmppConnection = null;
private String server = null;
private String user = null;
private String pass = null;
private JingleManager jm = null;
private IncomingJingleSession incoming = null;
private OutgoingJingleSession outgoing = null;
private JTextField jid = new JTextField(30);
public Demo(String server, String user, String pass) {
this.server = server;
this.user = user;
this.pass = pass;
xmppConnection = new XMPPConnection(server);
try {
xmppConnection.connect();
xmppConnection.login(user, pass);
initialize();
} catch (XMPPException e) {
e.printStackTrace();
}
}
public void initialize() {
if (RTPBridge.serviceAvailable(xmppConnection))
transportManager = new BridgedTransportManager(xmppConnection);
else
transportManager = new STUNTransportManager();
jm = new JingleManager(xmppConnection, transportManager, new JmfMediaManager());
if (transportManager instanceof BridgedTransportManager)
jm.addCreationListener((BridgedTransportManager) transportManager);
jm.addJingleSessionRequestListener(new JingleSessionRequestListener() {
public void sessionRequested(JingleSessionRequest request) {
if (incoming != null)
return;
try {
// Accept the call
incoming = request.accept();
// Start the call
incoming.start();
}
catch (XMPPException e) {
e.printStackTrace();
}
}
});
createGUI();
}
public void createGUI() {
JPanel jPanel = new JPanel();
jPanel.add(jid);
jPanel.add(new JButton(new AbstractAction("Call") {
public void actionPerformed(ActionEvent e) {
if (outgoing != null) return;
try {
outgoing = jm.createOutgoingJingleSession(jid.getText());
} catch (XMPPException e1) {
e1.printStackTrace();
}
}
}));
jPanel.add(new JButton(new AbstractAction("Hangup") {
public void actionPerformed(ActionEvent e) {
if (outgoing != null)
try {
outgoing.terminate();
} catch (XMPPException e1) {
e1.printStackTrace();
} finally {
outgoing = null;
}
if (incoming != null)
try {
incoming.terminate();
} catch (XMPPException e1) {
e1.printStackTrace();
} finally {
incoming = null;
}
}
}));
this.add(jPanel);
}
public static void main(String args[]) {
Demo demo = null;
if (args.length > 2) {
demo = new Demo(args[0], args[1], args[2]);
demo.pack();
demo.setVisible(true);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
}

View file

@ -0,0 +1,439 @@
/**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.jingleaudio.jmf;
import javax.media.*;
import javax.media.control.TrackControl;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.PushBufferDataSource;
import javax.media.protocol.PushBufferStream;
import javax.media.rtp.RTPManager;
import javax.media.rtp.SendStream;
import javax.media.rtp.SessionAddress;
import javax.media.rtp.rtcp.SourceDescription;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
/**
* An Easy to use Audio Channel implemented using JMF.
* It sends and receives jmf for and from desired IPs and ports.
* Also has a rport Symetric behavior for better NAT Traversal.
* It send data from a defined port and receive data in the same port, making NAT binds easier.
* <p/>
* Send from portA to portB and receive from portB in portA.
* <p/>
* Sending
* portA ---> portB
* <p/>
* Receiving
* portB ---> portA
* <p/>
* <i>Transmit and Receive are interdependents. To receive you MUST trasmit. </i>
*/
public class AudioChannel {
private MediaLocator locator;
private String localIpAddress;
private String ipAddress;
private int localPort;
private int portBase;
private Format format;
private Processor processor = null;
private RTPManager rtpMgrs[];
private DataSource dataOutput = null;
private AudioReceiver audioReceiver;
private List<SendStream> sendStreams = new ArrayList<SendStream>();
private boolean started = false;
/**
* Creates an Audio Channel for a desired jmf locator. For instance: new MediaLocator("dsound://")
*
* @param locator
* @param ipAddress
* @param localPort
* @param remotePort
* @param format
*/
public AudioChannel(MediaLocator locator,
String localIpAddress,
String ipAddress,
int localPort,
int remotePort,
Format format) {
this.locator = locator;
this.localIpAddress = localIpAddress;
this.ipAddress = ipAddress;
this.localPort = localPort;
this.portBase = remotePort;
this.format = format;
}
/**
* Starts the transmission. Returns null if transmission started ok.
* Otherwise it returns a string with the reason why the setup failed.
* Starts receive also.
*/
public synchronized String start() {
if (started) return null;
started = true;
String result;
// Create a processor for the specified jmf locator
result = createProcessor();
if (result != null) {
started = false;
return result;
}
// Create an RTP session to transmit the output of the
// processor to the specified IP address and port no.
result = createTransmitter();
if (result != null) {
processor.close();
processor = null;
started = false;
return result;
}
// Start the transmission
processor.start();
return null;
}
/**
* Stops the transmission if already started.
* Stops the receiver also.
*/
public void stop() {
if (!started) return;
synchronized (this) {
try {
started = false;
if (processor != null) {
processor.stop();
processor = null;
for (int i = 0; i < rtpMgrs.length; i++) {
rtpMgrs[i].removeReceiveStreamListener(audioReceiver);
rtpMgrs[i].removeSessionListener(audioReceiver);
rtpMgrs[i].removeTargets("Session ended.");
rtpMgrs[i].dispose();
}
sendStreams.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private String createProcessor() {
if (locator == null)
return "Locator is null";
DataSource ds;
try {
ds = javax.media.Manager.createDataSource(locator);
} catch (Exception e) {
e.printStackTrace();
return "Couldn't create DataSource";
}
// Try to create a processor to handle the input jmf locator
try {
processor = javax.media.Manager.createProcessor(ds);
} catch (NoProcessorException npe) {
npe.printStackTrace();
return "Couldn't create processor";
} catch (IOException ioe) {
ioe.printStackTrace();
return "IOException creating processor";
}
// Wait for it to configure
boolean result = waitForState(processor, Processor.Configured);
if (result == false)
return "Couldn't configure processor";
// Get the tracks from the processor
TrackControl[] tracks = processor.getTrackControls();
// Do we have atleast one track?
if (tracks == null || tracks.length < 1)
return "Couldn't find tracks in processor";
// Set the output content descriptor to RAW_RTP
// This will limit the supported formats reported from
// Track.getSupportedFormats to only valid RTP formats.
ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP);
processor.setContentDescriptor(cd);
Format supported[];
Format chosen = null;
boolean atLeastOneTrack = false;
// Program the tracks.
for (int i = 0; i < tracks.length; i++) {
if (tracks[i].isEnabled()) {
supported = tracks[i].getSupportedFormats();
if (supported.length > 0) {
for (Format format : supported) {
if (format instanceof AudioFormat) {
if (this.format.matches(format))
chosen = format;
}
}
if (chosen != null) {
tracks[i].setFormat(chosen);
System.err.println("Track " + i + " is set to transmit as:");
System.err.println(" " + chosen);
atLeastOneTrack = true;
} else
tracks[i].setEnabled(false);
} else
tracks[i].setEnabled(false);
}
}
if (!atLeastOneTrack)
return "Couldn't set any of the tracks to a valid RTP format";
result = waitForState(processor, Controller.Realized);
if (result == false)
return "Couldn't realize processor";
// Get the output data source of the processor
dataOutput = processor.getDataOutput();
return null;
}
/**
* Use the RTPManager API to create sessions for each jmf
* track of the processor.
*/
private String createTransmitter() {
// Cheated. Should have checked the type.
PushBufferDataSource pbds = (PushBufferDataSource) dataOutput;
PushBufferStream pbss[] = pbds.getStreams();
rtpMgrs = new RTPManager[pbss.length];
SessionAddress localAddr, destAddr;
InetAddress ipAddr;
SendStream sendStream;
audioReceiver = new AudioReceiver(this);
int port;
SourceDescription srcDesList[];
for (int i = 0; i < pbss.length; i++) {
try {
rtpMgrs[i] = RTPManager.newInstance();
port = portBase + 2 * i;
ipAddr = InetAddress.getByName(ipAddress);
localAddr = new SessionAddress(InetAddress.getByName(this.localIpAddress),
localPort);
destAddr = new SessionAddress(ipAddr, port);
rtpMgrs[i].addReceiveStreamListener(audioReceiver);
rtpMgrs[i].addSessionListener(audioReceiver);
rtpMgrs[i].initialize(localAddr);
rtpMgrs[i].addTarget(destAddr);
System.err.println("Created RTP session at " + localPort + " to: " + ipAddress + " " + port);
sendStream = rtpMgrs[i].createSendStream(dataOutput, i);
sendStreams.add(sendStream);
sendStream.start();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
return null;
}
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
*
* @param active
*/
public void setTrasmit(boolean active) {
for (SendStream sendStream : sendStreams) {
try {
if (active) {
sendStream.start();
System.out.println("START");
} else {
sendStream.stop();
System.out.println("STOP");
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* *************************************************************
* Convenience methods to handle processor's state changes.
* **************************************************************
*/
private Integer stateLock = new Integer(0);
private boolean failed = false;
Integer getStateLock() {
return stateLock;
}
void setFailed() {
failed = true;
}
private synchronized boolean waitForState(Processor p, int state) {
p.addControllerListener(new StateListener());
failed = false;
// Call the required method on the processor
if (state == Processor.Configured) {
p.configure();
} else if (state == Processor.Realized) {
p.realize();
}
// Wait until we get an event that confirms the
// success of the method, or a failure event.
// See StateListener inner class
while (p.getState() < state && !failed) {
synchronized (getStateLock()) {
try {
getStateLock().wait();
} catch (InterruptedException ie) {
return false;
}
}
}
if (failed)
return false;
else
return true;
}
/**
* *************************************************************
* Inner Classes
* **************************************************************
*/
class StateListener implements ControllerListener {
public void controllerUpdate(ControllerEvent ce) {
// If there was an error during configure or
// realize, the processor will be closed
if (ce instanceof ControllerClosedEvent)
setFailed();
// All controller events, send a notification
// to the waiting thread in waitForState method.
if (ce instanceof ControllerEvent) {
synchronized (getStateLock()) {
getStateLock().notifyAll();
}
}
}
}
public static void main(String args[]) {
InetAddress localhost;
try {
localhost = InetAddress.getLocalHost();
AudioChannel audioChannel0 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7002, 7020, new AudioFormat(AudioFormat.GSM_RTP));
AudioChannel audioChannel1 = new AudioChannel(new MediaLocator("javasound://8000"), localhost.getHostAddress(), localhost.getHostAddress(), 7020, 7002, new AudioFormat(AudioFormat.GSM_RTP));
audioChannel0.start();
audioChannel1.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.setTrasmit(false);
audioChannel1.setTrasmit(false);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.setTrasmit(true);
audioChannel1.setTrasmit(true);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioChannel0.stop();
audioChannel1.stop();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,51 @@
/**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.jingleaudio.jmf;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import javax.media.format.AudioFormat;
/**
* Audio Format Ttils.
*/
public class AudioFormatUtils {
/**
* Return a JMF AudioFormat for a given Jingle Payload type.
* Return null if the payload is not supported by this jmf API.
*
* @param payloadtype
* @return
*/
public static AudioFormat getAudioFormat(PayloadType payloadtype) {
switch (payloadtype.getId()) {
case 3:
return new AudioFormat(AudioFormat.GSM_RTP);
case 4:
return new AudioFormat(AudioFormat.G723_RTP);
default:
return null;
}
}
}

View file

@ -0,0 +1,155 @@
package org.jivesoftware.jingleaudio.jmf; /**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.jivesoftware.jingleaudio.jmf.AudioChannel;
import org.jivesoftware.jingleaudio.jmf.AudioFormatUtils;
import org.jivesoftware.smackx.jingle.media.JingleMediaSession;
import org.jivesoftware.smackx.jingle.media.PayloadType;
import org.jivesoftware.smackx.jingle.nat.TransportCandidate;
import javax.media.MediaLocator;
import javax.media.format.AudioFormat;
import java.io.IOException;
import java.net.ServerSocket;
/**
* This Class implements a complete JingleMediaSession.
* It sould be used to transmit and receive audio captured from the Mic.
* This Class should be automaticly controlled by JingleSession.
* But you could also use in any VOIP application.
* For better NAT Traversal support this implementation don´t support only receive or only transmit.
* To receive you MUST transmit. So the only implemented and functionally methods are startTransmit() and stopTransmit()
*/
public class AudioMediaSession extends JingleMediaSession {
private AudioFormat format;
private AudioChannel audioChannel;
/**
* Creates a org.jivesoftware.jingleaudio.jmf.AudioMediaSession with defined payload type, remote and local candidates
*
* @param payloadType Payload of the jmf
* @param remote The remote information. The candidate that the jmf will be sent to.
* @param local The local information. The candidate that will receive the jmf
*/
public AudioMediaSession(final PayloadType payloadType, final TransportCandidate remote,
final TransportCandidate local) {
super(payloadType, remote, local);
}
/**
* Initialize the Audio Channel to make it able to send and receive audio
*/
public void initialize() {
String ip;
String localIp;
int localPort;
int remotePort;
if (this.getLocal().getSymmetric() != null) {
ip = this.getLocal().getIp();
localIp = this.getLocal().getLocalIp();
localPort = getFreePort();
remotePort = this.getLocal().getSymmetric().getPort();
System.out.println(this.getLocal().getConnection() + " " + ip + ": " + localPort + "->" + remotePort);
} else {
ip = this.getRemote().getIp();
localIp = this.getLocal().getLocalIp();
localPort = this.getLocal().getPort();
remotePort = this.getRemote().getPort();
}
audioChannel = new AudioChannel(new MediaLocator("dsound://"), localIp, ip, localPort, remotePort, AudioFormatUtils.getAudioFormat(this.getPayloadType()));
}
/**
* Starts transmission and for NAT Traversal reasons start receiving also.
*/
public void startTrasmit() {
audioChannel.start();
}
/**
* Set transmit activity. If the active is true, the instance should trasmit.
* If it is set to false, the instance should pause transmit.
*
* @param active
*/
public void setTrasmit(boolean active) {
audioChannel.setTrasmit(active);
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void startReceive() {
// Do nothing
}
/**
* Stops transmission and for NAT Traversal reasons stop receiving also.
*/
public void stopTrasmit() {
audioChannel.stop();
}
/**
* For NAT Reasons this method does nothing. Use startTransmit() to start transmit and receive jmf
*/
public void stopReceive() {
// Do nothing
}
/**
* Obtain a free port we can use.
*
* @return A free port number.
*/
protected int getFreePort() {
ServerSocket ss;
int freePort = 0;
for (int i = 0; i < 10; i++) {
freePort = (int) (10000 + Math.round(Math.random() * 10000));
freePort = freePort % 2 == 0 ? freePort : freePort + 1;
try {
ss = new ServerSocket(freePort);
freePort = ss.getLocalPort();
ss.close();
return freePort;
}
catch (IOException e) {
e.printStackTrace();
}
}
try {
ss = new ServerSocket(0);
freePort = ss.getLocalPort();
ss.close();
}
catch (IOException e) {
e.printStackTrace();
}
return freePort;
}
}

View file

@ -0,0 +1,143 @@
package org.jivesoftware.jingleaudio.jmf; /**
* $RCSfile$
* $Revision: $
* $Date: 08/11/2006
* <p/>
* Copyright 2003-2006 Jive Software.
* <p/>
* All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.media.*;
import javax.media.protocol.DataSource;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
/**
* This class implements receive methods and listeners to be used in AudioChannel
*/
public class AudioReceiver implements ReceiveStreamListener, SessionListener,
ControllerListener {
boolean dataReceived = false;
Object dataSync;
public AudioReceiver(Object dataSync) {
this.dataSync = dataSync;
}
/**
* JingleSessionListener.
*/
public synchronized void update(SessionEvent evt) {
if (evt instanceof NewParticipantEvent) {
Participant p = ((NewParticipantEvent) evt).getParticipant();
System.err.println(" - A new participant had just joined: " + p.getCNAME());
}
}
/**
* ReceiveStreamListener
*/
public synchronized void update(ReceiveStreamEvent evt) {
RTPManager mgr = (RTPManager) evt.getSource();
Participant participant = evt.getParticipant(); // could be null.
ReceiveStream stream = evt.getReceiveStream(); // could be null.
if (evt instanceof RemotePayloadChangeEvent) {
System.err.println(" - Received an RTP PayloadChangeEvent.");
System.err.println("Sorry, cannot handle payload change.");
// System.exit(0);
} else if (evt instanceof NewReceiveStreamEvent) {
try {
stream = ((NewReceiveStreamEvent) evt).getReceiveStream();
DataSource ds = stream.getDataSource();
// Find out the formats.
RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
if (ctl != null) {
System.err.println(" - Recevied new RTP stream: " + ctl.getFormat());
} else
System.err.println(" - Recevied new RTP stream");
if (participant == null)
System.err.println(" The sender of this stream had yet to be identified.");
else {
System.err.println(" The stream comes from: " + participant.getCNAME());
}
// create a player by passing datasource to the Media Manager
Player p = javax.media.Manager.createPlayer(ds);
if (p == null)
return;
p.addControllerListener(this);
p.realize();
// Notify intialize() that a new stream had arrived.
synchronized (dataSync) {
dataReceived = true;
dataSync.notifyAll();
}
} catch (Exception e) {
System.err.println("NewReceiveStreamEvent exception " + e.getMessage());
return;
}
} else if (evt instanceof StreamMappedEvent) {
if (stream != null && stream.getDataSource() != null) {
DataSource ds = stream.getDataSource();
// Find out the formats.
RTPControl ctl = (RTPControl) ds.getControl("javax.jmf.rtp.RTPControl");
System.err.println(" - The previously unidentified stream ");
if (ctl != null)
System.err.println(" " + ctl.getFormat());
System.err.println(" had now been identified as sent by: " + participant.getCNAME());
}
} else if (evt instanceof ByeEvent) {
System.err.println(" - Got \"bye\" from: " + participant.getCNAME());
}
}
/**
* ControllerListener for the Players.
*/
public synchronized void controllerUpdate(ControllerEvent ce) {
Player p = (Player) ce.getSourceController();
if (p == null)
return;
// Get this when the internal players are realized.
if (ce instanceof RealizeCompleteEvent) {
p.start();
}
if (ce instanceof ControllerErrorEvent) {
p.removeControllerListener(this);
System.err.println("Receiver internal error: " + ce);
}
}
}

Some files were not shown because too many files have changed in this diff Show more