liveMedia/SIPClient.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2008 Live Networks, Inc.  All rights reserved.
00018 // A generic SIP client
00019 // Implementation
00020 
00021 #include "SIPClient.hh"
00022 #include "GroupsockHelper.hh"
00023 
00024 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00025 #define _strncasecmp _strnicmp
00026 #else
00027 #define _strncasecmp strncasecmp
00028 #endif
00029 
00031 
00032 SIPClient* SIPClient
00033 ::createNew(UsageEnvironment& env,
00034             unsigned char desiredAudioRTPPayloadFormat,
00035             char const* mimeSubtype,
00036             int verbosityLevel, char const* applicationName) {
00037   return new SIPClient(env, desiredAudioRTPPayloadFormat, mimeSubtype,
00038                        verbosityLevel, applicationName);
00039 }
00040 
00041 SIPClient::SIPClient(UsageEnvironment& env,
00042                      unsigned char desiredAudioRTPPayloadFormat,
00043                      char const* mimeSubtype,
00044                      int verbosityLevel, char const* applicationName)
00045   : Medium(env),
00046     fT1(500000 /* 500 ms */),
00047     fDesiredAudioRTPPayloadFormat(desiredAudioRTPPayloadFormat),
00048     fVerbosityLevel(verbosityLevel),
00049     fCSeq(0), fURL(NULL), fURLSize(0),
00050     fToTagStr(NULL), fToTagStrSize(0),
00051     fUserName(NULL), fUserNameSize(0),
00052     fInviteSDPDescription(NULL), fInviteCmd(NULL), fInviteCmdSize(0){
00053   if (mimeSubtype == NULL) mimeSubtype = "";
00054   fMIMESubtype = strDup(mimeSubtype);
00055   fMIMESubtypeSize = strlen(fMIMESubtype);
00056 
00057   if (applicationName == NULL) applicationName = "";
00058   fApplicationName = strDup(applicationName);
00059   fApplicationNameSize = strlen(fApplicationName);
00060 
00061   struct in_addr ourAddress;
00062   ourAddress.s_addr = ourIPAddress(env); // hack
00063   fOurAddressStr = strDup(our_inet_ntoa(ourAddress));
00064   fOurAddressStrSize = strlen(fOurAddressStr);
00065 
00066   fOurSocket = new Groupsock(env, ourAddress, 0, 255);
00067   if (fOurSocket == NULL) {
00068     env << "ERROR: Failed to create socket for addr "
00069         << our_inet_ntoa(ourAddress) << ": "
00070         << env.getResultMsg() << "\n";
00071   }
00072 
00073   // Now, find out our source port number.  Hack: Do this by first trying to
00074   // send a 0-length packet, so that the "getSourcePort()" call will work.
00075   fOurSocket->output(envir(), 255, (unsigned char*)"", 0);
00076   Port srcPort(0);
00077   getSourcePort(env, fOurSocket->socketNum(), srcPort);
00078   if (srcPort.num() != 0) {
00079     fOurPortNum = ntohs(srcPort.num());
00080   } else {
00081     // No luck.  Try again using a default port number:
00082     fOurPortNum = 5060;
00083     delete fOurSocket;
00084     fOurSocket = new Groupsock(env, ourAddress, fOurPortNum, 255);
00085     if (fOurSocket == NULL) {
00086       env << "ERROR: Failed to create socket for addr "
00087           << our_inet_ntoa(ourAddress) << ", port "
00088           << fOurPortNum << ": "
00089           << env.getResultMsg() << "\n";
00090     }
00091   }
00092       
00093   // Set various headers to be used in each request:
00094   char const* formatStr;
00095   unsigned headerSize;
00096 
00097   // Set the "User-Agent:" header:
00098   char const* const libName = "LIVE555 Streaming Media v";
00099   char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00100   char const* libPrefix; char const* libSuffix;
00101   if (applicationName == NULL || applicationName[0] == '\0') {
00102     applicationName = libPrefix = libSuffix = "";
00103   } else {
00104     libPrefix = " (";
00105     libSuffix = ")";
00106   }
00107   formatStr = "User-Agent: %s%s%s%s%s\r\n";
00108   headerSize
00109     = strlen(formatStr) + fApplicationNameSize + strlen(libPrefix)
00110     + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix);
00111   fUserAgentHeaderStr = new char[headerSize];
00112   sprintf(fUserAgentHeaderStr, formatStr,
00113           applicationName, libPrefix, libName, libVersionStr, libSuffix);
00114   fUserAgentHeaderStrSize = strlen(fUserAgentHeaderStr);
00115 
00116   reset();
00117 }
00118 
00119 SIPClient::~SIPClient() {
00120   reset();
00121 
00122   delete[] fUserAgentHeaderStr;
00123   delete fOurSocket;
00124   delete[] (char*)fOurAddressStr;
00125   delete[] (char*)fApplicationName;
00126   delete[] (char*)fMIMESubtype;
00127 }
00128 
00129 void SIPClient::reset() {
00130   fWorkingAuthenticator = NULL;
00131   delete[] fInviteCmd; fInviteCmd = NULL; fInviteCmdSize = 0;
00132   delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
00133 
00134   delete[] (char*)fUserName; fUserName = strDup(fApplicationName);
00135   fUserNameSize = strlen(fUserName);
00136 
00137   fValidAuthenticator.reset();
00138 
00139   delete[] (char*)fToTagStr; fToTagStr = NULL; fToTagStrSize = 0;
00140   fServerPortNum = 0;
00141   fServerAddress.s_addr = 0;
00142   delete[] (char*)fURL; fURL = NULL; fURLSize = 0;
00143 }
00144 
00145 void SIPClient::setProxyServer(unsigned proxyServerAddress,
00146                                portNumBits proxyServerPortNum) {
00147   fServerAddress.s_addr = proxyServerAddress;
00148   fServerPortNum = proxyServerPortNum;
00149   if (fOurSocket != NULL) {
00150     fOurSocket->changeDestinationParameters(fServerAddress,
00151                                             fServerPortNum, 255);
00152   }
00153 }
00154 
00155 static char* getLine(char* startOfLine) {
00156   // returns the start of the next line, or NULL if none
00157   for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
00158     if (*ptr == '\r' || *ptr == '\n') {
00159       // We found the end of the line
00160       *ptr++ = '\0';
00161       if (*ptr == '\n') ++ptr;
00162       return ptr;
00163     }
00164   }
00165 
00166   return NULL;
00167 }
00168 
00169 char* SIPClient::invite(char const* url, Authenticator* authenticator) {
00170   // First, check whether "url" contains a username:password to be used:
00171   fInviteStatusCode = 0;
00172   char* username; char* password;
00173   if (authenticator == NULL
00174       && parseSIPURLUsernamePassword(url, username, password)) {
00175     char* result = inviteWithPassword(url, username, password);
00176     delete[] username; delete[] password; // they were dynamically allocated
00177     return result;
00178   }
00179 
00180   if (!processURL(url)) return NULL;
00181 
00182   delete[] (char*)fURL; fURL = strDup(url);
00183   fURLSize = strlen(fURL);
00184 
00185   fCallId = our_random();
00186   fFromTag = our_random();
00187 
00188   return invite1(authenticator);
00189 }
00190 
00191 char* SIPClient::invite1(Authenticator* authenticator) {
00192   do {
00193     // Send the INVITE command:
00194 
00195     // First, construct an authenticator string:
00196     fValidAuthenticator.reset();
00197     fWorkingAuthenticator = authenticator;
00198     char* authenticatorStr
00199       = createAuthenticatorString(fWorkingAuthenticator, "INVITE", fURL);
00200 
00201     // Then, construct the SDP description to be sent in the INVITE:
00202     char* rtpmapLine;
00203     unsigned rtpmapLineSize;
00204     if (fMIMESubtypeSize > 0) {
00205       char* const rtpmapFmt = 
00206         "a=rtpmap:%u %s/8000\r\n";
00207       unsigned rtpmapFmtSize = strlen(rtpmapFmt)
00208         + 3 /* max char len */ + fMIMESubtypeSize;
00209       rtpmapLine = new char[rtpmapFmtSize];
00210       sprintf(rtpmapLine, rtpmapFmt,
00211               fDesiredAudioRTPPayloadFormat, fMIMESubtype);
00212       rtpmapLineSize = strlen(rtpmapLine);
00213     } else {
00214       // Static payload type => no "a=rtpmap:" line
00215       rtpmapLine = strDup("");
00216       rtpmapLineSize = 0;
00217     }
00218     char* const inviteSDPFmt = 
00219       "v=0\r\n"
00220       "o=- %u %u IN IP4 %s\r\n"
00221       "s=%s session\r\n"
00222       "c=IN IP4 %s\r\n"
00223       "t=0 0\r\n"
00224       "m=audio %u RTP/AVP %u\r\n"
00225       "%s";
00226     unsigned inviteSDPFmtSize = strlen(inviteSDPFmt)
00227       + 20 /* max int len */ + 20 + fOurAddressStrSize
00228       + fApplicationNameSize
00229       + fOurAddressStrSize
00230       + 5 /* max short len */ + 3 /* max char len */
00231       + rtpmapLineSize;
00232     delete[] fInviteSDPDescription;
00233     fInviteSDPDescription = new char[inviteSDPFmtSize];
00234     sprintf(fInviteSDPDescription, inviteSDPFmt,
00235             fCallId, fCSeq, fOurAddressStr,
00236             fApplicationName,
00237             fOurAddressStr,
00238             fClientStartPortNum, fDesiredAudioRTPPayloadFormat,
00239             rtpmapLine);
00240     unsigned inviteSDPSize = strlen(fInviteSDPDescription);
00241     delete[] rtpmapLine;
00242 
00243     char* const cmdFmt =
00244       "INVITE %s SIP/2.0\r\n"
00245       "From: %s <sip:%s@%s>;tag=%u\r\n"
00246       "Via: SIP/2.0/UDP %s:%u\r\n"
00247       "To: %s\r\n"
00248       "Contact: sip:%s@%s:%u\r\n"
00249       "Call-ID: %u@%s\r\n"
00250       "CSeq: %d INVITE\r\n"
00251       "Content-Type: application/sdp\r\n"
00252       "%s" /* Proxy-Authorization: line (if any) */
00253       "%s" /* User-Agent: line */
00254       "Content-length: %d\r\n\r\n"
00255       "%s";
00256     unsigned inviteCmdSize = strlen(cmdFmt)
00257       + fURLSize
00258       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00259       + fOurAddressStrSize + 5 /* max port len */
00260       + fURLSize
00261       + fUserNameSize + fOurAddressStrSize + 5
00262       + 20 + fOurAddressStrSize
00263       + 20
00264       + strlen(authenticatorStr)
00265       + fUserAgentHeaderStrSize
00266       + 20
00267       + inviteSDPSize;
00268     delete[] fInviteCmd; fInviteCmd = new char[inviteCmdSize];
00269     sprintf(fInviteCmd, cmdFmt,
00270             fURL,
00271             fUserName, fUserName, fOurAddressStr, fFromTag,
00272             fOurAddressStr, fOurPortNum,
00273             fURL,
00274             fUserName, fOurAddressStr, fOurPortNum,
00275             fCallId, fOurAddressStr,
00276             ++fCSeq,
00277             authenticatorStr,
00278             fUserAgentHeaderStr,
00279             inviteSDPSize,
00280             fInviteSDPDescription);
00281     fInviteCmdSize = strlen(fInviteCmd);
00282     delete[] authenticatorStr;
00283 
00284     // Before sending the "INVITE", arrange to handle any response packets,
00285     // and set up timers:
00286     fInviteClientState = Calling;
00287     fEventLoopStopFlag = 0;
00288     TaskScheduler& sched = envir().taskScheduler(); // abbrev.
00289     sched.turnOnBackgroundReadHandling(fOurSocket->socketNum(),
00290                                        &inviteResponseHandler, this);
00291     fTimerALen = 1*fT1; // initially
00292     fTimerACount = 0; // initially
00293     fTimerA = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
00294     fTimerB = sched.scheduleDelayedTask(64*fT1, timerBHandler, this);
00295     fTimerD = NULL; // for now
00296 
00297     if (!sendINVITE()) break;
00298 
00299     // Enter the event loop, to handle response packets, and timeouts:
00300     envir().taskScheduler().doEventLoop(&fEventLoopStopFlag);
00301 
00302     // We're finished with this "INVITE".
00303     // Turn off response handling and timers:
00304     sched.turnOffBackgroundReadHandling(fOurSocket->socketNum());
00305     sched.unscheduleDelayedTask(fTimerA);
00306     sched.unscheduleDelayedTask(fTimerB);
00307     sched.unscheduleDelayedTask(fTimerD);
00308 
00309     // NOTE: We return the SDP description that we used in the "INVITE",
00310     // not the one that we got from the server.
00311     // ##### Later: match the codecs in the response (offer, answer) #####
00312     if (fInviteSDPDescription != NULL) {
00313       return strDup(fInviteSDPDescription);
00314     }
00315   } while (0);
00316 
00317   fInviteStatusCode = 2;
00318   return NULL;
00319 }
00320 
00321 void SIPClient::inviteResponseHandler(void* clientData, int /*mask*/) {
00322   SIPClient* client = (SIPClient*)clientData;
00323   unsigned responseCode = client->getResponseCode();
00324   client->doInviteStateMachine(responseCode);
00325 }
00326 
00327 // Special 'response codes' that represent timers expiring:
00328 unsigned const timerAFires = 0xAAAAAAAA;
00329 unsigned const timerBFires = 0xBBBBBBBB;
00330 unsigned const timerDFires = 0xDDDDDDDD;
00331 
00332 void SIPClient::timerAHandler(void* clientData) {
00333   SIPClient* client = (SIPClient*)clientData;
00334   if (client->fVerbosityLevel >= 1) {
00335     client->envir() << "RETRANSMISSION " << ++client->fTimerACount
00336                     << ", after " << client->fTimerALen/1000000.0
00337                     << " additional seconds\n";
00338   }
00339   client->doInviteStateMachine(timerAFires);
00340 }
00341 
00342 void SIPClient::timerBHandler(void* clientData) {
00343   SIPClient* client = (SIPClient*)clientData;
00344   if (client->fVerbosityLevel >= 1) {
00345     client->envir() << "RETRANSMISSION TIMEOUT, after "
00346                     << 64*client->fT1/1000000.0 << " seconds\n";
00347     fflush(stderr);
00348   }
00349   client->doInviteStateMachine(timerBFires);
00350 }
00351 
00352 void SIPClient::timerDHandler(void* clientData) {
00353   SIPClient* client = (SIPClient*)clientData;
00354   if (client->fVerbosityLevel >= 1) {
00355     client->envir() << "TIMER D EXPIRED\n";
00356   }
00357   client->doInviteStateMachine(timerDFires);
00358 }
00359 
00360 void SIPClient::doInviteStateMachine(unsigned responseCode) {
00361   // Implement the state transition diagram (RFC 3261, Figure 5)
00362   TaskScheduler& sched = envir().taskScheduler(); // abbrev.
00363   switch (fInviteClientState) {
00364     case Calling: {
00365       if (responseCode == timerAFires) {
00366         // Restart timer A (with double the timeout interval):
00367         fTimerALen *= 2;
00368         fTimerA
00369           = sched.scheduleDelayedTask(fTimerALen, timerAHandler, this);
00370 
00371         fInviteClientState = Calling;
00372         if (!sendINVITE()) doInviteStateTerminated(0);
00373       } else {
00374         // Turn off timers A & B before moving to a new state: 
00375         sched.unscheduleDelayedTask(fTimerA);
00376         sched.unscheduleDelayedTask(fTimerB);
00377         
00378         if (responseCode == timerBFires) {
00379           envir().setResultMsg("No response from server");
00380           doInviteStateTerminated(0);
00381         } else if (responseCode >= 100 && responseCode <= 199) {
00382           fInviteClientState = Proceeding;
00383         } else if (responseCode >= 200 && responseCode <= 299) {
00384           doInviteStateTerminated(responseCode);
00385         } else if (responseCode >= 400 && responseCode <= 499) {
00386           doInviteStateTerminated(responseCode);
00387               // this isn't what the spec says, but it seems right...
00388         } else if (responseCode >= 300 && responseCode <= 699) {
00389           fInviteClientState = Completed;
00390           fTimerD
00391             = sched.scheduleDelayedTask(32000000, timerDHandler, this);
00392           if (!sendACK()) doInviteStateTerminated(0);
00393         }
00394       }
00395       break;
00396     }
00397 
00398     case Proceeding: {
00399       if (responseCode >= 100 && responseCode <= 199) {
00400         fInviteClientState = Proceeding;
00401       } else if (responseCode >= 200 && responseCode <= 299) {
00402         doInviteStateTerminated(responseCode);
00403       } else if (responseCode >= 400 && responseCode <= 499) {
00404         doInviteStateTerminated(responseCode);
00405             // this isn't what the spec says, but it seems right...
00406       } else if (responseCode >= 300 && responseCode <= 699) {
00407         fInviteClientState = Completed;
00408         fTimerD = sched.scheduleDelayedTask(32000000, timerDHandler, this);
00409         if (!sendACK()) doInviteStateTerminated(0);
00410       }
00411       break;
00412     }
00413 
00414     case Completed: {
00415       if (responseCode == timerDFires) {
00416         envir().setResultMsg("Transaction terminated");
00417         doInviteStateTerminated(0);
00418       } else if (responseCode >= 300 && responseCode <= 699) {
00419         fInviteClientState = Completed;
00420         if (!sendACK()) doInviteStateTerminated(0);
00421       }
00422       break;
00423     }
00424 
00425     case Terminated: {
00426         doInviteStateTerminated(responseCode);
00427         break;
00428     }
00429   }
00430 }
00431 
00432 void SIPClient::doInviteStateTerminated(unsigned responseCode) {
00433   fInviteClientState = Terminated; // FWIW...
00434   if (responseCode < 200 || responseCode > 299) {
00435     // We failed, so return NULL;
00436     delete[] fInviteSDPDescription; fInviteSDPDescription = NULL;
00437   }
00438 
00439   // Unblock the event loop:
00440   fEventLoopStopFlag = ~0;
00441 }
00442 
00443 Boolean SIPClient::sendINVITE() {
00444   if (!sendRequest(fInviteCmd, fInviteCmdSize)) {
00445     envir().setResultErrMsg("INVITE send() failed: ");
00446     return False;
00447   }
00448   return True;
00449 }
00450 
00451 unsigned SIPClient::getResponseCode() {
00452   unsigned responseCode = 0;
00453   do {
00454     // Get the response from the server:
00455     unsigned const readBufSize = 10000;
00456     char readBuffer[readBufSize+1]; char* readBuf = readBuffer;
00457 
00458     char* firstLine = NULL;
00459     char* nextLineStart = NULL;
00460     unsigned bytesRead = getResponse(readBuf, readBufSize);
00461     if (bytesRead == 0) break;
00462     if (fVerbosityLevel >= 1) {
00463       envir() << "Received INVITE response: " << readBuf << "\n";
00464     }
00465 
00466     // Inspect the first line to get the response code:
00467     firstLine = readBuf;
00468     nextLineStart = getLine(firstLine);
00469     if (!parseResponseCode(firstLine, responseCode)) break;
00470 
00471     if (responseCode != 200) {
00472       if (responseCode >= 400 && responseCode <= 499
00473           && fWorkingAuthenticator != NULL) {
00474         // We have an authentication failure, so fill in
00475         // "*fWorkingAuthenticator" using the contents of a following
00476         // "Proxy-Authenticate:" line.  (Once we compute a 'response' for
00477         // "fWorkingAuthenticator", it can be used in a subsequent request
00478         // - that will hopefully succeed.)
00479         char* lineStart;
00480         while (1) {
00481           lineStart = nextLineStart;
00482           if (lineStart == NULL) break;
00483 
00484           nextLineStart = getLine(lineStart);
00485           if (lineStart[0] == '\0') break; // this is a blank line
00486 
00487           char* realm = strDupSize(lineStart);
00488           char* nonce = strDupSize(lineStart);
00489           // ##### Check for the format of "Proxy-Authenticate:" lines from
00490           // ##### known server types.
00491           // ##### This is a crock! We should make the parsing more general
00492           Boolean foundAuthenticateHeader = False;
00493           if (
00494               // Asterisk #####
00495               sscanf(lineStart, "Proxy-Authenticate: Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"",
00496                      realm, nonce) == 2 ||
00497               // Cisco ATA #####
00498               sscanf(lineStart, "Proxy-Authenticate: Digest algorithm=MD5,domain=\"%*[^\"]\",nonce=\"%[^\"]\", realm=\"%[^\"]\"",
00499                      nonce, realm) == 2) {
00500             fWorkingAuthenticator->setRealmAndNonce(realm, nonce);
00501             foundAuthenticateHeader = True;
00502           }
00503           delete[] realm; delete[] nonce;
00504           if (foundAuthenticateHeader) break;
00505         } 
00506       }
00507       envir().setResultMsg("cannot handle INVITE response: ", firstLine);
00508       break;
00509     }
00510 
00511     // Skip every subsequent header line, until we see a blank line.
00512     // While doing so, check for "To:" and "Content-Length:" lines.
00513     // The remaining data is assumed to be the SDP descriptor that we want.
00514     // We should really do some more checking on the headers here - e.g., to
00515     // check for "Content-type: application/sdp", "CSeq", etc. #####
00516     int contentLength = -1;
00517     char* lineStart;
00518     while (1) {
00519       lineStart = nextLineStart;
00520       if (lineStart == NULL) break;
00521 
00522       nextLineStart = getLine(lineStart);
00523       if (lineStart[0] == '\0') break; // this is a blank line
00524 
00525       char* toTagStr = strDupSize(lineStart);
00526       if (sscanf(lineStart, "To:%*[^;]; tag=%s", toTagStr) == 1) {
00527         delete[] (char*)fToTagStr; fToTagStr = strDup(toTagStr);
00528         fToTagStrSize = strlen(fToTagStr);
00529       }
00530       delete[] toTagStr;
00531  
00532       if (sscanf(lineStart, "Content-Length: %d", &contentLength) == 1
00533           || sscanf(lineStart, "Content-length: %d", &contentLength) == 1) {
00534         if (contentLength < 0) {
00535           envir().setResultMsg("Bad \"Content-length:\" header: \"",
00536                                lineStart, "\"");
00537           break;
00538         }
00539       }
00540     }
00541 
00542     // We're now at the end of the response header lines
00543     if (lineStart == NULL) {
00544       envir().setResultMsg("no content following header lines: ", readBuf);
00545       break;
00546     }
00547 
00548     // Use the remaining data as the SDP descr, but first, check
00549     // the "Content-length:" header (if any) that we saw.  We may need to
00550     // read more data, or we may have extraneous data in the buffer.
00551     char* bodyStart = nextLineStart;
00552     if (bodyStart != NULL && contentLength >= 0) {
00553       // We saw a "Content-length:" header
00554       unsigned numBodyBytes = &readBuf[bytesRead] - bodyStart;
00555       if (contentLength > (int)numBodyBytes) {
00556         // We need to read more data.  First, make sure we have enough
00557         // space for it:
00558         unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
00559 #ifdef USING_TCP
00560         // THIS CODE WORKS ONLY FOR TCP: #####
00561         unsigned remainingBufferSize
00562           = readBufSize - (bytesRead + (readBuf - readBuffer));
00563         if (numExtraBytesNeeded > remainingBufferSize) {
00564           char tmpBuf[200];
00565           sprintf(tmpBuf, "Read buffer size (%d) is too small for \"Content-length:\" %d (need a buffer size of >= %d bytes\n",
00566                   readBufSize, contentLength,
00567                   readBufSize + numExtraBytesNeeded - remainingBufferSize);
00568           envir().setResultMsg(tmpBuf);
00569           break;
00570         }
00571 
00572         // Keep reading more data until we have enough:
00573         if (fVerbosityLevel >= 1) {
00574           envir() << "Need to read " << numExtraBytesNeeded
00575                   << " extra bytes\n";
00576         }
00577         while (numExtraBytesNeeded > 0) {
00578           char* ptr = &readBuf[bytesRead];
00579           unsigned bytesRead2;
00580           struct sockaddr_in fromAddr;
00581           Boolean readSuccess
00582             = fOurSocket->handleRead((unsigned char*)ptr,
00583                                      numExtraBytesNeeded,
00584                                      bytesRead2, fromAddr);
00585           if (!readSuccess) break;
00586           ptr[bytesRead2] = '\0';
00587           if (fVerbosityLevel >= 1) {
00588             envir() << "Read " << bytesRead2
00589                     << " extra bytes: " << ptr << "\n";
00590           }
00591 
00592           bytesRead += bytesRead2;
00593           numExtraBytesNeeded -= bytesRead2;
00594         }
00595 #endif
00596         if (numExtraBytesNeeded > 0) break; // one of the reads failed
00597       }
00598 
00599       bodyStart[contentLength] = '\0'; // trims any extra data
00600     }
00601   } while (0);
00602 
00603   return responseCode;
00604 }
00605 
00606 char* SIPClient::inviteWithPassword(char const* url, char const* username,
00607                                     char const* password) {
00608   delete[] (char*)fUserName; fUserName = strDup(username);
00609   fUserNameSize = strlen(fUserName);
00610 
00611   Authenticator authenticator;
00612   authenticator.setUsernameAndPassword(username, password);
00613   char* inviteResult = invite(url, &authenticator);
00614   if (inviteResult != NULL) {
00615     // We are already authorized
00616     return inviteResult;
00617   }
00618 
00619   // The "realm" and "nonce" fields should have been filled in:
00620   if (authenticator.realm() == NULL || authenticator.nonce() == NULL) {
00621     // We haven't been given enough information to try again, so fail:
00622     return NULL;
00623   }
00624 
00625   // Try again (but with the same CallId):
00626   inviteResult = invite1(&authenticator);
00627   if (inviteResult != NULL) {
00628     // The authenticator worked, so use it in future requests:
00629     fValidAuthenticator = authenticator;
00630   }
00631 
00632   return inviteResult;
00633 }
00634 
00635 Boolean SIPClient::sendACK() {
00636   char* cmd = NULL;
00637   do {
00638     char* const cmdFmt =
00639       "ACK %s SIP/2.0\r\n"
00640       "From: %s <sip:%s@%s>;tag=%u\r\n"
00641       "Via: SIP/2.0/UDP %s:%u\r\n"
00642       "To: %s;tag=%s\r\n"
00643       "Call-ID: %u@%s\r\n"
00644       "CSeq: %d ACK\r\n"
00645       "Content-length: 0\r\n\r\n";
00646     unsigned cmdSize = strlen(cmdFmt)
00647       + fURLSize
00648       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00649       + fOurAddressStrSize + 5 /* max port len */
00650       + fURLSize + fToTagStrSize
00651       + 20 + fOurAddressStrSize
00652       + 20;
00653     cmd = new char[cmdSize];
00654     sprintf(cmd, cmdFmt,
00655             fURL,
00656             fUserName, fUserName, fOurAddressStr, fFromTag,
00657             fOurAddressStr, fOurPortNum,
00658             fURL, fToTagStr,
00659             fCallId, fOurAddressStr,
00660             fCSeq /* note: it's the same as before; not incremented */);
00661     
00662     if (!sendRequest(cmd, strlen(cmd))) {
00663       envir().setResultErrMsg("ACK send() failed: ");
00664       break;
00665     }
00666 
00667     delete[] cmd;
00668     return True;
00669   } while (0);
00670 
00671   delete[] cmd;
00672   return False;
00673 }
00674 
00675 Boolean SIPClient::sendBYE() {
00676   // NOTE: This should really be retransmitted, for reliability #####
00677   char* cmd = NULL;
00678   do {
00679     char* const cmdFmt =
00680       "BYE %s SIP/2.0\r\n"
00681       "From: %s <sip:%s@%s>;tag=%u\r\n"
00682       "Via: SIP/2.0/UDP %s:%u\r\n"
00683       "To: %s;tag=%s\r\n"
00684       "Call-ID: %u@%s\r\n"
00685       "CSeq: %d ACK\r\n"
00686       "Content-length: 0\r\n\r\n";
00687     unsigned cmdSize = strlen(cmdFmt)
00688       + fURLSize
00689       + 2*fUserNameSize + fOurAddressStrSize + 20 /* max int len */
00690       + fOurAddressStrSize + 5 /* max port len */
00691       + fURLSize + fToTagStrSize
00692       + 20 + fOurAddressStrSize
00693       + 20;
00694     cmd = new char[cmdSize];
00695     sprintf(cmd, cmdFmt,
00696             fURL,
00697             fUserName, fUserName, fOurAddressStr, fFromTag,
00698             fOurAddressStr, fOurPortNum,
00699             fURL, fToTagStr,
00700             fCallId, fOurAddressStr,
00701             ++fCSeq);
00702     
00703     if (!sendRequest(cmd, strlen(cmd))) {
00704       envir().setResultErrMsg("BYE send() failed: ");
00705       break;
00706     }
00707 
00708     delete[] cmd;
00709     return True;
00710   } while (0);
00711 
00712   delete[] cmd;
00713   return False;
00714 }
00715 
00716 Boolean SIPClient::processURL(char const* url) {
00717   do {
00718     // If we don't already have a server address/port, then
00719     // get these by parsing the URL:
00720     if (fServerAddress.s_addr == 0) {
00721       NetAddress destAddress;
00722       if (!parseSIPURL(envir(), url, destAddress, fServerPortNum)) break;
00723       fServerAddress.s_addr = *(unsigned*)(destAddress.data());
00724     
00725       if (fOurSocket != NULL) {
00726         fOurSocket->changeDestinationParameters(fServerAddress,
00727                                                 fServerPortNum, 255);
00728       }
00729     }
00730 
00731     return True; 
00732   } while (0);
00733 
00734   fInviteStatusCode = 1;
00735   return False;
00736 }
00737 
00738 Boolean SIPClient::parseSIPURL(UsageEnvironment& env, char const* url,
00739                                NetAddress& address,
00740                                portNumBits& portNum) {
00741   do {
00742     // Parse the URL as "sip:<username>@<address>:<port>/<etc>"
00743     // (with ":<port>" and "/<etc>" optional)
00744     // Also, skip over any "<username>[:<password>]@" preceding <address>
00745     char const* prefix = "sip:";
00746     unsigned const prefixLength = 4;
00747     if (_strncasecmp(url, prefix, prefixLength) != 0) {
00748       env.setResultMsg("URL is not of the form \"", prefix, "\"");
00749       break;
00750     }
00751 
00752     unsigned const parseBufferSize = 100;
00753     char parseBuffer[parseBufferSize];
00754     unsigned addressStartIndex = prefixLength;
00755     while (url[addressStartIndex] != '\0'
00756            && url[addressStartIndex++] != '@') {}
00757     char const* from = &url[addressStartIndex];
00758 
00759     // Skip over any "<username>[:<password>]@"
00760     char const* from1 = from;
00761     while (*from1 != '\0' && *from1 != '/') {
00762       if (*from1 == '@') {
00763         from = ++from1;
00764         break;
00765       }
00766       ++from1;
00767     }
00768 
00769     char* to = &parseBuffer[0];
00770     unsigned i;
00771     for (i = 0; i < parseBufferSize; ++i) {
00772       if (*from == '\0' || *from == ':' || *from == '/') {
00773         // We've completed parsing the address
00774         *to = '\0';
00775         break;
00776       }
00777       *to++ = *from++;
00778     }
00779     if (i == parseBufferSize) {
00780       env.setResultMsg("URL is too long");
00781       break;
00782     }
00783 
00784     NetAddressList addresses(parseBuffer);
00785     if (addresses.numAddresses() == 0) {
00786       env.setResultMsg("Failed to find network address for \"",
00787                            parseBuffer, "\"");
00788       break;
00789     }
00790     address = *(addresses.firstAddress());
00791 
00792     portNum = 5060; // default value
00793     char nextChar = *from;
00794     if (nextChar == ':') {
00795       int portNumInt;
00796       if (sscanf(++from, "%d", &portNumInt) != 1) {
00797         env.setResultMsg("No port number follows ':'");
00798         break;
00799       }
00800       if (portNumInt < 1 || portNumInt > 65535) {
00801         env.setResultMsg("Bad port number");
00802         break;
00803       }
00804       portNum = (portNumBits)portNumInt;
00805     }
00806 
00807     return True;
00808   } while (0);
00809 
00810   return False;
00811 }
00812 
00813 Boolean SIPClient::parseSIPURLUsernamePassword(char const* url,
00814                                                char*& username,
00815                                                char*& password) {
00816   username = password = NULL; // by default
00817   do {
00818     // Parse the URL as "sip:<username>[:<password>]@<whatever>"
00819     char const* prefix = "sip:";
00820     unsigned const prefixLength = 4;
00821     if (_strncasecmp(url, prefix, prefixLength) != 0) break;
00822 
00823     // Look for the ':' and '@':
00824     unsigned usernameIndex = prefixLength;
00825     unsigned colonIndex = 0, atIndex = 0;
00826     for (unsigned i = usernameIndex; url[i] != '\0' && url[i] != '/'; ++i) {
00827       if (url[i] == ':' && colonIndex == 0) {
00828         colonIndex = i;
00829       } else if (url[i] == '@') {
00830         atIndex = i;
00831         break; // we're done
00832       }
00833     }
00834     if (atIndex == 0) break; // no '@' found
00835 
00836     char* urlCopy = strDup(url);
00837     urlCopy[atIndex] = '\0';
00838     if (colonIndex > 0) {
00839       urlCopy[colonIndex] = '\0';
00840       password = strDup(&urlCopy[colonIndex+1]);
00841     } else {
00842       password = strDup("");
00843     }
00844     username = strDup(&urlCopy[usernameIndex]);
00845     delete[] urlCopy;
00846 
00847     return True;
00848   } while (0);
00849 
00850   return False;
00851 }
00852 
00853 char*
00854 SIPClient::createAuthenticatorString(Authenticator const* authenticator,
00855                                       char const* cmd, char const* url) {
00856   if (authenticator != NULL && authenticator->realm() != NULL
00857       && authenticator->nonce() != NULL && authenticator->username() != NULL
00858       && authenticator->password() != NULL) {
00859     // We've been provided a filled-in authenticator, so use it:
00860     char* const authFmt = "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", response=\"%s\", uri=\"%s\"\r\n";
00861     char const* response = authenticator->computeDigestResponse(cmd, url);
00862     unsigned authBufSize = strlen(authFmt)
00863       + strlen(authenticator->username()) + strlen(authenticator->realm())
00864       + strlen(authenticator->nonce()) + strlen(url) + strlen(response);
00865     char* authenticatorStr = new char[authBufSize];
00866     sprintf(authenticatorStr, authFmt,
00867             authenticator->username(), authenticator->realm(),
00868             authenticator->nonce(), response, url);
00869     authenticator->reclaimDigestResponse(response);
00870 
00871     return authenticatorStr;
00872   }
00873 
00874   return strDup("");
00875 }
00876 
00877 Boolean SIPClient::sendRequest(char const* requestString,
00878                                unsigned requestLength) {
00879   if (fVerbosityLevel >= 1) {
00880     envir() << "Sending request: " << requestString << "\n";
00881   }
00882   // NOTE: We should really check that "requestLength" is not #####
00883   // too large for UDP (see RFC 3261, section 18.1.1) #####
00884   return fOurSocket->output(envir(), 255, (unsigned char*)requestString,
00885                             requestLength);
00886 }
00887 
00888 unsigned SIPClient::getResponse(char*& responseBuffer,
00889                                 unsigned responseBufferSize) {
00890   if (responseBufferSize == 0) return 0; // just in case...
00891   responseBuffer[0] = '\0'; // ditto
00892 
00893   // Keep reading data from the socket until we see "\r\n\r\n" (except
00894   // at the start), or until we fill up our buffer.
00895   // Don't read any more than this.
00896   char* p = responseBuffer;
00897   Boolean haveSeenNonCRLF = False;
00898   int bytesRead = 0;
00899   while (bytesRead < (int)responseBufferSize) {
00900     unsigned bytesReadNow;
00901     struct sockaddr_in fromAddr;
00902     unsigned char* toPosn = (unsigned char*)(responseBuffer+bytesRead);
00903     Boolean readSuccess
00904       = fOurSocket->handleRead(toPosn, responseBufferSize-bytesRead,
00905                                bytesReadNow, fromAddr);
00906     if (!readSuccess || bytesReadNow == 0) {
00907       envir().setResultMsg("SIP response was truncated");
00908       break;
00909     }
00910     bytesRead += bytesReadNow;
00911     
00912     // Check whether we have "\r\n\r\n":
00913     char* lastToCheck = responseBuffer+bytesRead-4;
00914     if (lastToCheck < responseBuffer) continue;
00915     for (; p <= lastToCheck; ++p) {
00916       if (haveSeenNonCRLF) {
00917         if (*p == '\r' && *(p+1) == '\n' &&
00918             *(p+2) == '\r' && *(p+3) == '\n') {
00919           responseBuffer[bytesRead] = '\0';
00920 
00921           // Before returning, trim any \r or \n from the start:
00922           while (*responseBuffer == '\r' || *responseBuffer == '\n') {
00923             ++responseBuffer;
00924             --bytesRead;
00925           }
00926           return bytesRead;
00927         }
00928       } else {
00929         if (*p != '\r' && *p != '\n') {
00930           haveSeenNonCRLF = True;
00931         }
00932       }
00933     }
00934   }
00935   
00936   return 0;
00937 }
00938 
00939 Boolean SIPClient::parseResponseCode(char const* line, 
00940                                       unsigned& responseCode) {
00941   if (sscanf(line, "%*s%u", &responseCode) != 1) {
00942     envir().setResultMsg("no response code in line: \"", line, "\"");
00943     return False;
00944   }
00945 
00946   return