diff --git a/lib/installer-exclude/JainSipApi1.2.jar b/lib/installer-exclude/JainSipApi1.2.jar
index 05f3b4286..7e1126f5f 100644
Binary files a/lib/installer-exclude/JainSipApi1.2.jar and b/lib/installer-exclude/JainSipApi1.2.jar differ
diff --git a/lib/installer-exclude/JainSipRi1.2.jar b/lib/installer-exclude/JainSipRi1.2.jar
index 27be2bc3c..48d84fe0a 100644
Binary files a/lib/installer-exclude/JainSipRi1.2.jar and b/lib/installer-exclude/JainSipRi1.2.jar differ
diff --git a/lib/installer-exclude/nist-sdp-1.0.jar b/lib/installer-exclude/nist-sdp-1.0.jar
index 7a8b82f70..e264639b9 100644
Binary files a/lib/installer-exclude/nist-sdp-1.0.jar and b/lib/installer-exclude/nist-sdp-1.0.jar differ
diff --git a/lib/logging.properties b/lib/logging.properties
index 85b86d01e..3a5b31b7f 100644
--- a/lib/logging.properties
+++ b/lib/logging.properties
@@ -27,7 +27,7 @@ handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
-.level= INFO
+.level= WARN
############################################################
# Handler specific properties.
@@ -39,13 +39,13 @@ java.util.logging.FileHandler.pattern = ./log/sip-communicator%u.log
java.util.logging.FileHandler.limit = 5000000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = net.java.sip.communicator.util.ScLogFormatter
-java.util.logging.FileHandler.level = FINEST
+java.util.logging.FileHandler.level = INFO
# Limit the message that are printed on the console to FINEST and above (all).
-java.util.logging.ConsoleHandler.level = FINEST
+java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index 74b4c95d8..6c12c15b1 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -663,14 +663,14 @@ gtalkRegisterNewAccountText=In case you don't have a Google Talk account, click
# key binding chooser
keybindings=Keybindings
-chat-nextTab=Next Tab
-chat-previousTab=Previous Tab
+chat-nextTab=Next Chat Tab
+chat-previousTab=Previous Chat Tab
chat-copy=Copy
chat-paste=Paste
-chat-openSmilies=Open Smilies
-chat-openHistory=Open History
-chat-close=Close
+chat-openSmilies=Show Smilies
+chat-openHistory=Show History
+chat-close=Close Chat Window
chat-cut=Cut
main-rename=Rename
-main-nextTab=Next Tab
-main-previousTab=Previous Tab
\ No newline at end of file
+main-nextTab=Next Contact List Tab
+main-previousTab=Previous Contact List Tab
diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java
index eb3924318..3eef5e916 100644
--- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java
+++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java
@@ -55,9 +55,6 @@
* bye the remote party and we only need to configure our processor and
* RTPManagers.
*
- * @todo implement SendStreamListener.
- * @todo implement ReceiveStreamListener.
- *
* @author Emil Ivov
* @author Ryan Ricard
* @author Ken Larson
@@ -461,7 +458,7 @@ private void printFlowStatistics(RTPManager rtpManager)
//print flow statistics.
GlobalTransmissionStats s = rtpManager.getGlobalTransmissionStats();
- logger.info(
+ logger.debug(
"global transmission stats (" + rtpManagerDescription + "): \n" +
"bytes sent: " + s.getBytesSent() + "\n" +
"local colls: " + s.getLocalColls() + "\n" +
@@ -473,7 +470,7 @@ private void printFlowStatistics(RTPManager rtpManager)
GlobalReceptionStats rs = rtpManager.getGlobalReceptionStats();
- logger.info(
+ logger.debug(
"global reception stats (" + rtpManagerDescription + "): \n" +
"bad RTCP packets: " + rs.getBadRTCPPkts() + "\n" +
"bad RTP packets: " + rs.getBadRTPkts() + "\n" +
@@ -1931,6 +1928,7 @@ public void callStateChanged(CallChangeEvent evt)
{
try
{
+ logger.debug("call connected. starting streaming");
startStreaming();
mediaServCallback.getMediaControl(getCall())
.startProcessingMedia(this);
@@ -1941,9 +1939,10 @@ public void callStateChanged(CallChangeEvent evt)
logger.error("Failed to start streaming.", ex);
}
}
- else if( evt.getNewValue() == CallState.CALL_ENDED
+ else if( evt.getNewValue() == CallState.CALL_ENDED
&& evt.getNewValue() != evt.getOldValue())
{
+ logger.warn("Stopping streaming.");
stopStreaming();
mediaServCallback.getMediaControl(getCall())
.stopProcessingMedia(this);
@@ -1960,7 +1959,6 @@ else if( evt.getNewValue() == CallState.CALL_ENDED
playersIter.remove();
}
-
//close all video frames that we have created in this session
Iterator videoFramesIter = videoFrames.iterator();
while(videoFramesIter.hasNext())
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
index 951e32c88..9a2e35418 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
@@ -190,17 +190,15 @@ public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
*/
public void participantStateChanged(CallParticipantChangeEvent evt)
{
- if(((CallParticipantState)evt.getNewValue())
- == CallParticipantState.DISCONNECTED
- || ((CallParticipantState)evt.getNewValue())
- == CallParticipantState.FAILED)
+ CallParticipantState newState = (CallParticipantState)evt.getNewValue();
+ if(newState == CallParticipantState.DISCONNECTED
+ || newState == CallParticipantState.FAILED)
{
removeCallParticipant(
(CallParticipantSipImpl)evt.getSourceCallParticipant());
}
- else if (((CallParticipantState)evt.getNewValue())
- == CallParticipantState.CONNECTED
- && getCallState().equals(CallState.CALL_INITIALIZATION))
+ else if ((newState == CallParticipantState.CONNECTED
+ || newState == CallParticipantState.CONNECTING_WITH_EARLY_MEDIA))
{
setCallState(CallState.CALL_IN_PROGRESS);
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index 9dd24ba27..6eca61e99 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -679,6 +679,11 @@ else if (response.getStatusCode() == Response.RINGING)
{
processRinging(clientTransaction, response);
}
+ //Session Progress
+ if (response.getStatusCode() == Response.SESSION_PROGRESS)
+ {
+ processSessionProgress(clientTransaction, response);
+ }
//Trying
else if (response.getStatusCode() == Response.TRYING)
{
@@ -787,6 +792,111 @@ private void processRinging(ClientTransaction clientTransaction,
callParticipant.setState(CallParticipantState.ALERTING_REMOTE_SIDE);
}
+ /**
+ * Handles early media in 183 Session Progress responses. Retrieves the SDP
+ * and makes sure that we start transmitting and playing early media that
+ * we receive. Puts the call into a CONNECTING_WITH_EARLY_MEDIA state.
+ *
+ * @param clientTransaction the ClientTransaction that the response
+ * arrived in.
+ * @param sessionProgress the 183 Response to process
+ */
+ private void processSessionProgress(ClientTransaction clientTransaction,
+ Response sessionProgress)
+ {
+
+ Dialog dialog = clientTransaction.getDialog();
+ //find the call
+ CallParticipantSipImpl callParticipant
+ = activeCallsRepository.findCallParticipant(dialog);
+
+ if (callParticipant.getState()
+ == CallParticipantState.CONNECTING_WITH_EARLY_MEDIA)
+ {
+ // This can happen if we are receigin early media for a second time.
+ logger.warn("Ignoring invite 183 since call participant is "
+ +"already exchanging early media.");
+ return;
+ }
+
+ if (sessionProgress.getContentLength().getContentLength() == 0)
+ {
+ logger.warn("Ignoring a 183 with no content");
+ return;
+ }
+
+ ContentTypeHeader contentTypeHeader = (ContentTypeHeader)
+ sessionProgress.getHeader(ContentTypeHeader.NAME);
+
+ if(!contentTypeHeader.getContentType().equalsIgnoreCase("application")
+ || !contentTypeHeader.getContentSubType().equalsIgnoreCase("sdp"))
+ {
+ // This can happen if we are receigin early media for a second time.
+ logger.warn("Ignoring invite 183 since call participant is "
+ +"already exchanging early media.");
+ return;
+ }
+
+ //set sdp content before setting call state as that is where
+ //listeners get alerted and they need the sdp
+ callParticipant.setSdpDescription(
+ new String(sessionProgress.getRawContent()));
+
+ //notify the media manager of the sdp content
+ CallSession callSession
+ = ((CallSipImpl)callParticipant.getCall()).getMediaCallSession();
+
+ if(callSession == null)
+ {
+ //unlikely to happen because it would mean we didn't send an offer
+ //in the invite and we always send one.
+ logger.warn("Could not find call session.");
+ return;
+ }
+
+ try
+ {
+ callSession.processSdpAnswer(
+ callParticipant,
+ new String(sessionProgress.getRawContent()));
+ }
+ catch (ParseException exc)
+ {
+ logger.error("There was an error parsing the SDP description of "
+ + callParticipant.getDisplayName()
+ + "(" + callParticipant.getAddress() + ")"
+ , exc);
+ callParticipant.setState(CallParticipantState.FAILED
+ , "There was an error parsing the SDP description of "
+ + callParticipant.getDisplayName()
+ + "(" + callParticipant.getAddress() + ")");
+ }
+ catch (MediaException exc)
+ {
+ logger.error("We failed to process the SDP description of "
+ + callParticipant.getDisplayName()
+ + "(" + callParticipant.getAddress() + ")"
+ + ". Error was: "
+ + exc.getMessage()
+ , exc);
+ callParticipant.setState(CallParticipantState.FAILED
+ , "We failed to process the SDP description of "
+ + callParticipant.getDisplayName()
+ + "(" + callParticipant.getAddress() + ")"
+ + ". Error was: "
+ + exc.getMessage());
+ }
+
+ //set the call url in case there was one
+ /** @todo this should be done in CallSession, once we move
+ * it here.*/
+ callParticipant.setCallInfoURL(callSession.getCallInfoURL());
+
+ //change status
+ callParticipant.setState(
+ CallParticipantState.CONNECTING_WITH_EARLY_MEDIA);
+ }
+
/**
* Sets to CONNECTED that state of the corresponding call participant and
* sends an ACK.
@@ -1794,9 +1904,12 @@ public synchronized void hangupCallParticipant(CallParticipant participant)
sayBye(callParticipant);
callParticipant.setState(CallParticipantState.DISCONNECTED);
}
- else if (participantState.equals(CallParticipantState.CONNECTING)
- || participantState
- .equals(CallParticipantState.ALERTING_REMOTE_SIDE))
+ else if (callParticipant.getState()
+ .equals(CallParticipantState.CONNECTING)
+ || callParticipant.getState()
+ .equals(CallParticipantState.CONNECTING_WITH_EARLY_MEDIA)
+ || callParticipant.getState()
+ .equals(CallParticipantState.ALERTING_REMOTE_SIDE))
{
if (callParticipant.getFirstTransaction() != null)
{
@@ -1935,6 +2048,28 @@ private void sayBye(CallParticipantSipImpl callParticipant)
try
{
bye = callParticipant.getDialog().createRequest(Request.BYE);
+
+ //we have to set the via headers our selves because otherwise
+ //jain sip would send them with a 0.0.0.0 address
+ InetAddress destinationInetAddress = null;
+ String host = ( (SipURI) bye.getRequestURI()).getHost();
+ try
+ {
+ destinationInetAddress = InetAddress.getByName(host);
+ }
+ catch (UnknownHostException ex)
+ {
+ throw new IllegalArgumentException(
+ host + " is not a valid internet address "
+ + ex.getMessage());
+ }
+
+ ArrayList viaHeaders= protocolProvider.getLocalViaHeaders(
+ destinationInetAddress,
+ protocolProvider.getRegistrarConnection()
+ .getRegistrarListeningPoint());
+ bye.setHeader((ViaHeader)viaHeaders.get(0));
+ bye.addHeader(protocolProvider.getSipCommUserAgentHeader());
}
catch (SipException ex)
{
diff --git a/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java b/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java
index f961310ae..0df1d1396 100644
--- a/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java
+++ b/src/net/java/sip/communicator/service/keybindings/KeybindingSet.java
@@ -24,7 +24,7 @@ public abstract class KeybindingSet
* @return mapping of keystrokes to the string representation of the actions
* they perform
*/
- public abstract LinkedHashMap getBindings();
+ public abstract HashMap getBindings();
/**
* Resets the bindings and notifies the observer's listeners if they've
diff --git a/src/net/java/sip/communicator/service/protocol/CallParticipantState.java b/src/net/java/sip/communicator/service/protocol/CallParticipantState.java
index d749a7b99..7971908a0 100644
--- a/src/net/java/sip/communicator/service/protocol/CallParticipantState.java
+++ b/src/net/java/sip/communicator/service/protocol/CallParticipantState.java
@@ -85,6 +85,21 @@ public class CallParticipantState
*/
public static final CallParticipantState CONNECTING =
new CallParticipantState(_CONNECTING);
+
+ /**
+ * This constant value indicates a String representation of the CONNECTING
+ * call state but in cases where early media is being exchanged.
+ *
This constant has the String value "Connecting".
+ */
+ public static final String _CONNECTING_WITH_EARLY_MEDIA = "Connecting*";
+
+ /**
+ * This constant value indicates that the state of the call participant is
+ * is CONNECTING - which means that a network connection to that participant
+ * is currently being established.
+ */
+ public static final CallParticipantState CONNECTING_WITH_EARLY_MEDIA =
+ new CallParticipantState( _CONNECTING_WITH_EARLY_MEDIA );
/**
* This constant value indicates a String representation of the
diff --git a/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java b/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java
index 159e5dc17..0dac4f887 100644
--- a/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java
+++ b/src/net/java/sip/communicator/service/protocol/jabberconstants/JabberStatusEnum.java
@@ -156,6 +156,10 @@ private JabberStatusEnum(String iconPath)
this.doNotDisturbStatus =
new JabberPresenceStatus(30, DO_NOT_DISTURB, loadIcon(iconPath
+ "/status16x16-dnd.png"));
+
+ this.extendedAwayStatus =
+ new JabberPresenceStatus(35, EXTENDED_AWAY, loadIcon(iconPath
+ + "/status16x16-xa.png"));
this.awayStatus =
new JabberPresenceStatus(40, AWAY, loadIcon(iconPath
@@ -168,10 +172,6 @@ private JabberStatusEnum(String iconPath)
this.freeForChatStatus =
new JabberPresenceStatus(85, FREE_FOR_CHAT, loadIcon(iconPath
+ "/status16x16-ffc.png"));
-
- this.extendedAwayStatus =
- new JabberPresenceStatus(85, EXTENDED_AWAY, loadIcon(iconPath
- + "/status16x16-xa.png"));
this.unknownStatus =
new JabberPresenceStatus(1, UNKNOWN, loadIcon(iconPath