/[resiprocate]/main/sip/resiprocate/dum/DialogUsageManager.cxx
ViewVC logotype

Contents of /main/sip/resiprocate/dum/DialogUsageManager.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2997 - (show annotations) (download)
Wed Jun 16 01:08:25 2004 UTC (15 years, 7 months ago) by derek
File size: 22903 byte(s)
Basic call works w/out crash on exit now.  Added BYE logic to Dialog.
Dialog still needs work.
1 #include "resiprocate/Helper.hxx"
2 #include "resiprocate/SipMessage.hxx"
3 #include "resiprocate/SipStack.hxx"
4 #include "resiprocate/dum/AppDialog.hxx"
5 #include "resiprocate/dum/AppDialogSet.hxx"
6 #include "resiprocate/dum/AppDialogSetFactory.hxx"
7 #include "resiprocate/dum/BaseUsage.hxx"
8 #include "resiprocate/dum/ClientAuthManager.hxx"
9 #include "resiprocate/dum/Dialog.hxx"
10 #include "resiprocate/dum/DialogUsageManager.hxx"
11 #include "resiprocate/dum/InviteSessionCreator.hxx"
12 #include "resiprocate/dum/ClientInviteSession.hxx"
13 #include "resiprocate/dum/ClientPublication.hxx"
14 #include "resiprocate/dum/ClientSubscription.hxx"
15 #include "resiprocate/dum/ClientOutOfDialogReq.hxx"
16 #include "resiprocate/dum/ClientRegistration.hxx"
17 #include "resiprocate/dum/DumShutdownHandler.hxx"
18 #include "resiprocate/dum/ServerInviteSession.hxx"
19 #include "resiprocate/dum/Profile.hxx"
20 #include "resiprocate/dum/PublicationCreator.hxx"
21 #include "resiprocate/dum/RegistrationCreator.hxx"
22 #include "resiprocate/dum/ServerAuthManager.hxx"
23 #include "resiprocate/dum/SubscriptionCreator.hxx"
24 #include "resiprocate/os/Inserter.hxx"
25 #include "resiprocate/os/Logger.hxx"
26
27 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
28
29 using namespace resip;
30
31 DialogUsageManager::DialogUsageManager(SipStack& stack) :
32 mProfile(0),
33 mRedirectManager(0),
34 mClientAuthManager(0),
35 mServerAuthManager(0),
36 mInviteSessionHandler(0),
37 mClientRegistrationHandler(0),
38 mServerRegistrationHandler(0),
39 mAppDialogSetFactory(new AppDialogSetFactory()),
40 mStack(stack),
41 mDumShutdownHandler(0)
42 {
43 }
44
45 DialogUsageManager::~DialogUsageManager()
46 {
47 DebugLog ( << "~DialogUsageManager" );
48 // !jf! iterate through dialogsets and dispose, this will cause them to be
49 // removed from HandleManager on destruction
50 // !dcm! -- figure out what this means when coherent
51
52 while(!mDialogSetMap.empty())
53 {
54 delete mDialogSetMap.begin()->second;
55 }
56 if (mDumShutdownHandler)
57 {
58 mDumShutdownHandler->dumDestroyed();
59 }
60 }
61
62 void DialogUsageManager::shutdown(DumShutdownHandler* h)
63 {
64 mDumShutdownHandler = h;
65 }
66
67
68 void DialogUsageManager::setAppDialogSetFactory(AppDialogSetFactory* factory)
69 {
70 delete mAppDialogSetFactory;
71 mAppDialogSetFactory = factory;
72 }
73
74 #if 0
75 AppDialogSet*
76 DialogUsageManager::createAppDialogSet()
77 {
78 return new AppDialogSet(*this);
79 }
80
81 AppDialog*
82 DialogUsageManager::createAppDialog()
83 {
84 return new AppDialog(*this);
85 }
86
87 ClientRegistration*
88 DialogUsageManager::createAppClientRegistration(Dialog& dialog, BaseCreator& creator)
89 {
90 return new ClientRegistration(*this, dialog, creator.getLastRequest());
91 }
92
93 ClientInviteSession*
94 DialogUsageManager::createAppClientInviteSession(Dialog& dialog, const InviteSessionCreator& creator)
95 {
96 return new ClientInviteSession(*this, dialog, creator.getLastRequest(), creator.getInitialOffer());
97 }
98
99 ClientSubscription*
100 DialogUsageManager::createAppClientSubscription(Dialog& dialog, BaseCreator& creator)
101 {
102 return new ClientSubscription(*this, dialog, creator.getLastRequest());
103 }
104
105 ClientOutOfDialogReq*
106 DialogUsageManager::createAppClientOutOfDialogRequest(Dialog& dialog, BaseCreator& creator)
107 {
108 return new ClientOutOfDialogReq(*this, dialog, creator.getLastRequest());
109 }
110
111 ClientPublication*
112 DialogUsageManager::createAppClientPublication(Dialog& dialog, BaseCreator& creator)
113 {
114 return new ClientPublication(*this, dialog, creator.getLastRequest());
115 }
116
117 ServerInviteSession*
118 DialogUsageManager::createAppServerInviteSession()
119 {
120 return 0;
121 }
122
123 ServerSubscription*
124 DialogUsageManager::createAppServerSubscription()
125 {
126 return 0;
127 }
128
129 ServerPublication*
130 DialogUsageManager::createAppServerPublication()
131 {
132 return 0;
133 }
134
135 ServerRegistration*
136 DialogUsageManager::createAppServerRegistration()
137 {
138 return 0;
139 }
140
141 ServerOutOfDialogReq*
142 DialogUsageManager::createAppServerOutOfDialogRequest()
143 {
144 return 0;
145 }
146 #endif
147
148
149 Profile*
150 DialogUsageManager::getProfile()
151 {
152 return mProfile;
153 }
154
155 void DialogUsageManager::setProfile(Profile* profile)
156 {
157 mProfile = profile;
158 }
159
160
161 void DialogUsageManager::setRedirectManager(RedirectManager* manager)
162 {
163 assert(!mRedirectManager);
164 mRedirectManager = manager;
165 }
166
167 void
168 DialogUsageManager::setClientAuthManager(ClientAuthManager* manager)
169 {
170 assert(!mClientAuthManager);
171 mClientAuthManager = manager;
172 }
173
174 void
175 DialogUsageManager::setServerAuthManager(ServerAuthManager* manager)
176 {
177 assert(!mServerAuthManager);
178 mServerAuthManager = manager;
179 }
180
181 void
182 DialogUsageManager::setClientRegistrationHandler(ClientRegistrationHandler* handler)
183 {
184 assert(!mClientRegistrationHandler);
185 mClientRegistrationHandler = handler;
186 }
187
188 void
189 DialogUsageManager::setServerRegistrationHandler(ServerRegistrationHandler* handler)
190 {
191 assert(!mServerRegistrationHandler);
192 mServerRegistrationHandler = handler;
193 }
194
195 void
196 DialogUsageManager::setInviteSessionHandler(InviteSessionHandler* handler)
197 {
198 assert(!mInviteSessionHandler);
199 mInviteSessionHandler = handler;
200 }
201
202 void
203 DialogUsageManager::addTimer(DumTimeout::Type type, unsigned long duration,
204 BaseUsageHandle target, int cseq, int rseq)
205 {
206 DumTimeout t(type, duration, target, cseq, rseq);
207 mStack.post(t, duration);
208 }
209
210 void
211 DialogUsageManager::addClientSubscriptionHandler(const Data& eventType, ClientSubscriptionHandler* handler)
212 {
213 assert(handler);
214 assert(mClientSubscriptionHandler.count(eventType) == 0);
215 mClientSubscriptionHandler[eventType] = handler;
216 }
217
218 void
219 DialogUsageManager::addServerSubscriptionHandler(const Data& eventType, ServerSubscriptionHandler* handler)
220 {
221 assert(handler);
222 assert(mServerSubscriptionHandler.count(eventType) == 0);
223 mServerSubscriptionHandler[eventType] = handler;
224 }
225
226 void
227 DialogUsageManager::addClientPublicationHandler(const Data& eventType, ClientPublicationHandler* handler)
228 {
229 assert(handler);
230 assert(mClientPublicationHandler.count(eventType) == 0);
231 mClientPublicationHandler[eventType] = handler;
232 }
233
234 void
235 DialogUsageManager::addServerPublicationHandler(const Data& eventType, ServerPublicationHandler* handler)
236 {
237 assert(handler);
238 assert(mServerPublicationHandler.count(eventType) == 0);
239 mServerPublicationHandler[eventType] = handler;
240 }
241
242 void
243 DialogUsageManager::addOutOfDialogHandler(MethodTypes& type, OutOfDialogHandler* handler)
244 {
245 assert(handler);
246 assert(mOutOfDialogHandler.count(type) == 0);
247 mOutOfDialogHandler[type] = handler;
248 }
249
250 SipMessage&
251 DialogUsageManager::makeNewSession(BaseCreator* creator, AppDialogSet* appDs)
252 {
253 InfoLog (<< "DialogUsageManager::makeNewSession" );
254 if (appDs == 0)
255 {
256 appDs = new AppDialogSet(*this);
257 }
258 prepareInitialRequest(creator->getLastRequest());
259 DialogSet* ds = new DialogSet(creator, *this);
260
261 appDs->mDialogSetId = ds->getId();
262 ds->mAppDialogSet = appDs;
263
264 InfoLog ( << "************* Adding DialogSet ***************" );
265 InfoLog ( << "Before: " << Inserter(mDialogSetMap) );
266 mDialogSetMap[ds->getId()] = ds;
267 InfoLog ( << "After: " << Inserter(mDialogSetMap) );
268
269
270 DebugLog (<< "Creator: " << creator->getLastRequest());
271 return creator->getLastRequest();
272 }
273
274 void
275 DialogUsageManager::makeResponse(SipMessage& response,
276 const SipMessage& request,
277 int responseCode,
278 const Data& reason) const
279 {
280 assert(request.isRequest());
281 Helper::makeResponse(response, request, responseCode, reason);
282 }
283
284 void
285 DialogUsageManager::sendResponse(SipMessage& response)
286 {
287 assert(response.isResponse());
288 mStack.send(response);
289 }
290
291
292 SipMessage&
293 DialogUsageManager::makeInviteSession(const Uri& target, const SdpContents* initialOffer, AppDialogSet* appDs)
294 {
295 SipMessage& inv = makeNewSession(new InviteSessionCreator(*this, target, initialOffer), appDs);
296 inv.header(h_RequestLine).uri() = target;
297 return inv;
298 }
299
300 SipMessage&
301 DialogUsageManager::makeSubscription(const Uri& aor, const NameAddr& target, const Data& eventType, AppDialogSet* appDs)
302 {
303 return makeNewSession(new SubscriptionCreator(*this, target, eventType), appDs);
304 }
305
306 SipMessage&
307 DialogUsageManager::makeRegistration(const NameAddr& aor, AppDialogSet* appDs)
308 {
309 return makeNewSession(new RegistrationCreator(*this, aor), appDs);
310 }
311
312 SipMessage&
313 DialogUsageManager::makePublication(const Uri& targetDocument,
314 const Contents& body,
315 const Data& eventType,
316 unsigned expiresSeconds,
317 AppDialogSet* appDs)
318 {
319 return makeNewSession(new PublicationCreator(*this, targetDocument, body, eventType, expiresSeconds), appDs);
320 }
321
322
323 void
324 DialogUsageManager::send(SipMessage& msg)
325 {
326 InfoLog (<< "SEND: " << msg.brief());
327 if (msg.isRequest()) //!dcm! -- invariant?
328 {
329 //will have no affect unless a strict route is sent
330 Helper::processStrictRoute(msg);
331
332 if (getProfile()->hasOutboundProxy())
333 {
334 DebugLog ( << "Using outbound proxy");
335 mStack.sendTo(msg, getProfile()->getOutboundProxy().uri());
336 }
337 else
338 {
339 mStack.send(msg);
340 }
341 }
342 else
343 {
344 sendResponse(msg);
345 }
346 }
347
348 void
349 DialogUsageManager::cancel(DialogSetId setid)
350 {
351 DialogSet* ds = findDialogSet(setid);
352 if (ds == 0)
353 {
354 throw Exception("Request no longer exists", __FILE__, __LINE__);
355 }
356 else
357 {
358 ds->cancel();
359 }
360 }
361
362
363 // !jf! maybe this should just be a handler that the application can provide
364 // (one or more of) to futz with the request before it goes out
365 void
366 DialogUsageManager::prepareInitialRequest(SipMessage& request)
367 {
368 // !jf!
369 //request.header(h_Supporteds) = mProfile->getSupportedOptionTags();
370 //request.header(h_Allows) = mProfile->getAllowedMethods();
371 }
372
373 void
374 DialogUsageManager::buildFdSet(FdSet& fdset)
375 {
376 mStack.buildFdSet(fdset);
377 }
378
379 int
380 DialogUsageManager::getTimeTillNextProcessMS()
381 {
382 return mStack.getTimeTillNextProcessMS();
383 }
384
385 void
386 DialogUsageManager::process(FdSet& fdset)
387 {
388 mStack.process(fdset);
389 std::auto_ptr<Message> msg( mStack.receiveAny() );
390 SipMessage* sipMsg = dynamic_cast<SipMessage*>(msg.get());
391 if (!msg.get()) return;
392 if (sipMsg)
393 {
394 InfoLog ( << "DialogUsageManager::process: " << sipMsg->brief());
395 if (sipMsg->isRequest())
396 {
397
398 // if( !validateRequest(*sipMsg) )
399 // {
400 // InfoLog (<< "Failed request validation " << *sipMsg);
401 // return;
402 // }
403 // if ( !validateTo(*sipMsg) )
404 // {
405 // InfoLog (<< "Failed to validation " << *sipMsg);
406 // return;
407 // }
408 if (sipMsg->header(h_From).exists(p_tag))
409 {
410 if (mergeRequest(*sipMsg) )
411 {
412 InfoLog (<< "Merged request: " << *sipMsg);
413 return;
414 }
415 }
416
417 if ( mServerAuthManager )
418 {
419 if ( mServerAuthManager->handle(*sipMsg) )
420 {
421 return;
422 }
423 }
424 processRequest(*sipMsg);
425 }
426 else if (sipMsg->isResponse())
427 {
428 processResponse(*sipMsg);
429 }
430 return;
431 }
432
433 DumTimeout* dumMsg = dynamic_cast<DumTimeout*>(msg.get());
434 if (dumMsg )
435 {
436 if ( !dumMsg->getBaseUsage().isValid())
437 {
438 return;
439 }
440
441 dumMsg->getBaseUsage()->dispatch(*dumMsg);
442 return;
443 }
444
445 ErrLog(<<"Unknown message received." << msg->brief());
446 assert(0);
447 }
448
449 bool
450 DialogUsageManager::validateRequest(const SipMessage& request)
451 {
452 if (!mProfile->isSchemeSupported(request.header(h_RequestLine).uri().scheme()))
453 {
454 InfoLog (<< "Received an unsupported scheme: " << request.brief());
455 SipMessage failure;
456 makeResponse(failure, request, 416);
457 sendResponse(failure);
458 }
459 else if (!mProfile->isMethodSupported(request.header(h_RequestLine).getMethod()))
460 {
461 InfoLog (<< "Received an unsupported method: " << request.brief());
462
463 SipMessage failure;
464 makeResponse(failure, request, 405);
465 failure.header(h_Allows) = mProfile->getAllowedMethods();
466 sendResponse(failure);
467
468 return false;
469 }
470 else
471 {
472 Tokens unsupported = mProfile->isSupported(request.header(h_Requires));
473 if (!unsupported.empty())
474 {
475 InfoLog (<< "Received an unsupported option tag(s): " << request.brief());
476
477 SipMessage failure;
478 makeResponse(failure, request, 420);
479 failure.header(h_Unsupporteds) = unsupported;
480 sendResponse(failure);
481
482 return false;
483 }
484 else if (request.exists(h_ContentDisposition))
485 {
486 if (request.header(h_ContentDisposition).exists(p_handling) &&
487 isEqualNoCase(request.header(h_ContentDisposition).param(p_handling), Symbols::Required))
488 {
489 if (!mProfile->isMimeTypeSupported(request.header(h_ContentType)))
490 {
491 InfoLog (<< "Received an unsupported mime type: " << request.brief());
492
493 SipMessage failure;
494 makeResponse(failure, request, 415);
495 failure.header(h_Accepts) = mProfile->getSupportedMimeTypes();
496 sendResponse(failure);
497
498 return false;
499 }
500 else if (!mProfile->isContentEncodingSupported(request.header(h_ContentEncoding)))
501 {
502 InfoLog (<< "Received an unsupported mime type: " << request.brief());
503 SipMessage failure;
504 makeResponse(failure, request, 415);
505 failure.header(h_AcceptEncodings) = mProfile->getSupportedEncodings();
506 sendResponse(failure);
507 }
508 else if (!mProfile->isLanguageSupported(request.header(h_ContentLanguages)))
509 {
510 InfoLog (<< "Received an unsupported language: " << request.brief());
511
512 SipMessage failure;
513 makeResponse(failure, request, 415);
514 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
515 sendResponse(failure);
516 }
517 }
518 }
519 }
520
521 return true;
522 }
523
524
525 bool
526 DialogUsageManager::validateTo(const SipMessage& request)
527 {
528 // !jf! check that the request is targeted at me!
529 // will require support in profile
530 // This should check the Request-Uri (not the To)
531 return true;
532 }
533
534 bool
535 DialogUsageManager::mergeRequest(const SipMessage& request)
536 {
537 assert(request.isRequest());
538 assert(request.isExternal());
539
540 if (!request.header(h_To).exists(p_tag))
541 {
542 if (mMergedRequests.count(MergedRequestKey(request)))
543 {
544 SipMessage failure;
545 makeResponse(failure, request, 482, "Merged Request");
546 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
547 sendResponse(failure);
548 return true;
549 }
550 }
551
552 return false;
553 }
554
555 void
556 DialogUsageManager::processRequest(const SipMessage& request)
557 {
558 InfoLog ( << "DialogUsageManager::processRequest: " << request.brief());
559
560 assert(mAppDialogSetFactory);
561 if (!request.header(h_To).exists(p_tag))
562 {
563 switch (request.header(h_RequestLine).getMethod())
564 {
565 case ACK:
566 DebugLog (<< "Discarding request: " << request.brief());
567 break;
568
569 case PRACK:
570 case BYE:
571 case UPDATE:
572 //case INFO: // !rm! in an ideal world
573 //case NOTIFY: // !rm! in an ideal world
574 {
575 SipMessage failure;
576 makeResponse(failure, request, 481);
577 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
578 sendResponse(failure);
579 break;
580 }
581 case CANCEL:
582 {
583 // find the appropropriate ServerInvSession
584 DialogSet* ds = findDialogSet(DialogSetId(request));
585 if (ds == 0)
586 {
587 InfoLog (<< "Received a CANCEL on a non-existent transaction ");
588 SipMessage failure;
589 makeResponse(failure, request, 481);
590 sendResponse(failure);
591 }
592 else
593 {
594 ds->cancel();
595 }
596 break;
597 }
598
599 case INVITE: // new INVITE
600 case SUBSCRIBE:
601 case REFER: // out-of-dialog REFER
602 case PUBLISH:
603 case INFO : // handle non-dialog (illegal) INFOs
604 case NOTIFY : // handle unsolicited (illegal) NOTIFYs
605 {
606 {
607 DialogSetId id(request);
608 //cryptographically dangerous
609 assert(mDialogSetMap.find(id) == mDialogSetMap.end());
610 }
611 try
612 {
613 DialogSet* dset = new DialogSet(request, *this);
614
615 AppDialogSet* appDs = mAppDialogSetFactory->createAppDialogSet(*this, request);
616 appDs->mDialogSetId = dset->getId();
617 dset->mAppDialogSet = appDs;
618
619 InfoLog ( << "************* Adding DialogSet ***************" );
620 InfoLog ( << "Before: " << Inserter(mDialogSetMap) );
621 mDialogSetMap[dset->getId()] = dset;
622 InfoLog ( << "After: " << Inserter(mDialogSetMap) );
623
624 dset->dispatch(request);
625 }
626 catch (BaseException& e)
627 {
628 SipMessage failure;
629 makeResponse(failure, request, 400, e.getMessage());
630 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
631 sendResponse(failure);
632 }
633
634 break;
635 }
636 case REGISTER:
637 case MESSAGE :
638 case OPTIONS :
639 {
640 SipMessage failure;
641 makeResponse(failure, request, 405);
642 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
643 sendResponse(failure);
644 }
645 break;
646 case RESPONSE:
647 case SERVICE:
648 assert(false);
649 break;
650 case UNKNOWN:
651 case MAX_METHODS:
652 assert(false);
653 break;
654 }
655 }
656 else // in a specific dialog
657 {
658 switch (request.header(h_RequestLine).getMethod())
659 {
660 case REGISTER:
661 {
662 SipMessage failure;
663 makeResponse(failure, request, 400, "rjs says, Go to hell");
664 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
665 sendResponse(failure);
666 break;
667 }
668
669 default:
670 {
671 DialogSet* ds = findDialogSet(DialogSetId(request));
672 if (ds == 0)
673 {
674 SipMessage failure;
675 makeResponse(failure, request, 481);
676 failure.header(h_AcceptLanguages) = mProfile->getSupportedLanguages();
677 sendResponse(failure);
678 }
679 else
680 {
681 ds->dispatch(request);
682 }
683 }
684 }
685 }
686 }
687
688 void
689 DialogUsageManager::processResponse(const SipMessage& response)
690 {
691 InfoLog ( << "DialogUsageManager::processResponse: " << response);
692
693 DialogSet* ds = findDialogSet(DialogSetId(response));
694
695 if (ds)
696 {
697 InfoLog ( << "DialogUsageManager::processResponse: " << response.brief());
698 ds->dispatch(response);
699 }
700 else
701 {
702 InfoLog (<< "Throwing away stray response: " << response.brief());
703 }
704 }
705
706 DialogSet*
707 DialogUsageManager::findDialogSet(const DialogSetId& id)
708 {
709 InfoLog ( << "Looking for dialogSet: " << id << " in map:" );
710 InfoLog ( << Inserter(mDialogSetMap) );
711 DialogSetMap::const_iterator it = mDialogSetMap.find(id);
712
713 if (it == mDialogSetMap.end())
714 {
715 return 0;
716 }
717 else
718 {
719 return it->second;
720 }
721 }
722
723 BaseCreator*
724 DialogUsageManager::findCreator(const DialogId& id)
725 {
726 DialogSet* ds = findDialogSet(id.getDialogSetId());
727 if (ds)
728 {
729 return ds->getCreator();
730 }
731 else
732 {
733 return 0;
734 }
735 }
736
737 void DialogUsageManager::removeDialogSet(const DialogSetId& dsId)
738 {
739 InfoLog ( << "************* Removing DialogSet ***************" );
740 InfoLog ( << "Before: " << Inserter(mDialogSetMap) );
741 mDialogSetMap.erase(dsId);
742 InfoLog ( << "After: " << Inserter(mDialogSetMap) );
743
744 if (mDialogSetMap.empty() && mDumShutdownHandler)
745 {
746 delete this;
747 }
748 }
749
750
751
752
753
754 /* ====================================================================
755 * The Vovida Software License, Version 1.0
756 *
757 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
758 *
759 * Redistribution and use in source and binary forms, with or without
760 * modification, are permitted provided that the following conditions
761 * are met:
762 *
763 * 1. Redistributions of source code must retain the above copyright
764 * notice, this list of conditions and the following disclaimer.
765 *
766 * 2. Redistributions in binary form must reproduce the above copyright
767 * notice, this list of conditions and the following disclaimer in
768 * the documentation and/or other materials provided with the
769 * distribution.
770 *
771 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
772 * and "Vovida Open Communication Application Library (VOCAL)" must
773 * not be used to endorse or promote products derived from this
774 * software without prior written permission. For written
775 * permission, please contact vocal@vovida.org.
776 *
777 * 4. Products derived from this software may not be called "VOCAL", nor
778 * may "VOCAL" appear in their name, without prior written
779 * permission of Vovida Networks, Inc.
780 *
781 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
782 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
783 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
784 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
785 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
786 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
787 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
788 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
789 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
790 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
791 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
792 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
793 * DAMAGE.
794 *
795 * ====================================================================
796 *
797 * This software consists of voluntary contributions made by Vovida
798 * Networks, Inc. and many individuals on behalf of Vovida Networks,
799 * Inc. For more information on Vovida Networks, Inc., please see
800 * <http://www.vovida.org/>.
801 *
802 */

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27