reSIProcate/repro  9694
Proxy.cxx
Go to the documentation of this file.
00001 #if defined(HAVE_CONFIG_H)
00002 #include "config.h"
00003 #endif
00004 
00005 #include "repro/ProcessorChain.hxx"
00006 #include "repro/Proxy.hxx"
00007 #include "repro/Ack200DoneMessage.hxx"
00008 #include "repro/UserStore.hxx"
00009 #include "repro/Dispatcher.hxx"
00010 
00011 #include "resip/stack/TransactionTerminated.hxx"
00012 #include "resip/stack/ApplicationMessage.hxx"
00013 #include "resip/stack/SipStack.hxx"
00014 #include "resip/stack/Helper.hxx"
00015 #include "resip/stack/InteropHelper.hxx"
00016 #include "rutil/Random.hxx"
00017 #include "rutil/Logger.hxx"
00018 #include "rutil/Inserter.hxx"
00019 #include "rutil/WinLeakCheck.hxx"
00020 
00021 #define RESIPROCATE_SUBSYSTEM resip::Subsystem::REPRO
00022 
00023 using namespace resip;
00024 using namespace repro;
00025 using namespace std;
00026 
00027 // Initialize statics
00028 Data Proxy::FlowTokenSalt;
00029 
00030 // Use Static fn with static local object to ensure KeyValueStoreKeyAllocator is created before
00031 // static calls to allocate new keys in the monkey classes
00032 KeyValueStore::KeyValueStoreKeyAllocator* Proxy::getGlobalKeyValueStoreKeyAllocator()
00033 {
00034    static KeyValueStore::KeyValueStoreKeyAllocator* globalAllocator = new KeyValueStore::KeyValueStoreKeyAllocator();
00035    return globalAllocator;
00036 }
00037 
00038 KeyValueStore::KeyValueStoreKeyAllocator* Proxy::getRequestKeyValueStoreKeyAllocator()
00039 {
00040    static KeyValueStore::KeyValueStoreKeyAllocator* requestAllocator = new KeyValueStore::KeyValueStoreKeyAllocator();
00041    return requestAllocator;
00042 }
00043 
00044 KeyValueStore::KeyValueStoreKeyAllocator* Proxy::getTargetKeyValueStoreKeyAllocator()
00045 {
00046    static KeyValueStore::KeyValueStoreKeyAllocator* targetAllocator = new KeyValueStore::KeyValueStoreKeyAllocator();
00047    return targetAllocator;
00048 }
00049 
00050 KeyValueStore::Key Proxy::allocateGlobalKeyValueStoreKey()
00051 {
00052    return getGlobalKeyValueStoreKeyAllocator()->allocateNewKey();
00053 }
00054 
00055 KeyValueStore::Key Proxy::allocateRequestKeyValueStoreKey()
00056 {
00057    return getRequestKeyValueStoreKeyAllocator()->allocateNewKey();
00058 }
00059 
00060 KeyValueStore::Key Proxy::allocateTargetKeyValueStoreKey()
00061 {
00062    return getTargetKeyValueStoreKeyAllocator()->allocateNewKey();
00063 }
00064 
00065 RequestContext* 
00066 RequestContextFactory::createRequestContext(Proxy& proxy,
00067                                             ProcessorChain& requestP,  // monkeys
00068                                             ProcessorChain& responseP, // lemurs
00069                                             ProcessorChain& targetP)   // baboons
00070 {
00071    return new RequestContext(proxy, requestP, responseP, targetP); 
00072 }
00073 
00074 Proxy::Proxy(SipStack& stack, 
00075              ProxyConfig& config,
00076              ProcessorChain& requestP, 
00077              ProcessorChain& responseP, 
00078              ProcessorChain& targetP) 
00079    : TransactionUser(TransactionUser::RegisterForTransactionTermination),
00080      mStack(stack), 
00081      mConfig(config),
00082      mRecordRoute(config.getConfigUri("RecordRouteUri", Uri())),
00083      mRecordRouteForced(config.getConfigBool("ForceRecordRouting", false)),
00084      mAssumePath(config.getConfigBool("AssumePath", false)),
00085      mPAssertedIdentityProcessing(config.getConfigBool("EnablePAssertedIdentityProcessing", false)),
00086      mServerText(config.getConfigData("ServerText", "")),
00087      mTimerC(config.getConfigInt("TimerC", 180)),
00088      mKeyValueStore(*Proxy::getGlobalKeyValueStoreKeyAllocator()),
00089      mRequestProcessorChain(requestP), 
00090      mResponseProcessorChain(responseP),
00091      mTargetProcessorChain(targetP),
00092      mUserStore(config.getDataStore()->mUserStore),
00093      mOptionsHandler(0),
00094      mRequestContextFactory(new RequestContextFactory)
00095 {
00096    FlowTokenSalt = Random::getCryptoRandom(20);   // 20-octet Crypto Random Key for Salting Flow Token HMACs
00097 
00098    mFifo.setDescription("Proxy::mFifo");
00099 
00100    if(InteropHelper::getOutboundSupported())
00101    {
00102       addSupportedOption("outbound");
00103    }
00104 }
00105 
00106 
00107 Proxy::~Proxy()
00108 {
00109    shutdown();
00110    join();
00111    InfoLog (<< "Proxy::thread shutdown with " << mServerRequestContexts.size() << " ServerRequestContexts and " << mClientRequestContexts.size() << " ClientRequestContexts.");
00112 }
00113 
00114 void 
00115 Proxy::setOptionsHandler(OptionsHandler* handler)
00116 {
00117    mOptionsHandler = handler;
00118 }
00119  
00120 void 
00121 Proxy::setRequestContextFactory(std::auto_ptr<RequestContextFactory> requestContextFactory)
00122 {
00123    mRequestContextFactory = requestContextFactory;
00124 }
00125 
00126 bool
00127 Proxy::isShutDown() const
00128 {
00129   return false;
00130 }
00131 
00132 
00133 UserStore&
00134 Proxy::getUserStore()
00135 {
00136    return mUserStore;
00137 }
00138 
00139 
00140 void
00141 Proxy::thread()
00142 {
00143    InfoLog (<< "Proxy::thread start");
00144 
00145    while (!isShutdown())
00146    {
00147       Message* msg=0;
00148       //DebugLog (<< "TransactionUser::postToTransactionUser " << " &=" << &mFifo << " size=" << mFifo.size());
00149 
00150       try
00151       {
00152          if ((msg = mFifo.getNext(100)) != 0)
00153          {
00154             DebugLog (<< "Got: " << *msg);
00155          
00156             SipMessage* sip = dynamic_cast<SipMessage*>(msg);
00157             ApplicationMessage* app = dynamic_cast<ApplicationMessage*>(msg);
00158             TransactionTerminated* term = dynamic_cast<TransactionTerminated*>(msg);
00159          
00160             if (sip)
00161             {
00162                Data tid(sip->getTransactionId());
00163                tid.lowercase();
00164                if (sip->isRequest())
00165                {
00166                   // Verify that the request has all the mandatory headers
00167                   // (To, From, Call-ID, CSeq)  Via is already checked by stack.  
00168                   // See RFC 3261 Section 16.3 Step 1
00169                   if (!sip->exists(h_To)     ||
00170                       !sip->exists(h_From)   ||
00171                       !sip->exists(h_CallID) ||
00172                       !sip->exists(h_CSeq)     )
00173                   {
00174                      // skip this message and move on to the next one
00175                      delete sip;
00176                      continue;  
00177                   }
00178 
00179                   // The TU selector already checks the URI scheme for us (Sect 16.3, Step 2)
00180                   if(sip->method()==OPTIONS && 
00181                      isMyUri(sip->header(h_RequestLine).uri()))
00182                   {
00183                      if(mOptionsHandler)
00184                      {
00185                         std::auto_ptr<SipMessage> resp(new SipMessage);
00186                         Helper::makeResponse(*resp,*sip,200);
00187                         if(mOptionsHandler->onOptionsRequest(*sip, *resp))
00188                         {
00189                            mStack.send(*resp,this);
00190                            delete sip;
00191                            continue;
00192                         }
00193                      }
00194                      else if(sip->header(h_RequestLine).uri().user().empty())
00195                      {
00196                         std::auto_ptr<SipMessage> resp(new SipMessage);
00197                         Helper::makeResponse(*resp,*sip,200);
00198 
00199                         if(resip::InteropHelper::getOutboundSupported())
00200                         {
00201                            resp->header(h_Supporteds).push_back(Token("outbound"));
00202                         }
00203                         mStack.send(*resp,this);
00204                         delete sip;
00205                         continue;
00206                      }
00207                   }
00208 
00209                   // check the MaxForwards isn't too low
00210                   if (!sip->exists(h_MaxForwards))
00211                   {
00212                      // .bwc. Add Max-Forwards header if not found.
00213                      sip->header(h_MaxForwards).value()=20;
00214                   }
00215                   
00216                   if(!sip->header(h_MaxForwards).isWellFormed())
00217                   {
00218                      //Malformed Max-Forwards! (Maybe we can be lenient and set
00219                      // it to 70...)
00220                      std::auto_ptr<SipMessage> response(Helper::makeResponse(*sip,400));
00221                      response->header(h_StatusLine).reason()="Malformed Max-Forwards";
00222                      mStack.send(*response,this);
00223                      delete sip;
00224                      continue;                     
00225                   }
00226                   
00227                   // .bwc. Unacceptable values for Max-Forwards
00228                   // !bwc! TODO make this ceiling configurable
00229                   if(sip->header(h_MaxForwards).value() > 255)
00230                   {
00231                      sip->header(h_MaxForwards).value() = 20;                     
00232                   }
00233                   else if(sip->header(h_MaxForwards).value() <= 0)
00234                   {
00235                      if (sip->header(h_RequestLine).method() != OPTIONS)
00236                      {
00237                      std::auto_ptr<SipMessage> response(Helper::makeResponse(*sip, 483));
00238                      mStack.send(*response, this);
00239                      }
00240                      else  // If the request is an OPTIONS, send an appropriate response
00241                      {
00242                         std::auto_ptr<SipMessage> response(Helper::makeResponse(*sip, 200));
00243                         mStack.send(*response, this);                        
00244                      }
00245                      // in either case get rid of the request and process the next one
00246                      delete sip;
00247                      continue;
00248                   }
00249 
00250                   if(!sip->empty(h_ProxyRequires))
00251                   {
00252                      std::auto_ptr<SipMessage> response(0);
00253 
00254                      for(Tokens::iterator i=sip->header(h_ProxyRequires).begin();
00255                            i!=sip->header(h_ProxyRequires).end();
00256                            ++i)
00257                      {
00258                         if(!i->isWellFormed() || 
00259                            !mSupportedOptions.count(i->value()) )
00260                         {
00261                            if(!response.get())
00262                            {
00263                               response.reset(Helper::makeResponse(*sip, 420, "Bad extension"));
00264                            }
00265                            response->header(h_Unsupporteds).push_back(*i);
00266                         }
00267                      }
00268 
00269                      if(response.get())
00270                      {
00271                         mStack.send(*response, this);
00272                         delete sip;
00273                         continue;
00274                      }
00275                   }
00276                   
00277                   
00278                   if (sip->method() == CANCEL)
00279                   {
00280                      HashMap<Data,RequestContext*>::iterator i = mServerRequestContexts.find(tid);
00281 
00282                      if(i == mServerRequestContexts.end())
00283                      {
00284                         SipMessage response;
00285                         Helper::makeResponse(response,*sip,481);
00286                         mStack.send(response,this);
00287                         delete sip;
00288                      }
00289                      else
00290                      {
00291                         try
00292                         {
00293                            i->second->process(std::auto_ptr<resip::SipMessage>(sip));
00294                         }
00295                         catch(resip::BaseException& e)
00296                         {
00297                            // .bwc. Some sort of unhandled error in process.
00298                            // This is very bad; we cannot form a response 
00299                            // at this point because we do not know
00300                            // whether the original request still exists.
00301                            ErrLog(<<"Uncaught exception in process on a CANCEL "
00302                                     "request: " << e);
00303                            mStack.abandonServerTransaction(tid);
00304                         }
00305                      }
00306                   }
00307                   else if (sip->method() == ACK)
00308                   {
00309                      // .bwc. This is going to be treated as a new transaction.
00310                      // The stack is maintaining no state whatsoever for this.
00311                      // We should treat this exactly like a new transaction.
00312                      if(sip->mIsBadAck200)
00313                      {
00314                         static Data ack("ack");
00315                         tid+=ack;
00316                      }
00317                      
00318                      RequestContext* context=0;
00319 
00320                      HashMap<Data,RequestContext*>::iterator i = mServerRequestContexts.find(tid);
00321                      
00322                      // .bwc. This might be an ACK/200, or a stray ACK/failure
00323                      if(i == mServerRequestContexts.end())
00324                      {
00325                         context = mRequestContextFactory->createRequestContext(*this, 
00326                                                      mRequestProcessorChain, 
00327                                                      mResponseProcessorChain, 
00328                                                      mTargetProcessorChain);
00329                         mServerRequestContexts[tid] = context;
00330                      }
00331                      else // .bwc. ACK/failure
00332                      {
00333                         context = i->second;
00334                      }
00335 
00336                      // The stack will send TransactionTerminated messages for
00337                      // client and server transaction which will clean up this
00338                      // RequestContext 
00339                      try
00340                      {
00341                         context->process(std::auto_ptr<resip::SipMessage>(sip));
00342                      }
00343                      catch(resip::BaseException& e)
00344                      {
00345                         // .bwc. Some sort of unhandled error in process.
00346                         ErrLog(<<"Uncaught exception in process on an ACK "
00347                                  "request: " << e);
00348                      }
00349                   }
00350                   else
00351                   {
00352                      // This is a new request, so create a Request Context for it
00353                      InfoLog (<< "New RequestContext tid=" << tid << " : " << sip->brief());
00354                      
00355 
00356                      if(mServerRequestContexts.count(tid) == 0)
00357                      {
00358                         RequestContext* context = mRequestContextFactory->createRequestContext(*this,
00359                                                                      mRequestProcessorChain, 
00360                                                                      mResponseProcessorChain, 
00361                                                                      mTargetProcessorChain);
00362                         InfoLog (<< "Inserting new RequestContext tid=" << tid
00363                                   << " -> " << *context);
00364                         mServerRequestContexts[tid] = context;
00365                         DebugLog (<< "RequestContexts: " << InserterP(mServerRequestContexts));
00366                         try
00367                         {
00368                            context->process(std::auto_ptr<resip::SipMessage>(sip));
00369                         }
00370                         catch(resip::BaseException& e)
00371                         {
00372                            // .bwc. Some sort of unhandled error in process.
00373                            // This is very bad; we cannot form a response 
00374                            // at this point because we do not know
00375                            // whether the original request still exists.
00376                            ErrLog(<<"Uncaught exception in process on a new "
00377                                     "request: " << e);
00378                            mStack.abandonServerTransaction(tid);
00379                         }
00380                      }
00381                      else
00382                      {
00383                         InfoLog(<<"Got a new non-ACK request "
00384                         "with an already existing transaction ID. This can "
00385                         "happen if a new request collides with a previously "
00386                         "received ACK/200.");
00387                         SipMessage response;
00388                         Helper::makeResponse(response,*sip,400,"Transaction-id "
00389                                                          "collision");
00390                         mStack.send(response,this);
00391                         delete sip;
00392                      }
00393                   }
00394                }
00395                else if (sip->isResponse())
00396                {
00397                   InfoLog (<< "Looking up RequestContext tid=" << tid);
00398                
00399                   // TODO  is there a problem with a stray 200?
00400                   HashMap<Data,RequestContext*>::iterator i = mClientRequestContexts.find(tid);
00401                   if (i != mClientRequestContexts.end())
00402                   {
00403                      try
00404                      {
00405                         i->second->process(std::auto_ptr<resip::SipMessage>(sip));
00406                      }
00407                      catch(resip::BaseException& e)
00408                      {
00409                         // .bwc. Some sort of unhandled error in process.
00410                         ErrLog(<<"Uncaught exception in process on a response: " << e);
00411                      }
00412                   }
00413                   else
00414                   {
00415                      // throw away stray responses
00416                      InfoLog (<< "Unmatched response (stray?) : " << endl << *msg);
00417                      delete sip;  
00418                   }
00419                }
00420             }
00421             else if (app)
00422             {
00423                Data tid(app->getTransactionId());
00424                tid.lowercase();
00425                DebugLog(<< "Trying to dispatch : " << *app );
00426                HashMap<Data,RequestContext*>::iterator i=mServerRequestContexts.find(tid);
00427                // the underlying RequestContext may not exist
00428                if (i != mServerRequestContexts.end())
00429                {
00430                   DebugLog(<< "Sending " << *app << " to " << *(i->second));
00431                   // This goes in as a Message and not an ApplicationMessage
00432                   // so that we have one peice of code doing dispatch to Monkeys
00433                   // (the intent is that Monkeys may eventually handle non-SIP
00434                   //  application messages).
00435                   bool eraseThisTid =  (dynamic_cast<Ack200DoneMessage*>(app)!=0);
00436                   try
00437                   {
00438                      i->second->process(std::auto_ptr<resip::ApplicationMessage>(app));
00439                   }
00440                   catch(resip::BaseException& e)
00441                   {
00442                      ErrLog(<<"Uncaught exception in process: " << e);
00443                   }
00444                   
00445                   if (eraseThisTid)
00446                   {
00447                      mServerRequestContexts.erase(i);
00448                   }
00449                }
00450                else
00451                {
00452                   InfoLog (<< "No matching request context...ignoring " << *app);
00453                   delete app;
00454                }
00455             }
00456             else if (term)
00457             {
00458                Data tid(term->getTransactionId());
00459                tid.lowercase();
00460                if (term->isClientTransaction())
00461                {
00462                   HashMap<Data,RequestContext*>::iterator i=mClientRequestContexts.find(tid);
00463                   if (i != mClientRequestContexts.end())
00464                   {
00465                      try
00466                      {
00467                         i->second->process(*term);
00468                      }
00469                      catch(resip::BaseException& e)
00470                      {
00471                         ErrLog(<<"Uncaught exception in process: " << e);
00472                      }
00473                      mClientRequestContexts.erase(i);
00474                   }
00475                   else
00476                   {
00477                      InfoLog (<< "No matching request context...ignoring " << *term);
00478                   }
00479                }
00480                else 
00481                {
00482                   HashMap<Data,RequestContext*>::iterator i=mServerRequestContexts.find(tid);
00483                   if (i != mServerRequestContexts.end())
00484                   {
00485                      try
00486                      {
00487                         i->second->process(*term);
00488                      }
00489                      catch(resip::BaseException& e)
00490                      {
00491                         ErrLog(<<"Uncaught exception in process: " << e);
00492                      }
00493                      mServerRequestContexts.erase(i);
00494                   }
00495                   else
00496                   {
00497                      InfoLog (<< "No matching request context...ignoring " << *term);
00498                   }
00499                }
00500                delete term;
00501             }
00502          }
00503       }
00504       catch (BaseException& e)
00505       {
00506          ErrLog (<< "Caught: " << e);
00507       }
00508       catch (...)
00509       {
00510          ErrLog (<< "Caught unknown exception");
00511       }
00512    }
00513    InfoLog (<< "Proxy::thread exit");
00514 }
00515 
00516 void
00517 Proxy::send(const SipMessage& msg) 
00518 {
00519    mStack.send(msg, this);
00520 }
00521 
00522 void
00523 Proxy::addClientTransaction(const Data& transactionId, RequestContext* rc)
00524 {
00525    if(mClientRequestContexts.count(transactionId) == 0)
00526    {
00527       InfoLog (<< "add client transaction tid=" << transactionId << " " << rc);
00528       mClientRequestContexts[transactionId] = rc;
00529    }
00530    else
00531    {
00532       ErrLog(<< "Received a client request context whose transaction id matches that of an existing request context. Ignoring.");
00533    }
00534 }
00535 
00536 void
00537 Proxy::postTimerC(std::auto_ptr<TimerCMessage> tc)
00538 {
00539    if(mTimerC > 0)
00540    {
00541       InfoLog(<<"Posting timer C");
00542       mStack.post(*tc,mTimerC,this);
00543    }
00544 }
00545 
00546 
00547 void
00548 Proxy::postMS(std::auto_ptr<resip::ApplicationMessage> msg, int msec)
00549 {
00550    mStack.postMS(*msg,msec,this);
00551 }
00552 
00553 
00554 const Data& 
00555 Proxy::name() const
00556 {
00557    static Data n("Proxy");
00558    return n;
00559 }
00560 
00561 bool 
00562 Proxy::isMyUri(const Uri& uri) const
00563 {
00564    bool ret = mStack.isMyDomain(uri.host(), uri.port());
00565    if(!ret)
00566    {
00567       ret = isMyDomain(uri.host());
00568 
00569       if(ret) 
00570       {
00571          // check if we are listening on the specified port
00572          // .slg. this is not perfect, but it will allow us to operate in most environments
00573          //       where the repro proxy and a UA are running on the same machine.
00574          //       Note:  There is a scenario that we cannot correctly handle - when a UA and 
00575          //       repro are running on the same machine, and they are using the same port but on 
00576          //       different transports types or interfaces.  In this case we cannot tell, by looking
00577          //       at a requestUri or From header if the uri is ours or the UA's, and things will break.
00578          if(uri.port() != 0)
00579          {
00580             ret = mStack.isMyPort(uri.port());
00581          }
00582       }
00583    }
00584    DebugLog( << "Proxy::isMyUri " << uri << " " << ret);
00585    return ret;
00586 }
00587 
00588 const resip::NameAddr& 
00589 Proxy::getRecordRoute(const Transport* transport) const
00590 {
00591    assert(transport);
00592    if(transport->hasRecordRoute())
00593    {
00594       // Transport specific record-route found
00595       return transport->getRecordRoute();
00596    }
00597    return mRecordRoute;
00598 }
00599 
00600 bool
00601 Proxy::getRecordRouteForced() const
00602 {
00603    return mRecordRouteForced;
00604 }
00605 
00606 bool 
00607 Proxy::compressionEnabled() const
00608 {
00609    return mStack.getCompression().getAlgorithm() != resip::Compression::NONE;
00610 }
00611 
00612 void 
00613 Proxy::addSupportedOption(const resip::Data& option)
00614 {
00615    mSupportedOptions.insert(option);
00616 }
00617 
00618 void 
00619 Proxy::removeSupportedOption(const resip::Data& option)
00620 {
00621    mSupportedOptions.erase(option);
00622 }
00623 
00624 
00625 
00626 /* ====================================================================
00627  * The Vovida Software License, Version 1.0 
00628  * 
00629  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00630  * 
00631  * Redistribution and use in source and binary forms, with or without
00632  * modification, are permitted provided that the following conditions
00633  * are met:
00634  * 
00635  * 1. Redistributions of source code must retain the above copyright
00636  *    notice, this list of conditions and the following disclaimer.
00637  * 
00638  * 2. Redistributions in binary form must reproduce the above copyright
00639  *    notice, this list of conditions and the following disclaimer in
00640  *    the documentation and/or other materials provided with the
00641  *    distribution.
00642  * 
00643  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00644  *    and "Vovida Open Communication Application Library (VOCAL)" must
00645  *    not be used to endorse or promote products derived from this
00646  *    software without prior written permission. For written
00647  *    permission, please contact vocal@vovida.org.
00648  *
00649  * 4. Products derived from this software may not be called "VOCAL", nor
00650  *    may "VOCAL" appear in their name, without prior written
00651  *    permission of Vovida Networks, Inc.
00652  * 
00653  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00654  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00655  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00656  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00657  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00658  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00659  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00660  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00661  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00662  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00663  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00664  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00665  * DAMAGE.
00666  * 
00667  * ====================================================================
00668  */