]> sjero.net Git - linphone/blob - p2pproxy/src/org/linphone/p2pproxy/core/sipproxy/SipProxyRegistrar.java
remove mediastreamer2 and add it as a submodule instead.
[linphone] / p2pproxy / src / org / linphone / p2pproxy / core / sipproxy / SipProxyRegistrar.java
1 /*
2 p2pproxy Copyright (C) 2007  Jehan Monnier ()
3
4 SipListener.java - sip proxy.
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21 package org.linphone.p2pproxy.core.sipproxy;
22
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.net.InetSocketAddress;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.Map;
30 import java.util.Timer;
31 import java.util.TimerTask;
32 import java.util.concurrent.Callable;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.ExecutorService;
35 import java.util.concurrent.Future;
36 import net.jxta.document.AdvertisementFactory;
37 import net.jxta.id.IDFactory;
38 import org.apache.log4j.Logger;
39 import org.apache.log4j.NDC;
40 import org.linphone.p2pproxy.api.P2pProxyException;
41 import org.linphone.p2pproxy.api.P2pProxyUserNotFoundException;
42
43 import org.linphone.p2pproxy.core.Configurator;
44 import org.linphone.p2pproxy.core.JxtaNetworkManager;
45 import org.linphone.p2pproxy.core.P2pProxyAccountManagementMBean;
46 import org.linphone.p2pproxy.core.media.rtprelay.MediaType;
47 import org.linphone.p2pproxy.core.sipproxy.superpeers.SuperPeerProxy;
48 import org.zoolu.sip.header.ExpiresHeader;
49 import org.zoolu.sip.header.MultipleHeader;
50 import org.zoolu.sip.message.Message;
51 import org.zoolu.sip.message.MessageFactory;
52 import org.zoolu.sip.provider.SipProvider;
53 import org.zoolu.sip.provider.SipProviderListener;
54 import org.zoolu.sip.provider.SipStack;
55 import org.zoolu.sip.transaction.TransactionServer;
56 import java.util.Collections;
57
58 public class SipProxyRegistrar implements SipProviderListener,SipProxyRegistrarMBean {
59    private final static Logger mLog = Logger.getLogger(SipProxyRegistrar.class);   
60    public final static String REGISTRAR_PORT="org.linphone.p2pproxy.SipListener.registrar.port";
61    public final static String REGISTRAR_PUBLIC_ADDRESS="org.linphone.p2pproxy.SipListener.registrar.public.address";
62    public final static String ADV_NAME = "p2p-proxy-proxyregistrar";
63    private final int ADV_LIFE_TIME=6000000;
64    //
65    private final SipProvider mProvider;
66    private final JxtaNetworkManager mJxtaNetworkManager;
67    private final ExecutorService mPool;
68
69    private final Map<String,Registration> mRegistrationTab = Collections.synchronizedMap(new HashMap<String,Registration>()); 
70    private final Map<String,SipMessageTask> mCancalableTaskTab = Collections.synchronizedMap(new HashMap<String,SipMessageTask>());
71
72    private final P2pProxyAccountManagementMBean mP2pProxyAccountManagement;
73    private final Configurator mProperties;
74    private final SuperPeerProxy mSuperPeerProxy;
75    private  NetworkResourceAdvertisement mProxyRegistrationAdvertisement;
76    private final Timer mTimer = new Timer ("Proxy registrar adv publisher");
77   
78    //private long mNumberOfEstablishedCall;
79    private long mNumberOfRefusedRegistration;
80    private long mNumberOfSuccessfullRegistration;
81    private long mNumberOfUSerNotFound;
82    private long mNumberOfUnknownUSers;
83    private long mNumberOfUnknownUsersForRegistration;
84    private long mNumberOfUnRegistration;
85    
86    
87    public static class Registration {
88       long RegistrationDate;
89       public long Expiration;
90       //implementation specific context
91       public Object NetResources;
92       public  Map<MediaType,InetSocketAddress> RtpRelays = new HashMap<MediaType,InetSocketAddress>() ;
93       public String Contact;
94       public final String From;
95       public Registration(String aFrom) {From = aFrom;}
96       public String toString() {
97           return "reg date ["+RegistrationDate+"] exp ["+Expiration+"] contact ["+Contact+"] from ["+From+"]";
98       }
99    }
100    
101    class SipMessageTask implements Callable<Boolean> {
102       private final SipProvider mProvider;
103       private final Message mMessage;
104       private Future<?> mFuture;
105       
106       /**
107        * @return Returns the mMessage.
108        */
109       public Message getMessage() {
110          return mMessage;
111       }
112       SipMessageTask(SipProvider aProvider, Message aMessage) {
113          mProvider = aProvider;
114          mMessage = aMessage;
115       }
116       public Boolean call() throws Exception {
117           NDC.push(mMessage.getFirstLine() + mMessage.getCallIdHeader().getCallId() +":");
118          try {
119             if (mMessage.isRequest()) {
120                if (mMessage.isRegister()) {
121                   processRegister(mProvider, mMessage);
122                } else {
123                   proxyRequest(mProvider, mMessage);
124                }
125             } else {
126                //1 remove via header   
127                SipUtils.removeVia(mProvider,mMessage);
128                //2 process response
129                proxyResponse(mProvider, mMessage);
130             }
131             if (mMessage.isInvite() && mCancalableTaskTab.containsKey(mMessage.getCallIdHeader().getCallId()) )  {
132                mCancalableTaskTab.remove(mMessage.getCallIdHeader().getCallId());
133             }
134          } catch (InterruptedException eInter) {
135             mLog.info("request interrupted",eInter);
136             //nop
137          }
138          catch (Exception e) {
139             mLog.error("unexpected behavior",e);
140             if (mMessage.isRequest()) {
141                Message lResp= null;
142                lResp = MessageFactory.createResponse(mMessage,500,e.getMessage(),null);
143                TransactionServer lTransactionServer = new TransactionServer(mProvider,mMessage,null);
144                lTransactionServer.respondWith(lResp);
145                synchronized (SipProxyRegistrar.this) {
146                   if (mMessage.isInvite() && mCancalableTaskTab.containsKey(mMessage.getCallIdHeader().getCallId()) )  {
147                      mCancalableTaskTab.remove(mMessage.getCallIdHeader().getCallId());
148                   }
149                }
150             }
151          } finally {
152                  NDC.pop();
153          }
154          return true;
155       }
156       /**
157        * @return Returns the mFuture.
158        */
159       public Future<?> getFuture() {
160          return mFuture;
161       }
162       /**
163        * @param future The mFuture to set.
164        */
165       public void setFuture(Future<?> future) {
166          mFuture = future;
167       }
168       
169    }
170    
171    public SipProxyRegistrar(Configurator lProperties,JxtaNetworkManager aJxtaNetworkManager,P2pProxyAccountManagementMBean aP2pProxyAccountManagement) throws IOException {
172       mJxtaNetworkManager =  aJxtaNetworkManager;
173       mP2pProxyAccountManagement = aP2pProxyAccountManagement;
174       mProperties = lProperties;
175       File lFile = new File(SipStack.log_path);
176       if (lFile.exists() == false) lFile.mkdir();
177       String lViaAddress = lProperties.getProperty(REGISTRAR_PUBLIC_ADDRESS);
178       int lPort = Integer.parseInt(lProperties.getProperty(REGISTRAR_PORT, "5060"));
179       String[] lProto = {SipProvider.PROTO_UDP};
180       mProvider=new SipProvider(lViaAddress,lPort,lProto,SipProvider.ALL_INTERFACES);
181       mProvider.addSipProviderListener(SipProvider.PROMISQUE,this);
182       mPool = Executors.newCachedThreadPool();
183       mSuperPeerProxy = new SuperPeerProxy(aJxtaNetworkManager, "sip:"+mProvider.getViaAddress()+":"+mProvider.getPort(),mRegistrationTab);
184       TimerTask lPublisherTask = new TimerTask() {
185
186                 @Override
187                 public void run() {
188                         try {
189                                 SipProxyRegistrar.this.publishAdvertisement();
190                         } catch (IOException e) {
191                                 mLog.error("cannot publish proxy registar adv", e);
192                         }
193                         
194                 }
195           
196       };
197       mTimer.scheduleAtFixedRate(lPublisherTask, 0, ADV_LIFE_TIME-ADV_LIFE_TIME/10);
198       TimerTask lRegistrationTableGC = new TimerTask() {
199
200                 @Override
201                 public void run() {
202                         // copy list
203                         Collection<Registration> lCurrentRegistrations = mRegistrationTab.values();
204                         long lCurrentDate = System.currentTimeMillis();
205                         for (Registration lRegistration : lCurrentRegistrations) {
206                                 if ((lCurrentDate - lRegistration.RegistrationDate - lRegistration.Expiration) > 0) {
207                                         if (mLog.isInfoEnabled()) mLog.info("registration entry ["+lRegistration+"] has expired");
208                                         mRegistrationTab.remove(lRegistration.From);
209                                 }
210                         }
211                 }
212           
213       };
214       mTimer.scheduleAtFixedRate(lRegistrationTableGC, 0, 60000);
215       
216    }
217    public  void onReceivedMessage(SipProvider aProvider, Message aMessage) {
218       if (aProvider.getListeners().containsKey(aMessage.getTransactionId())) {
219          if (mLog.isDebugEnabled()) mLog.debug ("nothing to do, transaction already handled for ["+aMessage+"]");
220          return;
221       }
222       if (mLog.isInfoEnabled()) mLog.info("receiving message ["+aMessage+"]");
223       String lCallId = aMessage.getCallIdHeader().getCallId();
224       SipMessageTask lPendingSipMessageTask = mCancalableTaskTab.get(lCallId);
225       
226       if (aMessage.isCancel() && lPendingSipMessageTask != null ) {
227          // search for pending transaction
228          
229          lPendingSipMessageTask.getFuture().cancel(true);
230          mCancalableTaskTab.remove(lCallId);
231
232          SipUtils.removeVia(mProvider,lPendingSipMessageTask.getMessage());
233          // accept cancel
234          Message lCancelResp = MessageFactory.createResponse(aMessage,200,"ok",null);
235          TransactionServer lCancelTransactionServer = new TransactionServer(mProvider,aMessage,null);
236          lCancelTransactionServer.respondWith(lCancelResp);
237          
238          // cancel invite
239          Message lInviteResp = MessageFactory.createResponse(lPendingSipMessageTask.getMessage(),487,"Request Terminated",null);
240          TransactionServer lInviteTransactionServer = new TransactionServer(mProvider,lPendingSipMessageTask.getMessage(),null);
241          lInviteTransactionServer.respondWith(lInviteResp);          
242       } else {
243          // normal behavior
244          SipMessageTask lSipMessageTask = new SipMessageTask(aProvider,aMessage);
245          lSipMessageTask.setFuture(mPool.submit(lSipMessageTask));
246          if (aMessage.isInvite()) {
247             mCancalableTaskTab.put(aMessage.getCallIdHeader().getCallId(),lSipMessageTask);                
248          }
249          
250       }
251       
252    }
253 //////////////////////////////////////////////////////////////////////
254 ////Proxy methods
255 /////////////////////////////////////////////////////////////////////   
256    private void proxyResponse(SipProvider aProvider, Message aMessage) throws NumberFormatException, InterruptedException, P2pProxyException, IOException {
257       mSuperPeerProxy.proxyResponse(aProvider, aMessage);
258    }
259    private void proxyRequest(SipProvider aProvider, Message aMessage) throws Exception {
260       if (aMessage.isAck() && aMessage.getToHeader().getTag() == null) {
261          // just terminate the Invite transaction
262          return;
263       }
264       
265       if (aMessage.isInvite() == true) {
266          // 100 trying
267          TransactionServer lTransactionServer = new TransactionServer(aProvider,aMessage,null);
268          Message l100Trying = MessageFactory.createResponse(aMessage,100,"trying",null);
269          lTransactionServer.respondWith(l100Trying);
270          lTransactionServer.terminate();
271       }
272       //remove route
273       MultipleHeader lMultipleRoute = aMessage.getRoutes();
274       if (lMultipleRoute != null) {
275          lMultipleRoute.removeTop();
276          aMessage.setRoutes(lMultipleRoute);
277       }
278       // add Via only udp
279       SipUtils.addVia(aProvider,aMessage);
280       try {
281          mSuperPeerProxy.proxyRequest(aProvider, aMessage);
282       }catch (P2pProxyUserNotFoundException e) {
283          //remove via 
284          SipUtils.removeVia(aProvider, aMessage);
285          if (aMessage.isInvite()) {
286             Message lresp = MessageFactory.createResponse(aMessage,404,e.getMessage(),null);
287             TransactionServer lTransactionServer = new TransactionServer(aProvider,aMessage,null);
288             lTransactionServer.respondWith(lresp);
289             lTransactionServer.terminate();
290          } else {
291             throw e;
292          }
293       }
294    }
295    
296    
297 //////////////////////////////////////////////////////////////////////
298 ////Registrar methods
299 /////////////////////////////////////////////////////////////////////   
300    
301    private  void processRegister(SipProvider aProvider, Message aMessage) throws IOException, P2pProxyException {
302       TransactionServer lTransactionServer = new TransactionServer(aProvider,aMessage,null);
303       Message l100Trying = MessageFactory.createResponse(aMessage,100,"trying",null);
304       lTransactionServer.respondWith(l100Trying);
305       Registration lRegistration=null;
306       
307       //check if already registered
308       
309       String lFromName = aMessage.getFromHeader().getNameAddress().getAddress().toString();
310       if ((lRegistration = mRegistrationTab.get(lFromName)) != null) {
311          
312          updateRegistration(lRegistration,aMessage);
313          
314          if (aMessage.getExpiresHeader().getDeltaSeconds() == 0) {
315             mRegistrationTab.remove(lFromName);
316          } 
317          
318       } else {
319          // new registration
320          // test if account already created
321          
322          if (mP2pProxyAccountManagement.isValidAccount(lFromName)) {
323          lRegistration = new Registration(lFromName);
324          // forgot contact, use rport lRegistration.Contact = aMessage.getContactHeader().getNameAddress().getAddress().toString();;
325          
326          updateRegistration(lRegistration,aMessage);
327          mRegistrationTab.put(lFromName, lRegistration);
328          } else {
329             // create negative answers
330             mLog.info("account for user ["+lFromName+"] not created yet");
331             Message lresp = MessageFactory.createResponse(aMessage,404,"Not found",null);
332             lTransactionServer.respondWith(lresp);
333             return;
334          }
335       }
336       // ok, create answers
337       Message lresp = MessageFactory.createResponse(aMessage,200,"Ok",null);
338       lresp.addContactHeader(aMessage.getContactHeader(), false);
339       ExpiresHeader lExpireHeader = new  ExpiresHeader((int) (lRegistration.Expiration/1000));
340       lresp.addHeader(lExpireHeader, false);
341       lTransactionServer.respondWith(lresp);
342       
343    }
344    private void updateRegistration(Registration aRegistration, Message aRegistrationMessage) throws P2pProxyException {
345       aRegistration.RegistrationDate = System.currentTimeMillis();
346       // default registration period
347       aRegistration.Expiration = 3600000;
348       if (aRegistrationMessage.getExpiresHeader() != null ) {
349          aRegistration.Expiration =  aRegistrationMessage.getExpiresHeader().getDeltaSeconds()*1000; 
350       }
351       aRegistration.Contact = "sip:"+aRegistrationMessage.getRemoteAddress()+":"+aRegistrationMessage.getRemotePort();
352       mSuperPeerProxy.updateRegistration(aRegistration, aRegistrationMessage); 
353    }
354    
355    
356 public void stop() {
357    try {
358       mJxtaNetworkManager.getPeerGroup().getDiscoveryService().flushAdvertisement(mProxyRegistrationAdvertisement);
359    } catch (IOException e) {
360       mLog.warn("cannot flush registrar adv",e );
361    }
362    mProvider.halt();
363 }
364  
365  //   public long getNumberOfEstablishedCall() {
366 //      return mNumberOfEstablishedCall;
367 //   }
368    public long getNumberOfRefusedRegistration() {
369       return mNumberOfRefusedRegistration;
370    }
371    public long getNumberOfSuccessfullRegistration() {
372       return mNumberOfSuccessfullRegistration;
373    }
374    public long getNumberOfUSerNotFound() {
375       return mNumberOfUSerNotFound;
376    }
377    public long getNumberOfUnknownUSers() {
378       return mNumberOfUnknownUSers;
379    }
380    public long getNumberOfUnknownUsersForRegistration() {
381        return mNumberOfUnknownUsersForRegistration;
382    }
383    public String[] getRegisteredList() {
384       String[] lRegisteredList = new String[mRegistrationTab.size()] ;
385       int i=0;
386       for (String lRegistrationKey : mRegistrationTab.keySet()) {
387          lRegisteredList[i++] = lRegistrationKey;
388       }
389       return   lRegisteredList;
390    }
391    public long getNumberOfUnRegistration() {
392       return mNumberOfUnRegistration;
393    }
394    private void publishAdvertisement() throws IOException {
395            if (mProxyRegistrationAdvertisement == null) {   
396            mProxyRegistrationAdvertisement = (NetworkResourceAdvertisement) AdvertisementFactory.newAdvertisement(NetworkResourceAdvertisement.getAdvertisementType());
397               mProxyRegistrationAdvertisement.setID(IDFactory.newCodatID(mJxtaNetworkManager.getPeerGroup().getPeerGroupID(), Integer.toHexString(mSuperPeerProxy.getSipProxyRegistrarAddress().hashCode()).getBytes("US-ASCII")));
398               mProxyRegistrationAdvertisement.setAddress(mSuperPeerProxy.getSipProxyRegistrarAddress());
399               mProxyRegistrationAdvertisement.setName(ADV_NAME);
400            }
401            mJxtaNetworkManager.getPeerGroup().getDiscoveryService().publish(mProxyRegistrationAdvertisement,ADV_LIFE_TIME,ADV_LIFE_TIME/2);        
402            mLog.info(mProxyRegistrationAdvertisement + "published");
403    }
404 }