[sip-comm-cvs] CVS update: /sip-communicator/src/net/java/sip/communicator/sip/simple/, /sip-communicator/src/net/java/sip/communicator/sip/

mandre at dev.java.net mandre at dev.java.net
Mon May 10 23:16:51 CEST 2004


User: mandre  
Date: 04/05/10 14:16:51

Modified:
 /sip-communicator/src/net/java/sip/communicator/sip/simple/
  PresenceAgent.java, Subscription.java, Watcher.java
 /sip-communicator/src/net/java/sip/communicator/sip/
  SipManager.java

Log:
 Ongoing SIMPLE work
 Avoid using SIP stack dialogs with SUBSCRIBE requests

File Changes:

Directory: /sip-communicator/src/net/java/sip/communicator/sip/simple/
======================================================================

File [changed]: PresenceAgent.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/src/net/java/sip/communicator/sip/simple/PresenceAgent.java?r1=1.10&r2=1.11
Delta lines:  +59 -46
---------------------
--- PresenceAgent.java	25 Apr 2004 14:54:47 -0000	1.10
+++ PresenceAgent.java	10 May 2004 21:16:47 -0000	1.11
@@ -65,6 +65,7 @@
 
 import javax.sip.ClientTransaction;
 import javax.sip.Dialog;
+import javax.sip.InvalidArgumentException;
 import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.TransactionUnavailableException;
@@ -305,6 +306,7 @@
                             subscriber,
                             presentity,
                             transaction.getDialog(),
+                            null,
                             Subscription.INCOMING_SUBSCRIPTION,
                             Subscription.PENDING,
                             null, //a null SubscriptionAuthorizationResponse would
@@ -313,7 +315,7 @@
                             preferredContentType,
                             this);
                         incSubscriptions.add(subscription);
-                        scheduler.schedule(subscription, expires);
+                        scheduler.schedule(subscription, new Date(System.currentTimeMillis() + expires));
                         if(console.isDebugEnabled())
                             console.debug("Adding " + subscriber + "/" + presentity
                                           + " to the subscription list");
@@ -400,10 +402,9 @@
      * @param presentity the user from whom the subscriber's subscription is to be removed.
      * @param reasonCode the reason code for the removal of the subscriber's subscription. Its value may be a null.
      * @throws CommunicationsException
-     * @throws java.lang.IllegalArgumentException
      */
     public void removeSubscription(String subscriber, String presentity, java.lang.String reasonCode)
-        throws CommunicationsException, java.lang.IllegalArgumentException
+        throws CommunicationsException
     {
 //	   1) Sends a NOTIFY message to the subscriber whose subscription was removed.
 //	       The NOTIFY message has a "Subscription-State" header with value "terminated".
@@ -450,7 +451,9 @@
 
                 //At least, remove the subscription
                 incSubscriptions.remove(rank);
-                System.out.println("Removing "+subscriber+"/"+presentity+" from subscription list");
+                if(console.isDebugEnabled())
+                            console.debug("Removing "+subscriber+"/"+presentity
+                                          + " from subscription list");
             }
         } finally {
             console.logExit();
@@ -499,8 +502,7 @@
             console.logEntry();
 
             String response = null;
-            try {
-    //			Transaction
+//			Transaction
                 ClientTransaction notTrans = null;
                 try {
                     ContentTypeHeader contentTypeHeader = sipManCallback.headerFactory.createContentTypeHeader("application", presenceFormat);
@@ -510,18 +512,32 @@
                     EventHeader eventHeader = sipManCallback.headerFactory.createEventHeader("presence");
 
                     //Increment CSeq
-                    CSeqHeader cseqHeader = ((CSeqHeader)notification.getHeader(CSeqHeader.NAME));
-                    int cseq =  cseqHeader.getSequenceNumber() + 1;
-                    cseqHeader.setSequenceNumber(cseq);
+                //CSeqHeader cseqHeader = ((CSeqHeader)notification.getHeader(CSeqHeader.NAME));
+                //int cseq =  dialog.getLocalSequenceNumber() + 1;
+                //cseqHeader.setSequenceNumber(cseq);
 
                     //UserAgentHeader userAgentHeader = sipManCallback.headerFactory.createUserAgentHeader("SIP-Communicator");
                     notification.setContent(presenceDocument, contentTypeHeader);
                     notification.setContentLength(contentLengthHeader);
                     notification.setHeader(eventHeader);
-                    notification.setHeader(cseqHeader);
+                //notification.setHeader(cseqHeader);
 
                     notTrans = sipManCallback.sipProvider.getNewClientTransaction(notification);
                 }
+            catch (ParseException ex) {
+                ex.printStackTrace();
+                console.error("Header must not contain a null value!",
+                            ex);
+                throw new CommunicationsException(
+                        "Header must not contain a null value!");
+            }
+            catch (InvalidArgumentException ex) {
+                ex.printStackTrace();
+                console.error("Content Length Header must contain an integer value!",
+                            ex);
+                throw new CommunicationsException(
+                        "Content Length Header must contain an integer value!");
+            }
                 catch (TransactionUnavailableException ex) {
                     ex.printStackTrace();
                     console.error("Could not create notify message!",
@@ -530,6 +546,7 @@
                     throw new CommunicationsException(
                             "Could not create notify message!");
                 }
+                
                 try {
                     dialog.sendRequest(notTrans);
                     //notTrans.sendRequest();
@@ -542,10 +559,6 @@
                     //throw was missing - reported by Eero Vaarnas
                     throw new CommunicationsException(
                         "Could not send out the notify request!", ex);
-                }
-            }
-            catch (Exception e) {
-                // TODO: handle exception
             }
             return response;
         } finally {

File [changed]: Subscription.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/src/net/java/sip/communicator/sip/simple/Subscription.java?r1=1.4&r2=1.5
Delta lines:  +109 -34
----------------------
--- Subscription.java	13 Apr 2004 12:41:24 -0000	1.4
+++ Subscription.java	10 May 2004 21:16:48 -0000	1.5
@@ -57,13 +57,14 @@
  */
 package net.java.sip.communicator.sip.simple;
 
-//import javax.sip.Dialog;
 import javax.sip.Dialog;
+import javax.sip.InvalidArgumentException;
 import javax.sip.header.CSeqHeader;
 import javax.sip.message.Request;
 
 import net.java.sip.communicator.common.Console;
 import net.java.sip.communicator.common.SchedulerTask;
+import net.java.sip.communicator.sip.CommunicationsException;
 
 /**
  * @author Martin Andre
@@ -73,8 +74,8 @@
 {
     //Date expires;
     private long delay;
-    private Request request;
     private Dialog dialog;
+    private Request request;
     private String subscriptionType;	/* Incoming or Outgoing */
     private String subscriber;
     private String presentity;
@@ -96,6 +97,7 @@
         String subscriber,
         String presentity,
         Dialog dialog,
+        Request request,
         String subscriptionType,
         String status,
         SubscriptionAuthorizationResponse authorizationResponse,
@@ -105,6 +107,7 @@
         this.subscriber = subscriber;
         this.presentity = presentity;
         this.dialog = dialog;
+        this.request = request;
         this.subscriptionType = subscriptionType;
         this.status = status;
         this.authorizationResponse = authorizationResponse;
@@ -124,80 +127,152 @@
         {
             console.logEntry();
 
-            try
-            {
                 //PA, remove from subscriptionList
-                if (subscriptionType.equalsIgnoreCase(INCOMING_SUBSCRIPTION))
-                {
+            if (subscriptionType.equalsIgnoreCase(INCOMING_SUBSCRIPTION)) {
                     ((PresenceAgent) callback).removeFromList(this);
+                if(console.isDebugEnabled())
+                        console.debug("Processing INCOMING_SUBSCRIPTION trigger");
                 }
 
                 //Watcher, resend a subscribe msg
-                else if  (subscriptionType.equalsIgnoreCase(OUTGOING_SUBSCRIPTION))
-                {
+            else if  (subscriptionType.equalsIgnoreCase(OUTGOING_SUBSCRIPTION)) {
+                try {
+                    request = (Request)request.clone();
+                    
+                    try {
+                        //Increment CSeq
                     CSeqHeader cseqHeader = ((CSeqHeader)request.getHeader(CSeqHeader.NAME));
-                    int cseq =  cseqHeader.getSequenceNumber() +1;
-                    System.out.println(cseq);
+                        int cseq =  cseqHeader.getSequenceNumber() + 1;
                     cseqHeader.setSequenceNumber(cseq);
-                    request.setHeader(cseqHeader);
+                    }
+                    catch (InvalidArgumentException ex) {
+                        ex.printStackTrace();
+                        console.error("Cseq Header must contain a integerl value!",
+                                    ex);
+                        throw new CommunicationsException(
+                                "Cseq Header must contain a integerl value!");
+                    }
+                    
                     ((Watcher) callback).sendSubscription(request);
+                    if(console.isDebugEnabled())
+                            console.debug("Processing OUTGOING_SUBSCRIPTION trigger");
                 }
+                catch (CommunicationsException ex) {
+                    ex.printStackTrace();
+                    console.error("Unable to send SUBSCRIBE request !", 
+                                ex);
             }
-            catch (Exception e) {
-                // TODO: handle exception
+            }
+            else {
+                if(console.isDebugEnabled())
+                            console.debug("Tried to process an unknown subscription type trigger : " 
+                                        + subscriptionType);
             }
         } finally {
             console.logExit();
         }
     }
 
+    /**
+     * Test if the couple subscriber/presentity is the same as the one in
+     * the current object. This couple can be considered as the subscription
+     * unique identifier.
+     * 
+     * @param subscriber the subscriber's address
+     * @param presentity the presentity's address
+     * @return true if equal, false else
+     */
     public boolean equals(String subscriber, String presentity)
     {
         return (this.subscriber.equals(subscriber) && this.presentity.equals(presentity));
     }
 
-    public String getSubscriber()
+
+    /**
+     * @return
+     */
+    public SubscriptionAuthorizationResponse getAuthorizationResponse()
     {
-        return this.subscriber;
+        return authorizationResponse;
     }
 
-    public String getPresentity()
+    /**
+     * @return
+     */
+    public Dialog getDialog()
     {
-        return this.presentity;
+        return dialog;
     }
 
+    /**
+     * @return
+     */
     public String getPresenceDocFormat()
     {
-        return this.presenceDocFormat;
+        return presenceDocFormat;
     }
 
-    public void setStatus(String status)
+    /**
+     * @return
+     */
+    public String getPresentity()
     {
-        this.status = status;
+        return presentity;
     }
 
+    /**
+     * @return
+     */
     public String getStatus()
     {
-        return this.status;
+        return status;
     }
 
-    public void setAuthorizationResponse(SubscriptionAuthorizationResponse authorizationResponse)
+    /**
+     * @return
+     */
+    public String getSubscriber()
     {
-        this.authorizationResponse = authorizationResponse;
+        return subscriber;
     }
 
-    public SubscriptionAuthorizationResponse getAuthorizationResponse()
+    /**
+     * @param response
+     */
+    public void setAuthorizationResponse(SubscriptionAuthorizationResponse response)
     {
-        return this.authorizationResponse;
+        authorizationResponse = response;
     }
 
+    /**
+     * @param string
+     */
+    public void setStatus(String string)
+    {
+        status = string;
+    }
+    
+    /**
+     * @return
+     */
     public Request getRequest()
     {
-        return this.request;
+        return request;
     }
 
-    public Dialog getDialog()
+    public String toString()
     {
-        return this.dialog;
+        String sub = "subscriber = " + subscriber +"\n" +
+                            "presentity = " + presentity +"\n" +
+                            "dialog = " + dialog +"\n" +
+                            "subscriptionType = " + subscriptionType +"\n" +
+                            "status = " + status +"\n" +
+                            "authorizationResponse = " + authorizationResponse +"\n" +
+                            "presenceDocFormat = " + presenceDocFormat ;
+        
+        return sub;
     }
+
+
+
 }

File [changed]: Watcher.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/src/net/java/sip/communicator/sip/simple/Watcher.java?r1=1.9&r2=1.10
Delta lines:  +136 -113
-----------------------
--- Watcher.java	25 Apr 2004 14:54:47 -0000	1.9
+++ Watcher.java	10 May 2004 21:16:48 -0000	1.10
@@ -63,12 +63,11 @@
 import java.util.Vector;
 
 import javax.sip.ClientTransaction;
+import javax.sip.Dialog;
 import javax.sip.InvalidArgumentException;
 import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.TransactionUnavailableException;
-import javax.sip.address.Address;
-import javax.sip.address.URI;
 import javax.sip.message.*;
 import javax.sip.header.*;
 import javax.sip.address.*;
@@ -81,12 +80,6 @@
 import net.java.sip.communicator.sip.security.SipSecurityException;
 
 
-////a degager
-import java.awt.*;
-import javax.swing.*;
-import java.awt.event.*;
-
-
 /**
  * @author Martin Andre
  */
@@ -109,7 +102,6 @@
             ex.printStackTrace();
             expiresSubscription = 600;	/* Default value */
         }
-        new TestDialog().show();
     }
 
     void setSipManagerCallBack(SipManager sipManCallback)
@@ -264,8 +256,11 @@
             try {
                 expiresHeader = sipManCallback.headerFactory.createExpiresHeader(expiresSubscription);
             }
-            catch (Exception e) {
-                // TODO: handle exception
+            catch (InvalidArgumentException ex) {
+                console.error(
+                    "Expires Header must be an integer!", ex);
+                throw new CommunicationsException(
+                    "Expires Header must be an integer!", ex);
             }
 
             //ViaHeaders
@@ -309,34 +304,32 @@
     * @param subscribe SUBSCRIBE message to be sent to the notifier.
     * @return transaction ID associated with the SUBSCRIBE message sent by this method.
     * @throws CommunicationsException
-    * @throws java.lang.IllegalArgumentException
     */
     public java.lang.String sendSubscription(Request subscribe)
-        throws CommunicationsException, java.lang.IllegalArgumentException
+        throws CommunicationsException
     {
         try
         {
             console.logEntry();
 
             ClientTransaction subscribeTransaction = null;
-            // Presentity has here a sip scheme
             String presentity = subscribe.getRequestURI().toString();
-            try {
+            String subscriber = sipManCallback.getFromHeader().getAddress().getURI().toString();
+            
                 try {
                     subscribeTransaction = sipManCallback.sipProvider.
                         getNewClientTransaction(subscribe);
                 }
                 catch (TransactionUnavailableException ex) {
                     console.error(
-                        "Failed to create subscribeTransaction.\n" +
-                        "This is most probably a network connection error.", ex);
+                    "Failed to create subscribeTransaction.", ex);
                     throw new CommunicationsException(
-                        "Failed to create subscribeTransaction.\n" +
-                        "This is most probably a network connection error.", ex);
+                    "Failed to create subscribeTransaction.", ex);
                 }
 
                 try {
                     subscribeTransaction.sendRequest();
+
                     if( console.isDebugEnabled() )
                         console.debug("sent request: " + subscribe);
 
@@ -345,29 +338,24 @@
                     // for exemple if expires is 10 min, a the subscription will be refreshed in 9 min
                     long expires = expiresSubscription * 900;
 
-                    String subscriber = sipManCallback.getFromHeader().getAddress().getURI().toString();
-                    int position = getIndexInSubscriptionCache(subscriber, presentity);
-                    if (position != -1)
-                    {
-                        /* Subscription already in incoming subscription list */
-                        //reschedule the execution
-                        scheduler.reschedule((Subscription) outgSubscriptions.elementAt(position), new Date(System.currentTimeMillis() + expires));
-                        //scheduler.reschedule((Subscription) incSubscriptions.elementAt(position), expires);
-                    }
-                    else
+                //is this a new subscription or have we already send subscriptions
+                //to the same subscriber
+                int sIndex = getIndexInSubscriptionCache(subscriber, presentity);
+                if (sIndex == -1)
                     {
                         /* First subscription for the couple subscriber/presentity */
                         Subscription sub = new Subscription(
                             subscriber,
                             presentity,
                             subscribeTransaction.getDialog(),
+                        subscribe,
                             Subscription.OUTGOING_SUBSCRIPTION,
                             Subscription.PENDING,
                             null,
                             "pidf+xml",
                             this);
                         outgSubscriptions.add(sub);
-                        scheduler.schedule(sub, expires);
+                    scheduler.scheduleAtFixedRate(sub, new Date(System.currentTimeMillis() + expires), expires);
                     }
                 }
                 catch (SipException ex) {
@@ -376,15 +364,69 @@
                     throw new CommunicationsException(
                         "An error occurred while sending notify request", ex);
                 }
+                
+            return subscribeTransaction.toString();
+        } finally {
+            console.logExit();
+        }
+    }
+    
+    
+    /**
+    * Sends a SUBSCRIBE message to a notifier using a specific dialog. This method is mainly used to
+    * refresh subscribtion to a notifier. Invoking this method will renew the subscription explicitly and 
+    * the application will receive a NOTIFY message with the current presence information for the 
+    * presentity.
+    * This method should be used to handle subscription refresh and unsubscribe.
+    * 
+    * @param dialog the dialog to use
+    * @return transaction ID associated with the SUBSCRIBE message sent by this method.
+    * @throws CommunicationsException
+    */
+    public java.lang.String sendSubscription(Dialog dialog)
+        throws CommunicationsException
+    {
+        try
+        {
+            console.logEntry();
+
+            ClientTransaction subscribeTransaction = null;
+            Request subscribe = null;
+            
+            try {
+                subscribe = dialog.createRequest(Request.SUBSCRIBE);
+            }
+            catch (SipException ex) {
+                console.error(
+                    "Failed to create subscribe request.", ex);
+                throw new CommunicationsException(
+                    "Failed to create subscribe request.", ex);
+            }
+            
+            try {
+                subscribeTransaction = sipManCallback.sipProvider.
+                    getNewClientTransaction(subscribe);
+            }
+            catch (TransactionUnavailableException ex) {
+                console.error(
+                    "Failed to create subscribeTransaction.", ex);
+                throw new CommunicationsException(
+                    "Failed to create subscribeTransaction.", ex);
+            }
+
+            try {
+                dialog.sendRequest(subscribeTransaction);
+
+                if( console.isDebugEnabled() )
+                    console.debug("sent request: " + subscribe);
             }
-            catch (Exception e) {
+            catch (SipException ex) {
                 console.error(
-                    "Failed to send subscription request.\n" +
-                    "This is most probably a network connection error.", e);
+                    "An error occurred while sending notify request", ex);
                 throw new CommunicationsException(
-                    "Failed to send subscription request.\n" +
-                    "This is most probably a network connection error.", e);
+                    "An error occurred while sending notify request", ex);
             }
+                
             return subscribeTransaction.toString();
         } finally {
             console.logExit();
@@ -401,8 +443,6 @@
      * @param notification the received NOTIFY message.
      * @param transaction the transaction associated with the received NOTIFY request.
      * @return the status code (200 or 481) of the response sent by this method.
-     * @throws CommunicationsException
-     * @throws java.lang.IllegalArgumentException
      */
     public int processNotification(Request notification, ServerTransaction transaction)
     {
@@ -486,9 +526,9 @@
 
     /**
      * Search for the position of the subscription identified by the couple subscriber/presentity
-     * @param subscriber
-     * @param presentity
-     * @return the position of the subscription in the Vector, and -1 if the couple is not foud
+     * @param subscriber the subscriber's address including sip scheme
+     * @param presentity the presentity's address including sip scheme
+     * @return the position of the subscription in the Vector, and -1 if the couple is not found
      */
     public int getIndexInSubscriptionCache(String subscriber, String presentity)
     {
@@ -505,7 +545,6 @@
      * Unsubscribe from a notifier.
      * @param subscriber the user whose subscription to the presentity is to be removed.
      * @param presentity the user from whom the subscriber's subscription is to be removed.
-     * @param reasonCode the reason code for the removal of the subscriber's subscription. Its value may be a null.
      * @throws CommunicationsException
      */
     public void removeSubscription(String subscriber, String presentity)
@@ -520,19 +559,25 @@
                 //Subscription is in outgoing subscription list
                 Subscription sub = (Subscription) outgSubscriptions.elementAt(rank);
 
-                Request subscribe = null;
+                Request subscribe = (Request)sub.getRequest().clone();
                 try {
-                    subscribe = (Request)sub.getDialog().createRequest(Request.SUBSCRIBE);
+                    //Increment CSeq
+                    CSeqHeader cseqHeader = (CSeqHeader)subscribe.getHeader(CSeqHeader.NAME);
+                    int cseq =  cseqHeader.getSequenceNumber() + 1;
+                    cseqHeader.setSequenceNumber(cseq);
                 }
-                catch (SipException ex) {
-                    console.error(
-                        "Could not create subscribe message!", ex);
+                catch (InvalidArgumentException ex) {
+                    ex.printStackTrace();
+                    console.error("Cseq Header must contain a integer value!",
+                                ex);
                     throw new CommunicationsException(
-                        "Could not create subscribe message!", ex);
+                            "Cseq Header must contain a integer value!");
                 }
-                ExpiresHeader expiresHeader = null;
+
                 try {
-                    expiresHeader = sipManCallback.headerFactory.createExpiresHeader(0);
+                    //Set Expires Header to 0
+                    ExpiresHeader expiresHeader = (ExpiresHeader)subscribe.getHeader(ExpiresHeader.NAME);
+                    expiresHeader.setExpires(0);
                 }
                 catch (InvalidArgumentException ex) {
                     //Shouldn't happen
@@ -541,12 +586,14 @@
                     throw new CommunicationsException(
                         "The expires header must be an integer!", ex);
                 }
-                subscribe.addHeader(expiresHeader);
+                
                 sendSubscription(subscribe);
                 
                 //At least, remove the subscription
                 outgSubscriptions.remove(rank);
-                //System.out.println("Removing "+subscriber+"/"+presentity+" from subscription list");
+                if(console.isDebugEnabled())
+                            console.debug("Removing " + subscriber + "/"
+                                          + presentity + " from subscription list");
             }
         } finally {
             console.logExit();
@@ -556,7 +603,7 @@
     
     /**
      * Removes all outgoing subscriptions.
-     * 
+     * @throws CommunicationsException
      */
     public void removeAllSubscriptions()
         throws CommunicationsException
@@ -574,30 +621,6 @@
         }
     }
 
-    public class TestDialog extends JDialog
-            implements ActionListener
-    {
-        public TestDialog()
-        {
-            super((JFrame)null, false);
-            setSize(200, 60);
-            JButton testB = new JButton("Send Msg");
-            testB.addActionListener(this);
-            getContentPane().setLayout(new BorderLayout());
-            getContentPane().add(testB);
-        }
-
-        public void actionPerformed(ActionEvent evt)
-        {
-            try {
-                sendSubscription("andre at iptel.org");
-            }
-            catch (Exception e) {
-                // TODO: handle exception
-            }
-        }
-    }
-
     /**
      * @todo handle properly OK messages
      *
@@ -614,7 +637,7 @@
     * @todo handle properly NOT_FOUND messages
     *
     * @param clientTransaction ClientTransaction
-    * @param ok Response
+    * @param notFound Response
     */
    public void processNotFound(ClientTransaction clientTransaction, Response notFound)
    {

Directory: /sip-communicator/src/net/java/sip/communicator/sip/
===============================================================

File [changed]: SipManager.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/src/net/java/sip/communicator/sip/SipManager.java?r1=1.24&r2=1.25
Delta lines:  +3 -3
-------------------
--- SipManager.java	25 Apr 2004 14:54:47 -0000	1.24
+++ SipManager.java	10 May 2004 21:16:49 -0000	1.25
@@ -70,8 +70,8 @@
 import net.java.sip.communicator.sip.simple.*;
 
 import java.net.InetSocketAddress;
-import net.java.sip.communicator.sip.simple.storage.*;
-import java.io.*;
+//import net.java.sip.communicator.sip.simple.storage.*;
+//import java.io.*;
 import net.java.sip.communicator.sip.simple.event.*;
 import net.java.sip.communicator.sip.simple.SubscriptionAuthority;
 
@@ -620,7 +620,7 @@
             }
             checkIfStarted();
             presenceAgent.removeAllSubscriptions(SubscriptionStateHeader.NO_RESOURCE);
-            //watcher.removeAllSubscriptions();
+            watcher.removeAllSubscriptions();
             registerProcessing.unregister();
         }
         finally {




---------------------------------------------------------------------
To unsubscribe, e-mail: cvs-unsubscribe at sip-communicator.dev.java.net
For additional commands, e-mail: cvs-help at sip-communicator.dev.java.net




More information about the commits mailing list