001: /**
002: * $RCSfile$
003: * $Revision: $
004: * $Date: $
005: *
006: * Copyright (C) 2007 Jive Software. All rights reserved.
007: *
008: * This software is published under the terms of the GNU Public License (GPL),
009: * a copy of which is included in this distribution.
010: */package org.jivesoftware.openfire.sip.tester.stack;
011:
012: import org.jivesoftware.openfire.sip.tester.security.SipSecurityException;
013: import org.jivesoftware.openfire.sip.tester.comm.CommunicationsException;
014: import org.jivesoftware.openfire.sip.tester.Log;
015:
016: import javax.sip.message.Request;
017: import javax.sip.message.Response;
018: import javax.sip.*;
019: import javax.sip.address.Address;
020: import javax.sip.address.SipURI;
021: import javax.sip.header.*;
022: import java.util.Timer;
023: import java.util.ArrayList;
024: import java.util.TimerTask;
025: import java.text.ParseException;
026:
027: /**
028: * Title: SIP Register Tester
029: * Description:JAIN-SIP Test application
030: *
031: * @author Thiago Rocha Camargo (thiago@jivesoftware.com)
032: */
033: class RegisterProcessing {
034:
035: private SipManager sipManCallback = null;
036:
037: private Request registerRequest = null;
038:
039: private boolean isRegistered = false;
040:
041: private boolean isUnregistering = false;
042:
043: private Timer reRegisterTimer = null;
044:
045: private int keepAlivePort = 0;
046:
047: private Timer keepAliveTimer = null;
048:
049: RegisterProcessing(SipManager sipManCallback) {
050: this .sipManCallback = sipManCallback;
051: }
052:
053: void setSipManagerCallBack(SipManager sipManCallback) {
054: this .sipManCallback = sipManCallback;
055: }
056:
057: void processOK(ClientTransaction clientTransatcion,
058: Response response) {
059: isRegistered = true;
060: FromHeader fromHeader = ((FromHeader) response
061: .getHeader(FromHeader.NAME));
062: Address address = fromHeader.getAddress();
063:
064: int expires = 0;
065: if (!isUnregistering) {
066: ContactHeader contactHeader = (ContactHeader) response
067: .getHeader(ContactHeader.NAME);
068: // TODO check if the registrar created the contact address
069: if (contactHeader != null) {
070: expires = contactHeader.getExpires();
071: } else {
072: ExpiresHeader expiresHeader = response.getExpires();
073: if (expiresHeader != null) {
074: expires = expiresHeader.getExpires();
075: }
076: }
077: }
078: // expires may be null
079: // fix by Luca Bincoletto <Luca.Bincoletto@tilab.com>
080: if (expires == 0) {
081: isUnregistering = false;
082: sipManCallback.fireUnregistered(address.toString());
083: } else {
084: if (reRegisterTimer != null)
085: reRegisterTimer.cancel();
086: if (keepAliveTimer != null)
087: keepAliveTimer.cancel();
088:
089: reRegisterTimer = new Timer();
090: keepAliveTimer = new Timer();
091: // if (expires > 0 && expires < 60) {
092: // [issue 2] Schedule re registrations
093: // bug reported by LynlvL@netscape.com
094: // use the value returned by the server to reschedule
095: // registration
096: SipURI uri = (SipURI) address.getURI();
097: scheduleReRegistration(uri.getHost(), uri.getPort(), uri
098: .getTransportParam(), expires);
099: // }
100: /*
101: * else{ SipURI uri = (SipURI) address.getURI();
102: * scheduleReRegistration(uri.getHost(), uri.getPort(),
103: * uri.getTransportParam(), expires); }
104: */
105: sipManCallback.fireRegistered(address.toString());
106:
107: }
108: }
109:
110: void processTimeout(Transaction transatcion, Request request) {
111: isRegistered = true;
112: FromHeader fromHeader = ((FromHeader) request
113: .getHeader(FromHeader.NAME));
114: Address address = fromHeader.getAddress();
115: sipManCallback.fireUnregistered("Request timeouted for: "
116: + address.toString());
117: }
118:
119: void processNotImplemented(ClientTransaction transatcion,
120: Response response) {
121: isRegistered = true;
122: FromHeader fromHeader = ((FromHeader) response
123: .getHeader(FromHeader.NAME));
124: Address address = fromHeader.getAddress();
125: sipManCallback
126: .fireUnregistered("Server returned NOT_IMPLEMENTED. "
127: + address.toString());
128: }
129:
130: /**
131: * Attempts to re-ogenerate the corresponding request with the proper
132: * credentials and terminates the call if it fails.
133: *
134: * @param clientTransaction the corresponding transaction
135: * @param response the challenge
136: */
137: void processAuthenticationChallenge(
138: ClientTransaction clientTransaction, Response response) {
139: try {
140:
141: ClientTransaction retryTran = sipManCallback.sipSecurityManager
142: .handleChallenge(response, clientTransaction);
143: retryTran.sendRequest();
144: } catch (SipSecurityException exc) {
145: // tell the others we couldn't register
146: sipManCallback
147: .fireUnregistered(((FromHeader) clientTransaction
148: .getRequest().getHeader(FromHeader.NAME))
149: .getAddress().toString());
150: sipManCallback
151: .fireCommunicationsError(new CommunicationsException(
152: "Authorization failed!", exc));
153: } catch (Exception exc) {
154: // tell the others we couldn't register
155: sipManCallback
156: .fireUnregistered(((FromHeader) clientTransaction
157: .getRequest().getHeader(FromHeader.NAME))
158: .getAddress().toString());
159: sipManCallback
160: .fireCommunicationsError(new CommunicationsException(
161: "Failed to resend a request "
162: + "after a security challenge!",
163: exc));
164: }
165: }
166:
167: synchronized void register(String registrarAddress,
168: int registrarPort, String registrarTransport, int expires)
169: throws CommunicationsException {
170: try {
171: isUnregistering = false;
172: // From
173: FromHeader fromHeader = sipManCallback.getFromHeader(true);
174: Address fromAddress = fromHeader.getAddress();
175: sipManCallback.fireRegistering(fromAddress.toString());
176: // Request URI
177: SipURI requestURI = null;
178: try {
179: requestURI = sipManCallback.addressFactory
180: .createSipURI(null, registrarAddress);
181: } catch (ParseException ex) {
182:
183: throw new CommunicationsException(
184: "Bad registrar address:" + registrarAddress, ex);
185: } catch (NullPointerException ex) {
186: // Do not throw an exc, we should rather silently notify the
187: // user
188: // throw new CommunicationsException("A registrar address was
189: // not specified!", ex);
190: sipManCallback.fireUnregistered(fromAddress.getURI()
191: .toString()
192: + " (registrar not specified)");
193: return;
194: }
195:
196: requestURI.setPort(registrarPort);
197: try {
198: requestURI.setTransportParam(registrarTransport);
199: } catch (ParseException ex) {
200: throw new CommunicationsException(registrarTransport
201: + " is not a valid transport!", ex);
202: }
203: // Call ID Header
204: CallIdHeader callIdHeader = sipManCallback.sipProvider
205: .getNewCallId();
206: // CSeq Header
207: CSeqHeader cSeqHeader = null;
208: try {
209: cSeqHeader = sipManCallback.headerFactory
210: .createCSeqHeader(1, Request.REGISTER);
211: } catch (ParseException ex) {
212: // Should never happen
213:
214: Log.error("register", ex);
215:
216: } catch (InvalidArgumentException ex) {
217: // Should never happen
218:
219: Log.error("register", ex);
220:
221: }
222: // To Header
223: ToHeader toHeader = null;
224: try {
225: toHeader = sipManCallback.headerFactory.createToHeader(
226: fromAddress, null);
227: } catch (ParseException ex) {
228: // throw was missing - reported by Eero Vaarnas
229: throw new CommunicationsException(
230: "Could not create a To header "
231: + "for address:"
232: + fromHeader.getAddress(), ex);
233: }
234: // User Agent Header
235: UserAgentHeader uaHeader = null;
236: ArrayList userAgentList = new ArrayList();
237: userAgentList.add(SIPConfig.getStackName());
238:
239: try {
240: uaHeader = sipManCallback.headerFactory
241: .createUserAgentHeader(userAgentList);
242: } catch (ParseException ex) {
243: // throw was missing - reported by Eero Vaarnas
244: throw new CommunicationsException(
245: "Could not create a To header "
246: + "for address:"
247: + fromHeader.getAddress(), ex);
248: }
249: // Via Headers
250: ArrayList viaHeaders = sipManCallback.getLocalViaHeaders();
251: // MaxForwardsHeader
252: MaxForwardsHeader maxForwardsHeader = sipManCallback
253: .getMaxForwardsHeader();
254: // Request
255: Request request = null;
256: try {
257: request = sipManCallback.messageFactory.createRequest(
258: requestURI, Request.REGISTER, callIdHeader,
259: cSeqHeader, fromHeader, toHeader, viaHeaders,
260: maxForwardsHeader);
261: request.setHeader(uaHeader);
262: } catch (ParseException ex) {
263:
264: // throw was missing - reported by Eero Vaarnas
265: throw new CommunicationsException(
266: "Could not create the register request!", ex);
267: }
268: // Expires Header
269: ExpiresHeader expHeader = null;
270: for (int retry = 0; retry < 2; retry++) {
271: try {
272: expHeader = sipManCallback.headerFactory
273: .createExpiresHeader(expires);
274: } catch (InvalidArgumentException ex) {
275: if (retry == 0) {
276: expires = 3600;
277: continue;
278: }
279: throw new CommunicationsException(
280: "Invalid registrations expiration parameter - "
281: + expires, ex);
282: }
283: }
284: request.addHeader(expHeader);
285: // Contact Header should contain IP - bug report - Eero Vaarnas
286: ContactHeader contactHeader = sipManCallback
287: .getRegistrationContactHeader();
288: request.addHeader(contactHeader);
289: // Transaction
290: ClientTransaction regTrans = null;
291: try {
292: regTrans = sipManCallback.sipProvider
293: .getNewClientTransaction(request);
294: } catch (TransactionUnavailableException ex) {
295: // throw was missing - reported by Eero Vaarnas
296: throw new CommunicationsException(
297: "Could not create a register transaction!\n"
298: + "Check that the Registrar address is correct!");
299: }
300: try {
301: regTrans.sendRequest();
302: }
303: // we sometimes get a null pointer exception here so catch them all
304: catch (Exception ex) {
305:
306: // throw was missing - reported by Eero Vaarnas
307: throw new CommunicationsException(
308: "Could not send out the register request!", ex);
309: }
310: this .registerRequest = request;
311: } catch (Exception e) {
312: Log.error("register", e);
313: }
314: }
315:
316: /**
317: * Synchronize because of timer tasks
318: *
319: * @throws CommunicationsException
320: */
321: synchronized void unregister() throws CommunicationsException {
322: try {
323:
324: Log.debug("UNREGISTER");
325:
326: cancelPendingRegistrations();
327: isRegistered = false;
328: isUnregistering = true;
329:
330: Request registerRequest = getRegisterRequest();
331: if (this .registerRequest == null) {
332:
333: throw new CommunicationsException(
334: "Couldn't find the initial register request");
335: }
336: Request unregisterRequest = (Request) registerRequest
337: .clone();
338: try {
339: unregisterRequest.getExpires().setExpires(0);
340: CSeqHeader cSeqHeader = (CSeqHeader) unregisterRequest
341: .getHeader(CSeqHeader.NAME);
342: // [issue 1] - increment registration cseq number
343: // reported by - Roberto Tealdi <roby.tea@tin.it>
344: cSeqHeader.setSequenceNumber(cSeqHeader
345: .getSequenceNumber() + 1);
346:
347: } catch (InvalidArgumentException ex) {
348:
349: // Shouldn't happen
350: throw new CommunicationsException(
351: "Unable to set Expires Header", ex);
352: }
353: ClientTransaction unregisterTransaction = null;
354: try {
355: unregisterTransaction = sipManCallback.sipProvider
356: .getNewClientTransaction(unregisterRequest);
357: } catch (TransactionUnavailableException ex) {
358:
359: throw new CommunicationsException(
360: "Unable to create a unregister transaction", ex);
361: }
362: try {
363: sipManCallback
364: .fireUnregistering(sipManCallback.currentlyUsedURI);
365: unregisterTransaction.sendRequest();
366: } catch (SipException ex) {
367:
368: throw new CommunicationsException(
369: "Failed to send unregister request", ex);
370: }
371: } catch (Exception e) {
372:
373: Log.error("unregister", e);
374:
375: }
376: }
377:
378: public void cancelSchedules() {
379:
380: if (reRegisterTimer != null)
381: reRegisterTimer.cancel();
382: if (keepAliveTimer != null)
383: keepAliveTimer.cancel();
384:
385: reRegisterTimer = null;
386: keepAliveTimer = null;
387:
388: }
389:
390: /**
391: * @return
392: */
393: boolean isRegistered() {
394: return isRegistered;
395: }
396:
397: /**
398: * @return Returns the registerRequest.
399: */
400: private Request getRegisterRequest() {
401: return registerRequest;
402: }
403:
404: private class ReRegisterTask extends TimerTask {
405: String registrarAddress = null;
406:
407: int registrarPort = -1;
408:
409: String transport = null;
410:
411: int expires = 0;
412:
413: public ReRegisterTask(String registrarAddress,
414: int registrarPort, String registrarTransport,
415: int expires) {
416: this .registrarAddress = registrarAddress;
417: this .registrarPort = registrarPort;
418:
419: // don't do this.transport = transport ;)
420: // bug report and fix by Willem Romijn (romijn at lucent.com)
421: this .transport = registrarTransport;
422: this .expires = expires;
423: }
424:
425: public void run() {
426: try {
427: if (isRegistered())
428: register(registrarAddress, registrarPort,
429: transport, expires);
430: } catch (CommunicationsException ex) {
431: sipManCallback
432: .fireCommunicationsError(new CommunicationsException(
433: "Failed to reRegister", ex));
434: }
435: }
436: }
437:
438: private void cancelPendingRegistrations() {
439: try {
440:
441: if (reRegisterTimer != null)
442: reRegisterTimer.cancel();
443: if (keepAliveTimer != null)
444: keepAliveTimer.cancel();
445:
446: reRegisterTimer = null;
447: keepAliveTimer = null;
448:
449: // reRegisterTimer = new Timer();
450: // keepAliveTimer = new Timer();
451:
452: } catch (Exception e) {
453: Log.error("cancelPendingRegistrations", e);
454: }
455: }
456:
457: private void scheduleReRegistration(String registrarAddress,
458: int registrarPort, String registrarTransport, int expires) {
459:
460: ReRegisterTask reRegisterTask = new ReRegisterTask(
461: registrarAddress, registrarPort, registrarTransport,
462: expires);
463:
464: // java.util.Timer thinks in miliseconds
465: // bug report and fix by Willem Romijn (romijn at lucent.com)
466: // We keep a margin of 10% when sending re-registrations (1000
467: // becomes 900)
468: expires = expires * 900;
469:
470: reRegisterTimer.schedule(reRegisterTask, expires);
471:
472: }
473: }
|