liveMedia/RTSPClient.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 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2014 Live Networks, Inc.  All rights reserved.
00018 // A generic RTSP client
00019 // Implementation
00020 
00021 #include "RTSPClient.hh"
00022 #include "RTSPCommon.hh"
00023 #include "Base64.hh"
00024 #include "Locale.hh"
00025 #include <GroupsockHelper.hh>
00026 #include "ourMD5.hh"
00027 
00029 
00030 RTSPClient* RTSPClient::createNew(UsageEnvironment& env, char const* rtspURL,
00031                                   int verbosityLevel,
00032                                   char const* applicationName,
00033                                   portNumBits tunnelOverHTTPPortNum,
00034                                   int socketNumToServer) {
00035   return new RTSPClient(env, rtspURL,
00036                         verbosityLevel, applicationName, tunnelOverHTTPPortNum, socketNumToServer);
00037 }
00038 
00039 unsigned RTSPClient::sendDescribeCommand(responseHandler* responseHandler, Authenticator* authenticator) {
00040   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00041   return sendRequest(new RequestRecord(++fCSeq, "DESCRIBE", responseHandler));
00042 }
00043 
00044 unsigned RTSPClient::sendOptionsCommand(responseHandler* responseHandler, Authenticator* authenticator) {
00045   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00046   return sendRequest(new RequestRecord(++fCSeq, "OPTIONS", responseHandler));
00047 }
00048 
00049 unsigned RTSPClient::sendAnnounceCommand(char const* sdpDescription, responseHandler* responseHandler, Authenticator* authenticator) {
00050   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00051   return sendRequest(new RequestRecord(++fCSeq, "ANNOUNCE", responseHandler, NULL, NULL, False, 0.0, 0.0, 0.0, sdpDescription));
00052 }
00053 
00054 unsigned RTSPClient::sendSetupCommand(MediaSubsession& subsession, responseHandler* responseHandler,
00055                                       Boolean streamOutgoing, Boolean streamUsingTCP, Boolean forceMulticastOnUnspecified,
00056                                       Authenticator* authenticator) {
00057   if (fTunnelOverHTTPPortNum != 0) streamUsingTCP = True; // RTSP-over-HTTP tunneling uses TCP (by definition)
00058   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00059 
00060   u_int32_t booleanFlags = 0;
00061   if (streamUsingTCP) booleanFlags |= 0x1;
00062   if (streamOutgoing) booleanFlags |= 0x2;
00063   if (forceMulticastOnUnspecified) booleanFlags |= 0x4;
00064   return sendRequest(new RequestRecord(++fCSeq, "SETUP", responseHandler, NULL, &subsession, booleanFlags));
00065 }
00066 
00067 unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
00068                                      double start, double end, float scale,
00069                                      Authenticator* authenticator) {
00070   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00071   sendDummyUDPPackets(session); // hack to improve NAT traversal
00072   return sendRequest(new RequestRecord(++fCSeq, "PLAY", responseHandler, &session, NULL, 0, start, end, scale));
00073 }
00074 
00075 unsigned RTSPClient::sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
00076                                      double start, double end, float scale,
00077                                      Authenticator* authenticator) {
00078   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00079   sendDummyUDPPackets(subsession); // hack to improve NAT traversal
00080   return sendRequest(new RequestRecord(++fCSeq, "PLAY", responseHandler, NULL, &subsession, 0, start, end, scale));
00081 }
00082 
00083 unsigned RTSPClient::sendPlayCommand(MediaSession& session, responseHandler* responseHandler,
00084                                      char const* absStartTime, char const* absEndTime, float scale,
00085                                      Authenticator* authenticator) {
00086   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00087   sendDummyUDPPackets(session); // hack to improve NAT traversal
00088   return sendRequest(new RequestRecord(++fCSeq, responseHandler, absStartTime, absEndTime, scale, &session, NULL));
00089 }
00090 
00091 unsigned RTSPClient::sendPlayCommand(MediaSubsession& subsession, responseHandler* responseHandler,
00092                                      char const* absStartTime, char const* absEndTime, float scale,
00093                                      Authenticator* authenticator) {
00094   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00095   sendDummyUDPPackets(subsession); // hack to improve NAT traversal
00096   return sendRequest(new RequestRecord(++fCSeq, responseHandler, absStartTime, absEndTime, scale, NULL, &subsession));
00097 }
00098 
00099 unsigned RTSPClient::sendPauseCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
00100   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00101   return sendRequest(new RequestRecord(++fCSeq, "PAUSE", responseHandler, &session));
00102 }
00103 
00104 unsigned RTSPClient::sendPauseCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
00105   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00106   return sendRequest(new RequestRecord(++fCSeq, "PAUSE", responseHandler, NULL, &subsession));
00107 }
00108 
00109 unsigned RTSPClient::sendRecordCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
00110   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00111   return sendRequest(new RequestRecord(++fCSeq, "RECORD", responseHandler, &session));
00112 }
00113 
00114 unsigned RTSPClient::sendRecordCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
00115   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00116   return sendRequest(new RequestRecord(++fCSeq, "RECORD", responseHandler, NULL, &subsession));
00117 }
00118 
00119 unsigned RTSPClient::sendTeardownCommand(MediaSession& session, responseHandler* responseHandler, Authenticator* authenticator) {
00120   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00121   return sendRequest(new RequestRecord(++fCSeq, "TEARDOWN", responseHandler, &session));
00122 }
00123 
00124 unsigned RTSPClient::sendTeardownCommand(MediaSubsession& subsession, responseHandler* responseHandler, Authenticator* authenticator) {
00125   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00126   return sendRequest(new RequestRecord(++fCSeq, "TEARDOWN", responseHandler, NULL, &subsession));
00127 }
00128 
00129 unsigned RTSPClient::sendSetParameterCommand(MediaSession& session, responseHandler* responseHandler,
00130                                              char const* parameterName, char const* parameterValue,
00131                                              Authenticator* authenticator) {
00132   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00133   char* paramString = new char[strlen(parameterName) + strlen(parameterValue) + 10];
00134   sprintf(paramString, "%s: %s\r\n", parameterName, parameterValue);
00135   unsigned result = sendRequest(new RequestRecord(++fCSeq, "SET_PARAMETER", responseHandler, &session, NULL, False, 0.0, 0.0, 0.0, paramString));
00136   delete[] paramString;
00137   return result;
00138 }
00139 
00140 unsigned RTSPClient::sendGetParameterCommand(MediaSession& session, responseHandler* responseHandler, char const* parameterName,
00141                                              Authenticator* authenticator) {
00142   if (authenticator != NULL) fCurrentAuthenticator = *authenticator;
00143 
00144   // We assume that:
00145   //    parameterName is NULL means: Send no body in the request.
00146   //    parameterName is "" means: Send only \r\n in the request body.  
00147   //    parameterName is non-empty means: Send "<parameterName>\r\n" as the request body.  
00148   unsigned parameterNameLen = parameterName == NULL ? 0 : strlen(parameterName);
00149   char* paramString = new char[parameterNameLen + 3]; // the 3 is for \r\n + the '\0' byte
00150   if (parameterName == NULL) {
00151     paramString[0] = '\0';
00152   } else {
00153     sprintf(paramString, "%s\r\n", parameterName);
00154   }
00155   unsigned result = sendRequest(new RequestRecord(++fCSeq, "GET_PARAMETER", responseHandler, &session, NULL, False, 0.0, 0.0, 0.0, paramString));
00156   delete[] paramString;
00157   return result;
00158 }
00159 
00160 void RTSPClient::sendDummyUDPPackets(MediaSession& session, unsigned numDummyPackets) {
00161   MediaSubsessionIterator iter(session);
00162   MediaSubsession* subsession;
00163 
00164   while ((subsession = iter.next()) != NULL) {
00165     sendDummyUDPPackets(*subsession, numDummyPackets);
00166   }
00167 }
00168 
00169 void RTSPClient::sendDummyUDPPackets(MediaSubsession& subsession, unsigned numDummyPackets) {
00170   // Hack: To increase the likelihood of UDP packets from the server reaching us,
00171   // if we're behind a NAT, send a few 'dummy' UDP packets to the server now.
00172   // (We do this on both our RTP port and our RTCP port.)
00173   Groupsock* gs1 = NULL; Groupsock* gs2 = NULL;
00174   if (subsession.rtpSource() != NULL) gs1 = subsession.rtpSource()->RTPgs();
00175   if (subsession.rtcpInstance() != NULL) gs2 = subsession.rtcpInstance()->RTCPgs();
00176   u_int32_t const dummy = 0xFEEDFACE;
00177   for (unsigned i = 0; i < numDummyPackets; ++i) {
00178     if (gs1 != NULL) gs1->output(envir(), 255, (unsigned char*)&dummy, sizeof dummy);
00179     if (gs2 != NULL) gs2->output(envir(), 255, (unsigned char*)&dummy, sizeof dummy);
00180   }
00181 }
00182 
00183 Boolean RTSPClient::changeResponseHandler(unsigned cseq, responseHandler* newResponseHandler) { 
00184   // Look for the matching request record in each of our 'pending requests' queues:
00185   RequestRecord* request;
00186   if ((request = fRequestsAwaitingConnection.findByCSeq(cseq)) != NULL
00187       || (request = fRequestsAwaitingHTTPTunneling.findByCSeq(cseq)) != NULL
00188       || (request = fRequestsAwaitingResponse.findByCSeq(cseq)) != NULL) {
00189     request->handler() = newResponseHandler;
00190     return True;
00191   }
00192 
00193   return False;
00194 }
00195 
00196 Boolean RTSPClient::lookupByName(UsageEnvironment& env,
00197                                  char const* instanceName,
00198                                  RTSPClient*& resultClient) {
00199   resultClient = NULL; // unless we succeed
00200 
00201   Medium* medium;
00202   if (!Medium::lookupByName(env, instanceName, medium)) return False;
00203 
00204   if (!medium->isRTSPClient()) {
00205     env.setResultMsg(instanceName, " is not a RTSP client");
00206     return False;
00207   }
00208 
00209   resultClient = (RTSPClient*)medium;
00210   return True;
00211 }
00212 
00213 Boolean RTSPClient::parseRTSPURL(UsageEnvironment& env, char const* url,
00214                                  char*& username, char*& password,
00215                                  NetAddress& address,
00216                                  portNumBits& portNum,
00217                                  char const** urlSuffix) {
00218   do {
00219     // Parse the URL as "rtsp://[<username>[:<password>]@]<server-address-or-name>[:<port>][/<stream-name>]"
00220     char const* prefix = "rtsp://";
00221     unsigned const prefixLength = 7;
00222     if (_strncasecmp(url, prefix, prefixLength) != 0) {
00223       env.setResultMsg("URL is not of the form \"", prefix, "\"");
00224       break;
00225     }
00226 
00227     unsigned const parseBufferSize = 100;
00228     char parseBuffer[parseBufferSize];
00229     char const* from = &url[prefixLength];
00230 
00231     // Check whether "<username>[:<password>]@" occurs next.
00232     // We do this by checking whether '@' appears before the end of the URL, or before the first '/'.
00233     username = password = NULL; // default return values
00234     char const* colonPasswordStart = NULL;
00235     char const* p;
00236     for (p = from; *p != '\0' && *p != '/'; ++p) {
00237       if (*p == ':' && colonPasswordStart == NULL) {
00238         colonPasswordStart = p;
00239       } else if (*p == '@') {
00240         // We found <username> (and perhaps <password>).  Copy them into newly-allocated result strings:
00241         if (colonPasswordStart == NULL) colonPasswordStart = p;
00242 
00243         char const* usernameStart = from;
00244         unsigned usernameLen = colonPasswordStart - usernameStart;
00245         username = new char[usernameLen + 1] ; // allow for the trailing '\0'
00246         for (unsigned i = 0; i < usernameLen; ++i) username[i] = usernameStart[i];
00247         username[usernameLen] = '\0';
00248 
00249         char const* passwordStart = colonPasswordStart;
00250         if (passwordStart < p) ++passwordStart; // skip over the ':'
00251         unsigned passwordLen = p - passwordStart;
00252         password = new char[passwordLen + 1]; // allow for the trailing '\0'
00253         for (unsigned j = 0; j < passwordLen; ++j) password[j] = passwordStart[j];
00254         password[passwordLen] = '\0';
00255 
00256         from = p + 1; // skip over the '@'
00257         break;
00258       }
00259     }
00260 
00261     // Next, parse <server-address-or-name>
00262     char* to = &parseBuffer[0];
00263     unsigned i;
00264     for (i = 0; i < parseBufferSize; ++i) {
00265       if (*from == '\0' || *from == ':' || *from == '/') {
00266         // We've completed parsing the address
00267         *to = '\0';
00268         break;
00269       }
00270       *to++ = *from++;
00271     }
00272     if (i == parseBufferSize) {
00273       env.setResultMsg("URL is too long");
00274       break;
00275     }
00276 
00277     NetAddressList addresses(parseBuffer);
00278     if (addresses.numAddresses() == 0) {
00279       env.setResultMsg("Failed to find network address for \"",
00280                        parseBuffer, "\"");
00281       break;
00282     }
00283     address = *(addresses.firstAddress());
00284 
00285     portNum = 554; // default value
00286     char nextChar = *from;
00287     if (nextChar == ':') {
00288       int portNumInt;
00289       if (sscanf(++from, "%d", &portNumInt) != 1) {
00290         env.setResultMsg("No port number follows ':'");
00291         break;
00292       }
00293       if (portNumInt < 1 || portNumInt > 65535) {
00294         env.setResultMsg("Bad port number");
00295         break;
00296       }
00297       portNum = (portNumBits)portNumInt;
00298       while (*from >= '0' && *from <= '9') ++from; // skip over port number
00299     }
00300 
00301     // The remainder of the URL is the suffix:
00302     if (urlSuffix != NULL) *urlSuffix = from;
00303 
00304     return True;
00305   } while (0);
00306 
00307   return False;
00308 }
00309 
00310 void RTSPClient::setUserAgentString(char const* userAgentName) {
00311   if (userAgentName == NULL) return;
00312 
00313   // Change the existing user agent header string:
00314   char const* const formatStr = "User-Agent: %s\r\n";
00315   unsigned const headerSize = strlen(formatStr) + strlen(userAgentName);
00316   delete[] fUserAgentHeaderStr;
00317   fUserAgentHeaderStr = new char[headerSize];
00318   sprintf(fUserAgentHeaderStr, formatStr, userAgentName);
00319   fUserAgentHeaderStrLen = strlen(fUserAgentHeaderStr);
00320 }
00321 
00322 unsigned RTSPClient::responseBufferSize = 20000; // default value; you can reassign this in your application if you need to
00323 
00324 RTSPClient::RTSPClient(UsageEnvironment& env, char const* rtspURL,
00325                        int verbosityLevel, char const* applicationName,
00326                        portNumBits tunnelOverHTTPPortNum, int socketNumToServer)
00327   : Medium(env),
00328     fVerbosityLevel(verbosityLevel), fCSeq(1), fServerAddress(0),
00329     fTunnelOverHTTPPortNum(tunnelOverHTTPPortNum), fUserAgentHeaderStr(NULL), fUserAgentHeaderStrLen(0),
00330     fInputSocketNum(-1), fOutputSocketNum(-1), fBaseURL(NULL), fTCPStreamIdCount(0),
00331     fLastSessionId(NULL), fSessionTimeoutParameter(0), fSessionCookieCounter(0), fHTTPTunnelingConnectionIsPending(False) {
00332   setBaseURL(rtspURL);
00333 
00334   fResponseBuffer = new char[responseBufferSize+1];
00335   resetResponseBuffer();
00336 
00337   if (socketNumToServer >= 0) {
00338     // This socket number is (assumed to be) already connected to the server.
00339     // Use it, and arrange to handle responses to requests sent on it:
00340     fInputSocketNum = fOutputSocketNum = socketNumToServer;
00341     envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION,
00342                                                   (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
00343   }
00344 
00345   // Set the "User-Agent:" header to use in each request:
00346   char const* const libName = "LIVE555 Streaming Media v";
00347   char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
00348   char const* libPrefix; char const* libSuffix;
00349   if (applicationName == NULL || applicationName[0] == '\0') {
00350     applicationName = libPrefix = libSuffix = "";
00351   } else {
00352     libPrefix = " (";
00353     libSuffix = ")";
00354   }
00355   unsigned userAgentNameSize
00356     = strlen(applicationName) + strlen(libPrefix) + strlen(libName) + strlen(libVersionStr) + strlen(libSuffix) + 1;
00357   char* userAgentName = new char[userAgentNameSize];
00358   sprintf(userAgentName, "%s%s%s%s%s", applicationName, libPrefix, libName, libVersionStr, libSuffix);
00359   setUserAgentString(userAgentName);
00360   delete[] userAgentName;
00361 }
00362 
00363 RTSPClient::~RTSPClient() {
00364   RTPInterface::clearServerRequestAlternativeByteHandler(envir(), fInputSocketNum); // in case we were receiving RTP-over-TCP
00365   reset();
00366 
00367   delete[] fResponseBuffer;
00368   delete[] fUserAgentHeaderStr;
00369 }
00370 
00371 void RTSPClient::reset() {
00372   resetTCPSockets();
00373   resetResponseBuffer();
00374   fServerAddress = 0;
00375 
00376   setBaseURL(NULL);
00377 
00378   fCurrentAuthenticator.reset();
00379 
00380   delete[] fLastSessionId; fLastSessionId = NULL;
00381 }
00382 
00383 void RTSPClient::setBaseURL(char const* url) {
00384   delete[] fBaseURL; fBaseURL = strDup(url);
00385 }
00386 
00387 int RTSPClient::grabSocket() {
00388   int inputSocket = fInputSocketNum;
00389   fInputSocketNum = -1;
00390 
00391   return inputSocket;
00392 }
00393 
00394 unsigned RTSPClient::sendRequest(RequestRecord* request) {
00395   char* cmd = NULL;
00396   do {
00397     Boolean connectionIsPending = False;
00398     if (!fRequestsAwaitingConnection.isEmpty()) {
00399       // A connection is currently pending (with at least one enqueued request).  Enqueue this request also:
00400       connectionIsPending = True;
00401     } else if (fInputSocketNum < 0) { // we need to open a connection
00402       int connectResult = openConnection();
00403       if (connectResult < 0) break; // an error occurred
00404       else if (connectResult == 0) {
00405         // A connection is pending
00406         connectionIsPending = True;
00407       } // else the connection succeeded.  Continue sending the command.
00408     }
00409     if (connectionIsPending) {
00410       fRequestsAwaitingConnection.enqueue(request);
00411       return request->cseq();
00412     }
00413 
00414     // If requested (and we're not already doing it, or have done it), set up the special protocol for tunneling RTSP-over-HTTP:
00415     if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && fOutputSocketNum == fInputSocketNum) {
00416       if (!setupHTTPTunneling1()) break;
00417       fRequestsAwaitingHTTPTunneling.enqueue(request);
00418       return request->cseq();
00419     }
00420 
00421     // Construct and send the command:
00422 
00423     // First, construct command-specific headers that we need:
00424 
00425     char* cmdURL = fBaseURL; // by default
00426     Boolean cmdURLWasAllocated = False;
00427 
00428     char const* protocolStr = "RTSP/1.0"; // by default
00429 
00430     char* extraHeaders = (char*)""; // by default
00431     Boolean extraHeadersWereAllocated = False; 
00432 
00433     char* contentLengthHeader = (char*)""; // by default
00434     Boolean contentLengthHeaderWasAllocated = False;
00435 
00436     if (!setRequestFields(request,
00437                           cmdURL, cmdURLWasAllocated,
00438                           protocolStr,
00439                           extraHeaders, extraHeadersWereAllocated)) {
00440       break;
00441     }
00442 
00443     char const* contentStr = request->contentStr(); // by default
00444     if (contentStr == NULL) contentStr = "";
00445     unsigned contentStrLen = strlen(contentStr);
00446     if (contentStrLen > 0) {
00447       char const* contentLengthHeaderFmt =
00448         "Content-Length: %d\r\n";
00449       unsigned contentLengthHeaderSize = strlen(contentLengthHeaderFmt)
00450         + 20 /* max int len */;
00451       contentLengthHeader = new char[contentLengthHeaderSize];
00452       sprintf(contentLengthHeader, contentLengthHeaderFmt, contentStrLen);
00453       contentLengthHeaderWasAllocated = True;
00454     }
00455 
00456     char* authenticatorStr = createAuthenticatorString(request->commandName(), fBaseURL);
00457 
00458     char const* const cmdFmt =
00459       "%s %s %s\r\n"
00460       "CSeq: %d\r\n"
00461       "%s"
00462       "%s"
00463       "%s"
00464       "%s"
00465       "\r\n"
00466       "%s";
00467     unsigned cmdSize = strlen(cmdFmt)
00468       + strlen(request->commandName()) + strlen(cmdURL) + strlen(protocolStr)
00469       + 20 /* max int len */
00470       + strlen(authenticatorStr)
00471       + fUserAgentHeaderStrLen
00472       + strlen(extraHeaders)
00473       + strlen(contentLengthHeader)
00474       + contentStrLen;
00475     cmd = new char[cmdSize];
00476     sprintf(cmd, cmdFmt,
00477             request->commandName(), cmdURL, protocolStr,
00478             request->cseq(),
00479             authenticatorStr,
00480             fUserAgentHeaderStr,
00481             extraHeaders,
00482             contentLengthHeader,
00483             contentStr);
00484     delete[] authenticatorStr;
00485     if (cmdURLWasAllocated) delete[] cmdURL;
00486     if (extraHeadersWereAllocated) delete[] extraHeaders;
00487     if (contentLengthHeaderWasAllocated) delete[] contentLengthHeader;
00488 
00489     if (fVerbosityLevel >= 1) envir() << "Sending request: " << cmd << "\n";
00490 
00491     if (fTunnelOverHTTPPortNum != 0 && strcmp(request->commandName(), "GET") != 0 && strcmp(request->commandName(), "POST") != 0) {
00492       // When we're tunneling RTSP-over-HTTP, we Base-64-encode the request before we send it.
00493       // (However, we don't do this for the HTTP "GET" and "POST" commands that we use to set up the tunnel.)
00494       char* origCmd = cmd;
00495       cmd = base64Encode(origCmd, strlen(cmd));
00496       if (fVerbosityLevel >= 1) envir() << "\tThe request was base-64 encoded to: " << cmd << "\n\n";
00497       delete[] origCmd;
00498     }
00499 
00500     if (send(fOutputSocketNum, cmd, strlen(cmd), 0) < 0) {
00501       char const* errFmt = "%s send() failed: ";
00502       unsigned const errLength = strlen(errFmt) + strlen(request->commandName());
00503       char* err = new char[errLength];
00504       sprintf(err, errFmt, request->commandName());
00505       envir().setResultErrMsg(err);
00506       delete[] err;
00507       break;
00508     }
00509 
00510     // The command send succeeded, so enqueue the request record, so that its response (when it comes) can be handled.
00511     // However, note that we do not expect a response to a POST command with RTSP-over-HTTP, so don't enqueue that.
00512     int cseq = request->cseq();
00513 
00514     if (fTunnelOverHTTPPortNum == 0 || strcmp(request->commandName(), "POST") != 0) {
00515       fRequestsAwaitingResponse.enqueue(request);
00516     } else {
00517       delete request;
00518     }
00519 
00520     delete[] cmd;
00521     return cseq;
00522   } while (0);
00523 
00524   // An error occurred, so call the response handler immediately (indicating the error):
00525   delete[] cmd;
00526   handleRequestError(request);
00527   delete request;
00528   return 0;
00529 }
00530 
00531 static char* createSessionString(char const* sessionId) {
00532   char* sessionStr;
00533   if (sessionId != NULL) {
00534     sessionStr = new char[20+strlen(sessionId)];
00535     sprintf(sessionStr, "Session: %s\r\n", sessionId);
00536   } else {
00537     sessionStr = strDup("");
00538   }
00539   return sessionStr;
00540 }
00541 
00542 static char* createScaleString(float scale, float currentScale) {
00543   char buf[100];
00544   if (scale == 1.0f && currentScale == 1.0f) {
00545     // This is the default value; we don't need a "Scale:" header:
00546     buf[0] = '\0';
00547   } else {
00548     Locale l("C", Numeric);
00549     sprintf(buf, "Scale: %f\r\n", scale);
00550   }
00551 
00552   return strDup(buf);
00553 }
00554 
00555 static char* createRangeString(double start, double end, char const* absStartTime, char const* absEndTime) {
00556   char buf[100];
00557 
00558   if (absStartTime != NULL) {
00559     // Create a "Range:" header that specifies 'absolute' time values:
00560 
00561     if (absEndTime == NULL) {
00562       // There's no end time:
00563       snprintf(buf, sizeof buf, "Range: clock=%s-\r\n", absStartTime);
00564     } else {
00565       // There's both a start and an end time; include them both in the "Range:" hdr
00566       snprintf(buf, sizeof buf, "Range: clock=%s-%s\r\n", absStartTime, absEndTime);
00567     }
00568   } else {
00569     // Create a "Range:" header that specifies relative (i.e., NPT) time values:
00570 
00571     if (start < 0) {
00572       // We're resuming from a PAUSE; there's no "Range:" header at all
00573       buf[0] = '\0';
00574     } else if (end < 0) {
00575       // There's no end time:
00576       Locale l("C", Numeric);
00577       sprintf(buf, "Range: npt=%.3f-\r\n", start);
00578     } else {
00579       // There's both a start and an end time; include them both in the "Range:" hdr
00580       Locale l("C", Numeric);
00581       sprintf(buf, "Range: npt=%.3f-%.3f\r\n", start, end);
00582     }
00583   }
00584 
00585   return strDup(buf);
00586 }
00587 
00588 Boolean RTSPClient::setRequestFields(RequestRecord* request,
00589                                      char*& cmdURL, Boolean& cmdURLWasAllocated,
00590                                      char const*& protocolStr,
00591                                      char*& extraHeaders, Boolean& extraHeadersWereAllocated
00592                                      ) {
00593   // Set various fields that will appear in our outgoing request, depending upon the particular command that we are sending.
00594 
00595   if (strcmp(request->commandName(), "DESCRIBE") == 0) {
00596     extraHeaders = (char*)"Accept: application/sdp\r\n";
00597   } else if (strcmp(request->commandName(), "OPTIONS") == 0) {
00598     // If we're currently part of a session, create a "Session:" header (in case the server wants this to indicate
00599     // client 'liveness); this makes up our 'extra headers':
00600     extraHeaders = createSessionString(fLastSessionId);
00601     extraHeadersWereAllocated = True;
00602   } else if (strcmp(request->commandName(), "ANNOUNCE") == 0) {
00603     extraHeaders = (char*)"Content-Type: application/sdp\r\n";
00604   } else if (strcmp(request->commandName(), "SETUP") == 0) {
00605     MediaSubsession& subsession = *request->subsession();
00606     Boolean streamUsingTCP = (request->booleanFlags()&0x1) != 0;
00607     Boolean streamOutgoing = (request->booleanFlags()&0x2) != 0;
00608     Boolean forceMulticastOnUnspecified = (request->booleanFlags()&0x4) != 0;
00609     
00610     char const *prefix, *separator, *suffix;
00611     constructSubsessionURL(subsession, prefix, separator, suffix);
00612     
00613     char const* transportFmt;
00614     if (strcmp(subsession.protocolName(), "UDP") == 0) {
00615       suffix = "";
00616       transportFmt = "Transport: RAW/RAW/UDP%s%s%s=%d-%d\r\n";
00617     } else {
00618       transportFmt = "Transport: RTP/AVP%s%s%s=%d-%d\r\n";
00619     }
00620     
00621     cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1];
00622     cmdURLWasAllocated = True;
00623     sprintf(cmdURL, "%s%s%s", prefix, separator, suffix);
00624     
00625     // Construct a "Transport:" header.
00626     char const* transportTypeStr;
00627     char const* modeStr = streamOutgoing ? ";mode=receive" : "";
00628     // Note: I think the above is nonstandard, but DSS wants it this way
00629     char const* portTypeStr;
00630     portNumBits rtpNumber, rtcpNumber;
00631     if (streamUsingTCP) { // streaming over the RTSP connection
00632       transportTypeStr = "/TCP;unicast";
00633       portTypeStr = ";interleaved";
00634       rtpNumber = fTCPStreamIdCount++;
00635       rtcpNumber = fTCPStreamIdCount++;
00636     } else { // normal RTP streaming
00637       unsigned connectionAddress = subsession.connectionEndpointAddress();
00638       Boolean requestMulticastStreaming
00639         = IsMulticastAddress(connectionAddress) || (connectionAddress == 0 && forceMulticastOnUnspecified);
00640       transportTypeStr = requestMulticastStreaming ? ";multicast" : ";unicast";
00641       portTypeStr = ";client_port";
00642       rtpNumber = subsession.clientPortNum();
00643       if (rtpNumber == 0) {
00644         envir().setResultMsg("Client port number unknown\n");
00645         delete[] cmdURL;
00646         return False;
00647       }
00648       rtcpNumber = subsession.rtcpIsMuxed() ? rtpNumber : rtpNumber + 1;
00649     }
00650     unsigned transportSize = strlen(transportFmt)
00651       + strlen(transportTypeStr) + strlen(modeStr) + strlen(portTypeStr) + 2*5 /* max port len */;
00652     char* transportStr = new char[transportSize];
00653     sprintf(transportStr, transportFmt,
00654             transportTypeStr, modeStr, portTypeStr, rtpNumber, rtcpNumber);
00655     
00656     // When sending more than one "SETUP" request, include a "Session:" header in the 2nd and later commands:
00657     char* sessionStr = createSessionString(fLastSessionId);
00658     
00659     // The "Transport:" and "Session:" (if present) headers make up the 'extra headers':
00660     extraHeaders = new char[transportSize + strlen(sessionStr)];
00661     extraHeadersWereAllocated = True;
00662     sprintf(extraHeaders, "%s%s", transportStr, sessionStr);
00663     delete[] transportStr; delete[] sessionStr;
00664   } else if (strcmp(request->commandName(), "GET") == 0 || strcmp(request->commandName(), "POST") == 0) {
00665     // We will be sending a HTTP (not a RTSP) request.
00666     // Begin by re-parsing our RTSP URL, to get the stream name (which we'll use as our 'cmdURL'
00667     // in the subsequent request), and the server address (which we'll use in a "Host:" header):
00668     char* username;
00669     char* password;
00670     NetAddress destAddress;
00671     portNumBits urlPortNum;
00672     if (!parseRTSPURL(envir(), fBaseURL, username, password, destAddress, urlPortNum, (char const**)&cmdURL)) return False;
00673     if (cmdURL[0] == '\0') cmdURL = (char*)"/";
00674     delete[] username;
00675     delete[] password;
00676     netAddressBits serverAddress = *(netAddressBits*)(destAddress.data());
00677     AddressString serverAddressString(serverAddress);
00678     
00679     protocolStr = "HTTP/1.1";
00680     
00681     if (strcmp(request->commandName(), "GET") == 0) {
00682       // Create a 'session cookie' string, using MD5:
00683       struct {
00684         struct timeval timestamp;
00685         unsigned counter;
00686       } seedData;
00687       gettimeofday(&seedData.timestamp, NULL);
00688       seedData.counter = ++fSessionCookieCounter;
00689       our_MD5Data((unsigned char*)(&seedData), sizeof seedData, fSessionCookie);
00690       // DSS seems to require that the 'session cookie' string be 22 bytes long:
00691       fSessionCookie[23] = '\0';
00692       
00693       char const* const extraHeadersFmt =
00694         "Host: %s\r\n"
00695         "x-sessioncookie: %s\r\n"
00696         "Accept: application/x-rtsp-tunnelled\r\n"
00697         "Pragma: no-cache\r\n"
00698         "Cache-Control: no-cache\r\n";
00699       unsigned extraHeadersSize = strlen(extraHeadersFmt)
00700         + strlen(serverAddressString.val())
00701         + strlen(fSessionCookie);
00702       extraHeaders = new char[extraHeadersSize];
00703       extraHeadersWereAllocated = True;
00704       sprintf(extraHeaders, extraHeadersFmt,
00705               serverAddressString.val(),
00706               fSessionCookie);
00707     } else { // "POST"
00708       char const* const extraHeadersFmt =
00709         "Host: %s\r\n"
00710         "x-sessioncookie: %s\r\n"
00711         "Content-Type: application/x-rtsp-tunnelled\r\n"
00712         "Pragma: no-cache\r\n"
00713         "Cache-Control: no-cache\r\n"
00714         "Content-Length: 32767\r\n"
00715         "Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n";
00716       unsigned extraHeadersSize = strlen(extraHeadersFmt)
00717         + strlen(serverAddressString.val())
00718         + strlen(fSessionCookie);
00719       extraHeaders = new char[extraHeadersSize];
00720       extraHeadersWereAllocated = True;
00721       sprintf(extraHeaders, extraHeadersFmt,
00722               serverAddressString.val(),
00723               fSessionCookie);
00724     }
00725   } else { // "PLAY", "PAUSE", "TEARDOWN", "RECORD", "SET_PARAMETER", "GET_PARAMETER"
00726     // First, make sure that we have a RTSP session in progress
00727     if (fLastSessionId == NULL) {
00728       envir().setResultMsg("No RTSP session is currently in progress\n");
00729       return False;
00730     }
00731     
00732     char const* sessionId;
00733     float originalScale;
00734     if (request->session() != NULL) {
00735       // Session-level operation
00736       cmdURL = (char*)sessionURL(*request->session());
00737       
00738       sessionId = fLastSessionId;
00739       originalScale = request->session()->scale();
00740     } else {
00741       // Media-level operation
00742       char const *prefix, *separator, *suffix;
00743       constructSubsessionURL(*request->subsession(), prefix, separator, suffix);
00744       cmdURL = new char[strlen(prefix) + strlen(separator) + strlen(suffix) + 1];
00745       cmdURLWasAllocated = True;
00746       sprintf(cmdURL, "%s%s%s", prefix, separator, suffix);
00747       
00748       sessionId = request->subsession()->sessionId();
00749       originalScale = request->subsession()->scale();
00750     }
00751     
00752     if (strcmp(request->commandName(), "PLAY") == 0) {
00753       // Create "Session:", "Scale:", and "Range:" headers; these make up the 'extra headers':
00754       char* sessionStr = createSessionString(sessionId);
00755       char* scaleStr = createScaleString(request->scale(), originalScale);
00756       char* rangeStr = createRangeString(request->start(), request->end(), request->absStartTime(), request->absEndTime());
00757       extraHeaders = new char[strlen(sessionStr) + strlen(scaleStr) + strlen(rangeStr) + 1];
00758       extraHeadersWereAllocated = True;
00759       sprintf(extraHeaders, "%s%s%s", sessionStr, scaleStr, rangeStr);
00760       delete[] sessionStr; delete[] scaleStr; delete[] rangeStr;
00761     } else {
00762       // Create a "Session:" header; this makes up our 'extra headers':
00763       extraHeaders = createSessionString(sessionId);
00764       extraHeadersWereAllocated = True;
00765     }
00766   }
00767 
00768   return True;
00769 }
00770 
00771 Boolean RTSPClient::isRTSPClient() const {
00772   return True;
00773 }
00774 
00775 void RTSPClient::resetTCPSockets() {
00776   if (fInputSocketNum >= 0) {
00777     envir().taskScheduler().disableBackgroundHandling(fInputSocketNum);
00778     ::closeSocket(fInputSocketNum);
00779     if (fOutputSocketNum != fInputSocketNum) {
00780       envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum);
00781       ::closeSocket(fOutputSocketNum);
00782     }
00783   }
00784   fInputSocketNum = fOutputSocketNum = -1;
00785 }
00786 
00787 void RTSPClient::resetResponseBuffer() {
00788   fResponseBytesAlreadySeen = 0;
00789   fResponseBufferBytesLeft = responseBufferSize;
00790 }
00791 
00792 int RTSPClient::openConnection() {
00793   do {
00794     // Set up a connection to the server.  Begin by parsing the URL:
00795     
00796     char* username;
00797     char* password;
00798     NetAddress destAddress;
00799     portNumBits urlPortNum;
00800     char const* urlSuffix;
00801     if (!parseRTSPURL(envir(), fBaseURL, username, password, destAddress, urlPortNum, &urlSuffix)) break;
00802     portNumBits destPortNum = fTunnelOverHTTPPortNum == 0 ? urlPortNum : fTunnelOverHTTPPortNum;
00803     if (username != NULL || password != NULL) {
00804       fCurrentAuthenticator.setUsernameAndPassword(username, password);
00805       delete[] username;
00806       delete[] password;
00807     }
00808     
00809     // We don't yet have a TCP socket (or we used to have one, but it got closed).  Set it up now.
00810     fInputSocketNum = fOutputSocketNum = setupStreamSocket(envir(), 0);
00811     if (fInputSocketNum < 0) break;
00812     ignoreSigPipeOnSocket(fInputSocketNum); // so that servers on the same host that get killed don't also kill us
00813       
00814     // Connect to the remote endpoint:
00815     fServerAddress = *(netAddressBits*)(destAddress.data());
00816     int connectResult = connectToServer(fInputSocketNum, destPortNum);
00817     if (connectResult < 0) break;
00818     else if (connectResult > 0) {
00819       // The connection succeeded.  Arrange to handle responses to requests sent on it:
00820       envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION,
00821                                                     (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
00822     }
00823     return connectResult;
00824   } while (0);
00825   
00826   resetTCPSockets();
00827   return -1;
00828 }
00829 
00830 int RTSPClient::connectToServer(int socketNum, portNumBits remotePortNum) {
00831   MAKE_SOCKADDR_IN(remoteName, fServerAddress, htons(remotePortNum));
00832   if (fVerbosityLevel >= 1) {
00833     envir() << "Opening connection to " << AddressString(remoteName).val() << ", port " << remotePortNum << "...\n";
00834   }
00835   if (connect(socketNum, (struct sockaddr*) &remoteName, sizeof remoteName) != 0) {
00836     int const err = envir().getErrno();
00837     if (err == EINPROGRESS || err == EWOULDBLOCK) {
00838       // The connection is pending; we'll need to handle it later.  Wait for our socket to be 'writable', or have an exception.
00839       envir().taskScheduler().setBackgroundHandling(socketNum, SOCKET_WRITABLE|SOCKET_EXCEPTION,
00840                                                     (TaskScheduler::BackgroundHandlerProc*)&connectionHandler, this);
00841       return 0;
00842     }
00843     envir().setResultErrMsg("connect() failed: ");
00844     if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n";
00845     return -1;
00846   }
00847   if (fVerbosityLevel >= 1) envir() << "...local connection opened\n";
00848 
00849   return 1;
00850 }
00851 
00852 char* RTSPClient::createAuthenticatorString(char const* cmd, char const* url) {
00853   Authenticator& auth = fCurrentAuthenticator; // alias, for brevity
00854   if (auth.realm() != NULL && auth.username() != NULL && auth.password() != NULL) {
00855     // We have a filled-in authenticator, so use it:
00856     char* authenticatorStr;
00857     if (auth.nonce() != NULL) { // Digest authentication
00858       char const* const authFmt =
00859         "Authorization: Digest username=\"%s\", realm=\"%s\", "
00860         "nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n";
00861       char const* response = auth.computeDigestResponse(cmd, url);
00862       unsigned authBufSize = strlen(authFmt)
00863         + strlen(auth.username()) + strlen(auth.realm())
00864         + strlen(auth.nonce()) + strlen(url) + strlen(response);
00865       authenticatorStr = new char[authBufSize];
00866       sprintf(authenticatorStr, authFmt,
00867               auth.username(), auth.realm(),
00868               auth.nonce(), url, response);
00869       auth.reclaimDigestResponse(response);
00870     } else { // Basic authentication
00871       char const* const authFmt = "Authorization: Basic %s\r\n";
00872 
00873       unsigned usernamePasswordLength = strlen(auth.username()) + 1 + strlen(auth.password());
00874       char* usernamePassword = new char[usernamePasswordLength+1];
00875       sprintf(usernamePassword, "%s:%s", auth.username(), auth.password());
00876 
00877       char* response = base64Encode(usernamePassword, usernamePasswordLength);
00878       unsigned const authBufSize = strlen(authFmt) + strlen(response) + 1;
00879       authenticatorStr = new char[authBufSize];
00880       sprintf(authenticatorStr, authFmt, response);
00881       delete[] response; delete[] usernamePassword;
00882     }
00883 
00884     return authenticatorStr;
00885   }
00886 
00887   // We don't have a (filled-in) authenticator.
00888   return strDup("");
00889 }
00890 
00891 void RTSPClient::handleRequestError(RequestRecord* request) {
00892   int resultCode = -envir().getErrno();
00893   if (resultCode == 0) {
00894     // Choose some generic error code instead:
00895 #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
00896     resultCode = -WSAENOTCONN;
00897 #else
00898     resultCode = -ENOTCONN;
00899 #endif
00900   }
00901   if (request->handler() != NULL) (*request->handler())(this, resultCode, strDup(envir().getResultMsg()));
00902 }
00903 
00904 Boolean RTSPClient
00905 ::parseResponseCode(char const* line, unsigned& responseCode, char const*& responseString) {
00906   if (sscanf(line, "RTSP/%*s%u", &responseCode) != 1 &&
00907       sscanf(line, "HTTP/%*s%u", &responseCode) != 1) return False;
00908   // Note: We check for HTTP responses as well as RTSP responses, both in order to setup RTSP-over-HTTP tunneling,
00909   // and so that we get back a meaningful error if the client tried to mistakenly send a RTSP command to a HTTP-only server.
00910 
00911   // Use everything after the RTSP/* (or HTTP/*) as the response string:
00912   responseString = line;
00913   while (responseString[0] != '\0' && responseString[0] != ' '  && responseString[0] != '\t') ++responseString;
00914   while (responseString[0] != '\0' && (responseString[0] == ' '  || responseString[0] == '\t')) ++responseString; // skip whitespace
00915 
00916   return True;
00917 }
00918 
00919 void RTSPClient::handleIncomingRequest() {
00920   // Parse the request string into command name and 'CSeq', then 'handle' the command (by responding that we don't support it):
00921   char cmdName[RTSP_PARAM_STRING_MAX];
00922   char urlPreSuffix[RTSP_PARAM_STRING_MAX];
00923   char urlSuffix[RTSP_PARAM_STRING_MAX];
00924   char cseq[RTSP_PARAM_STRING_MAX];
00925   char sessionId[RTSP_PARAM_STRING_MAX];
00926   unsigned contentLength;
00927   if (!parseRTSPRequestString(fResponseBuffer, fResponseBytesAlreadySeen,
00928                               cmdName, sizeof cmdName,
00929                               urlPreSuffix, sizeof urlPreSuffix,
00930                               urlSuffix, sizeof urlSuffix,
00931                               cseq, sizeof cseq,
00932                               sessionId, sizeof sessionId,
00933                               contentLength)) {
00934     return;
00935   } else {
00936     if (fVerbosityLevel >= 1) {
00937       envir() << "Received incoming RTSP request: " << fResponseBuffer << "\n";
00938     }
00939     char tmpBuf[2*RTSP_PARAM_STRING_MAX];
00940     snprintf((char*)tmpBuf, sizeof tmpBuf,
00941              "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n\r\n", cseq);
00942     send(fOutputSocketNum, tmpBuf, strlen(tmpBuf), 0);
00943   }
00944 }
00945 
00946 Boolean RTSPClient::checkForHeader(char const* line, char const* headerName, unsigned headerNameLength, char const*& headerParams) {
00947   if (_strncasecmp(line, headerName, headerNameLength) != 0) return False;
00948 
00949   // The line begins with the desired header name.  Trim off any whitespace, and return the header parameters:
00950   unsigned paramIndex = headerNameLength;
00951   while (line[paramIndex] != '\0' && (line[paramIndex] == ' ' || line[paramIndex] == '\t')) ++paramIndex;
00952   if (line[paramIndex] == '\0') return False; // the header is assumed to be bad if it has no parameters
00953 
00954   headerParams = &line[paramIndex];
00955   return True;
00956 }
00957 
00958 Boolean RTSPClient::parseTransportParams(char const* paramsStr,
00959                                          char*& serverAddressStr, portNumBits& serverPortNum,
00960                                          unsigned char& rtpChannelId, unsigned char& rtcpChannelId) {
00961   // Initialize the return parameters to 'not found' values:
00962   serverAddressStr = NULL;
00963   serverPortNum = 0;
00964   rtpChannelId = rtcpChannelId = 0xFF;
00965   if (paramsStr == NULL) return False;  
00966 
00967   char* foundServerAddressStr = NULL;
00968   Boolean foundServerPortNum = False;
00969   portNumBits clientPortNum = 0;
00970   Boolean foundClientPortNum = False;
00971   Boolean foundChannelIds = False;
00972   unsigned rtpCid, rtcpCid;
00973   Boolean isMulticast = True; // by default
00974   char* foundDestinationStr = NULL;
00975   portNumBits multicastPortNumRTP, multicastPortNumRTCP;
00976   Boolean foundMulticastPortNum = False;
00977 
00978   // Run through each of the parameters, looking for ones that we handle:
00979   char const* fields = paramsStr;
00980   char* field = strDupSize(fields);
00981   while (sscanf(fields, "%[^;]", field) == 1) {
00982     if (sscanf(field, "server_port=%hu", &serverPortNum) == 1) {
00983       foundServerPortNum = True;
00984     } else if (sscanf(field, "client_port=%hu", &clientPortNum) == 1) {
00985       foundClientPortNum = True;
00986     } else if (_strncasecmp(field, "source=", 7) == 0) {
00987       delete[] foundServerAddressStr;
00988       foundServerAddressStr = strDup(field+7);
00989     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
00990       rtpChannelId = (unsigned char)rtpCid;
00991       rtcpChannelId = (unsigned char)rtcpCid;
00992       foundChannelIds = True;
00993     } else if (strcmp(field, "unicast") == 0) {
00994       isMulticast = False;
00995     } else if (_strncasecmp(field, "destination=", 12) == 0) {
00996       delete[] foundDestinationStr;
00997       foundDestinationStr = strDup(field+12);
00998     } else if (sscanf(field, "port=%hu-%hu", &multicastPortNumRTP, &multicastPortNumRTCP) == 2 ||
00999                sscanf(field, "port=%hu", &multicastPortNumRTP) == 1) {
01000       foundMulticastPortNum = True;
01001     }
01002 
01003     fields += strlen(field);
01004     while (fields[0] == ';') ++fields; // skip over all leading ';' chars
01005     if (fields[0] == '\0') break;
01006   }
01007   delete[] field;
01008 
01009   // If we're multicast, and have a "destination=" (multicast) address, then use this
01010   // as the 'server' address (because some weird servers don't specify the multicast
01011   // address earlier, in the "DESCRIBE" response's SDP:
01012   if (isMulticast && foundDestinationStr != NULL && foundMulticastPortNum) {
01013     delete[] foundServerAddressStr;
01014     serverAddressStr = foundDestinationStr;
01015     serverPortNum = multicastPortNumRTP;
01016     return True;
01017   }
01018   delete[] foundDestinationStr;
01019 
01020   // We have a valid "Transport:" header if any of the following are true:
01021   //   - We saw a "interleaved=" field, indicating RTP/RTCP-over-TCP streaming, or
01022   //   - We saw a "server_port=" field, or
01023   //   - We saw a "client_port=" field.
01024   //     If we didn't also see a "server_port=" field, then the server port is assumed to be the same as the client port.
01025   if (foundChannelIds || foundServerPortNum || foundClientPortNum) {
01026     if (foundClientPortNum && !foundServerPortNum) {
01027       serverPortNum = clientPortNum;
01028     }
01029     serverAddressStr = foundServerAddressStr;
01030     return True;
01031   }
01032 
01033   delete[] foundServerAddressStr;
01034   return False;
01035 }
01036 
01037 Boolean RTSPClient::parseScaleParam(char const* paramStr, float& scale) {
01038   Locale l("C", Numeric);
01039   return sscanf(paramStr, "%f", &scale) == 1;
01040 }
01041 
01042 Boolean RTSPClient::parseRTPInfoParams(char const*& paramsStr, u_int16_t& seqNum, u_int32_t& timestamp) {
01043   if (paramsStr == NULL || paramsStr[0] == '\0') return False;
01044   while (paramsStr[0] == ',') ++paramsStr;
01045 
01046   // "paramsStr" now consists of a ';'-separated list of parameters, ending with ',' or '\0'.
01047   char* field = strDupSize(paramsStr);
01048 
01049   Boolean sawSeq = False, sawRtptime = False;
01050   while (sscanf(paramsStr, "%[^;,]", field) == 1) {
01051     if (sscanf(field, "seq=%hu", &seqNum) == 1) {
01052       sawSeq = True;
01053     } else if (sscanf(field, "rtptime=%u", &timestamp) == 1) {
01054       sawRtptime = True;
01055     }
01056 
01057     paramsStr += strlen(field);
01058     if (paramsStr[0] == '\0' || paramsStr[0] == ',') break;
01059     // ASSERT: paramsStr[0] == ';'
01060     ++paramsStr; // skip over the ';'
01061   }
01062 
01063   delete[] field;
01064   // For the "RTP-Info:" parameters to be useful to us, we need to have seen both the "seq=" and "rtptime=" parameters:
01065   return sawSeq && sawRtptime;
01066 }
01067 
01068 Boolean RTSPClient::handleSETUPResponse(MediaSubsession& subsession, char const* sessionParamsStr, char const* transportParamsStr,
01069                                         Boolean streamUsingTCP) {
01070   char* sessionId = new char[responseBufferSize]; // ensures we have enough space
01071   Boolean success = False;
01072   do {
01073     // Check for a session id:
01074     if (sessionParamsStr == NULL || sscanf(sessionParamsStr, "%[^;]", sessionId) != 1) {
01075       envir().setResultMsg("Missing or bad \"Session:\" header");
01076       break;
01077     }
01078     subsession.setSessionId(sessionId);
01079     delete[] fLastSessionId; fLastSessionId = strDup(sessionId);
01080 
01081     // Also look for an optional "; timeout = " parameter following this:
01082     char const* afterSessionId = sessionParamsStr + strlen(sessionId);
01083     int timeoutVal;
01084     if (sscanf(afterSessionId, "; timeout = %d", &timeoutVal) == 1) {
01085       fSessionTimeoutParameter = timeoutVal;
01086     }
01087 
01088     // Parse the "Transport:" header parameters:
01089     char* serverAddressStr;
01090     portNumBits serverPortNum;
01091     unsigned char rtpChannelId, rtcpChannelId;
01092     if (!parseTransportParams(transportParamsStr, serverAddressStr, serverPortNum, rtpChannelId, rtcpChannelId)) {
01093       envir().setResultMsg("Missing or bad \"Transport:\" header");
01094       break;
01095     }
01096     delete[] subsession.connectionEndpointName();
01097     subsession.connectionEndpointName() = serverAddressStr;
01098     subsession.serverPortNum = serverPortNum;
01099     subsession.rtpChannelId = rtpChannelId;
01100     subsession.rtcpChannelId = rtcpChannelId;
01101 
01102     if (streamUsingTCP) {
01103       // Tell the subsession to receive RTP (and send/receive RTCP) over the RTSP stream:
01104       if (subsession.rtpSource() != NULL) {
01105         subsession.rtpSource()->setStreamSocket(fInputSocketNum, subsession.rtpChannelId);
01106           // So that we continue to receive & handle RTSP commands and responses from the server
01107         subsession.rtpSource()->enableRTCPReports() = False;
01108           // To avoid confusing the server (which won't start handling RTP/RTCP-over-TCP until "PLAY"), don't send RTCP "RR"s yet
01109       }
01110       if (subsession.rtcpInstance() != NULL) subsession.rtcpInstance()->setStreamSocket(fInputSocketNum, subsession.rtcpChannelId);
01111       RTPInterface::setServerRequestAlternativeByteHandler(envir(), fInputSocketNum, handleAlternativeRequestByte, this);
01112     } else {
01113       // Normal case.
01114       // Set the RTP and RTCP sockets' destination address and port from the information in the SETUP response (if present):
01115       netAddressBits destAddress = subsession.connectionEndpointAddress();
01116       if (destAddress == 0) destAddress = fServerAddress;
01117       subsession.setDestinations(destAddress);
01118     }
01119 
01120     success = True;
01121   } while (0);
01122 
01123   delete[] sessionId;
01124   return success;
01125 }
01126 
01127 Boolean RTSPClient::handlePLAYResponse(MediaSession& session, MediaSubsession& subsession,
01128                                        char const* scaleParamsStr, char const* rangeParamsStr, char const* rtpInfoParamsStr) {
01129   Boolean scaleOK = False, rangeOK = False;
01130   do {
01131     if (&session != NULL) {
01132       // The command was on the whole session
01133       if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, session.scale())) break;
01134       scaleOK = True;
01135       Boolean startTimeIsNow;
01136       if (rangeParamsStr != NULL &&
01137           !parseRangeParam(rangeParamsStr,
01138                            session.playStartTime(), session.playEndTime(),
01139                            session._absStartTime(), session._absEndTime(),
01140                            startTimeIsNow)) break;
01141       rangeOK = True;
01142 
01143       MediaSubsessionIterator iter(session);
01144       MediaSubsession* subsession;
01145       while ((subsession = iter.next()) != NULL) {
01146         u_int16_t seqNum; u_int32_t timestamp;
01147         subsession->rtpInfo.infoIsNew = False;
01148         if (parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) {
01149           subsession->rtpInfo.seqNum = seqNum;
01150           subsession->rtpInfo.timestamp = timestamp;
01151           subsession->rtpInfo.infoIsNew = True;
01152         }
01153 
01154         if (subsession->rtpSource() != NULL) subsession->rtpSource()->enableRTCPReports() = True; // start sending RTCP "RR"s now
01155       }
01156     } else {
01157       // The command was on a subsession
01158       if (scaleParamsStr != NULL && !parseScaleParam(scaleParamsStr, subsession.scale())) break;
01159       scaleOK = True;
01160       Boolean startTimeIsNow;
01161       if (rangeParamsStr != NULL &&
01162           !parseRangeParam(rangeParamsStr,
01163                            subsession._playStartTime(), subsession._playEndTime(),
01164                            subsession._absStartTime(), subsession._absEndTime(),
01165                            startTimeIsNow)) break;
01166       rangeOK = True;
01167 
01168       u_int16_t seqNum; u_int32_t timestamp;
01169       subsession.rtpInfo.infoIsNew = False;
01170       if (parseRTPInfoParams(rtpInfoParamsStr, seqNum, timestamp)) {
01171         subsession.rtpInfo.seqNum = seqNum;
01172         subsession.rtpInfo.timestamp = timestamp;
01173         subsession.rtpInfo.infoIsNew = True;
01174       }
01175 
01176       if (subsession.rtpSource() != NULL) subsession.rtpSource()->enableRTCPReports() = True; // start sending RTCP "RR"s now
01177     }
01178 
01179     return True;
01180   } while (0);
01181 
01182   // An error occurred:
01183   if (!scaleOK) {
01184     envir().setResultMsg("Bad \"Scale:\" header");
01185   } else if (!rangeOK) {
01186     envir().setResultMsg("Bad \"Range:\" header");
01187   } else {
01188     envir().setResultMsg("Bad \"RTP-Info:\" header");
01189   }
01190   return False;
01191 }
01192 
01193 Boolean RTSPClient::handleTEARDOWNResponse(MediaSession& /*session*/, MediaSubsession& /*subsession*/) {
01194   // Because we don't expect to always get a response to "TEARDOWN", we don't need to do anything if we do get one:
01195   return True;
01196 }
01197 
01198 Boolean RTSPClient::handleGET_PARAMETERResponse(char const* parameterName, char*& resultValueString) {
01199   do {
01200     // If "parameterName" is non-empty, it may be (possibly followed by ':' and whitespace) at the start of the result string:
01201     if (parameterName != NULL && parameterName[0] != '\0') {
01202       if (parameterName[1] == '\0') break; // sanity check; there should have been \r\n at the end of "parameterName"
01203 
01204       unsigned parameterNameLen = strlen(parameterName);
01205       // ASSERT: parameterNameLen >= 2;
01206       parameterNameLen -= 2; // because of the trailing \r\n
01207       if (_strncasecmp(resultValueString, parameterName, parameterNameLen) == 0) {
01208         resultValueString += parameterNameLen;
01209         if (resultValueString[0] == ':') ++resultValueString;
01210         while (resultValueString[0] == ' ' || resultValueString[0] == '\t') ++resultValueString;
01211       }
01212     }
01213 
01214     // The rest of "resultValueStr" should be our desired result, but first trim off any \r and/or \n characters at the end:
01215     unsigned resultLen = strlen(resultValueString);
01216     while (resultLen > 0 && (resultValueString[resultLen-1] == '\r' || resultValueString[resultLen-1] == '\n')) --resultLen;
01217     resultValueString[resultLen] = '\0';
01218 
01219     return True;
01220   } while (0);
01221 
01222   // An error occurred:
01223   envir().setResultMsg("Bad \"GET_PARAMETER\" response");
01224   return False;
01225 }
01226 
01227 Boolean RTSPClient::handleAuthenticationFailure(char const* paramsStr) {
01228   if (paramsStr == NULL) return False; // There was no "WWW-Authenticate:" header; we can't proceed.
01229 
01230   // Fill in "fCurrentAuthenticator" with the information from the "WWW-Authenticate:" header:
01231   Boolean alreadyHadRealm = fCurrentAuthenticator.realm() != NULL;
01232   char* realm = strDupSize(paramsStr);
01233   char* nonce = strDupSize(paramsStr);
01234   Boolean success = True;
01235   if (sscanf(paramsStr, "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
01236     fCurrentAuthenticator.setRealmAndNonce(realm, nonce);
01237   } else if (sscanf(paramsStr, "Basic realm=\"%[^\"]\"", realm) == 1) {
01238     fCurrentAuthenticator.setRealmAndNonce(realm, NULL); // Basic authentication
01239   } else {
01240     success = False; // bad "WWW-Authenticate:" header
01241   }
01242   delete[] realm; delete[] nonce;
01243 
01244   if (alreadyHadRealm || fCurrentAuthenticator.username() == NULL || fCurrentAuthenticator.password() == NULL) {
01245     // We already had a 'realm', or don't have a username and/or password,
01246     // so the new "WWW-Authenticate:" header information won't help us.  We remain unauthenticated.
01247     success = False;
01248   }
01249 
01250   return success;
01251 }
01252 
01253 Boolean RTSPClient::resendCommand(RequestRecord* request) {
01254   if (fVerbosityLevel >= 1) envir() << "Resending...\n";
01255   if (request != NULL && strcmp(request->commandName(), "GET") != 0) request->cseq() = ++fCSeq;
01256   return sendRequest(request) != 0;
01257 }
01258 
01259 char const* RTSPClient::sessionURL(MediaSession const& session) const {
01260   char const* url = session.controlPath();
01261   if (url == NULL || strcmp(url, "*") == 0) url = fBaseURL;
01262 
01263   return url;
01264 }
01265 
01266 void RTSPClient::handleAlternativeRequestByte(void* rtspClient, u_int8_t requestByte) {
01267   ((RTSPClient*)rtspClient)->handleAlternativeRequestByte1(requestByte);
01268 }
01269 
01270 void RTSPClient::handleAlternativeRequestByte1(u_int8_t requestByte) {
01271   if (requestByte == 0xFF) {
01272     // Hack: The new handler of the input TCP socket encountered an error reading it.  Indicate this:
01273     handleResponseBytes(-1);
01274   } else if (requestByte == 0xFE) {
01275     // Another hack: The new handler of the input TCP socket no longer needs it, so take back control:
01276     envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION,
01277                                                   (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
01278   } else {
01279     // Normal case:
01280     fResponseBuffer[fResponseBytesAlreadySeen] = requestByte;
01281     handleResponseBytes(1);
01282   }
01283 }
01284 
01285 static Boolean isAbsoluteURL(char const* url) {
01286   // Assumption: "url" is absolute if it contains a ':', before any
01287   // occurrence of '/'
01288   while (*url != '\0' && *url != '/') {
01289     if (*url == ':') return True;
01290     ++url;
01291   }
01292 
01293   return False;
01294 }
01295 
01296 void RTSPClient::constructSubsessionURL(MediaSubsession const& subsession,
01297                                         char const*& prefix,
01298                                         char const*& separator,
01299                                         char const*& suffix) {
01300   // Figure out what the URL describing "subsession" will look like.
01301   // The URL is returned in three parts: prefix; separator; suffix
01302   //##### NOTE: This code doesn't really do the right thing if "sessionURL()"
01303   // doesn't end with a "/", and "subsession.controlPath()" is relative.
01304   // The right thing would have been to truncate "sessionURL()" back to the
01305   // rightmost "/", and then add "subsession.controlPath()".
01306   // In practice, though, each "DESCRIBE" response typically contains
01307   // a "Content-Base:" header that consists of "sessionURL()" followed by
01308   // a "/", in which case this code ends up giving the correct result.
01309   // However, we should really fix this code to do the right thing, and
01310   // also check for and use the "Content-Base:" header appropriately. #####
01311   prefix = sessionURL(subsession.parentSession());
01312   if (prefix == NULL) prefix = "";
01313 
01314   suffix = subsession.controlPath();
01315   if (suffix == NULL) suffix = "";
01316 
01317   if (isAbsoluteURL(suffix)) {
01318     prefix = separator = "";
01319   } else {
01320     unsigned prefixLen = strlen(prefix);
01321     separator = (prefixLen == 0 || prefix[prefixLen-1] == '/' || suffix[0] == '/') ? "" : "/";
01322   }
01323 }
01324 
01325 Boolean RTSPClient::setupHTTPTunneling1() {
01326   // Set up RTSP-over-HTTP tunneling, as described in
01327   //     http://developer.apple.com/quicktime/icefloe/dispatch028.html and http://images.apple.com/br/quicktime/pdf/QTSS_Modules.pdf
01328   if (fVerbosityLevel >= 1) {
01329     envir() << "Requesting RTSP-over-HTTP tunneling (on port " << fTunnelOverHTTPPortNum << ")\n\n";
01330   }
01331 
01332   // Begin by sending a HTTP "GET", to set up the server->client link.  Continue when we handle the response:
01333   return sendRequest(new RequestRecord(1, "GET", responseHandlerForHTTP_GET)) != 0;
01334 }
01335 
01336 void RTSPClient::responseHandlerForHTTP_GET(RTSPClient* rtspClient, int responseCode, char* responseString) {
01337   if (rtspClient != NULL) rtspClient->responseHandlerForHTTP_GET1(responseCode, responseString);
01338 }
01339 
01340 void RTSPClient::responseHandlerForHTTP_GET1(int responseCode, char* responseString) {
01341   RequestRecord* request;
01342   do {
01343     delete[] responseString; // we don't need it (but are responsible for deleting it)
01344     if (responseCode != 0) break; // The HTTP "GET" failed.
01345 
01346     // Having successfully set up (using the HTTP "GET" command) the server->client link, set up a second TCP connection
01347     // (to the same server & port as before) for the client->server link.  All future output will be to this new socket.
01348     fOutputSocketNum = setupStreamSocket(envir(), 0);
01349     if (fOutputSocketNum < 0) break;
01350     ignoreSigPipeOnSocket(fOutputSocketNum); // so that servers on the same host that killed don't also kill us
01351 
01352     fHTTPTunnelingConnectionIsPending = True;
01353     int connectResult = connectToServer(fOutputSocketNum, fTunnelOverHTTPPortNum);
01354     if (connectResult < 0) break; // an error occurred
01355     else if (connectResult == 0) {
01356       // A connection is pending.  Continue setting up RTSP-over-HTTP when the connection completes.
01357       // First, move the pending requests to the 'awaiting connection' queue:
01358       while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) {
01359         fRequestsAwaitingConnection.enqueue(request);
01360       }
01361       return;
01362     }
01363 
01364     // The connection succeeded.  Continue setting up RTSP-over-HTTP:
01365     if (!setupHTTPTunneling2()) break;
01366 
01367     // RTSP-over-HTTP tunneling succeeded.  Resume the pending request(s):
01368     while ((request = fRequestsAwaitingHTTPTunneling.dequeue()) != NULL) {
01369       sendRequest(request);
01370     }
01371     return;
01372   } while (0);
01373 
01374   // An error occurred.  Dequeue the pending request(s), and tell them about the error:
01375   fHTTPTunnelingConnectionIsPending = False;
01376   resetTCPSockets(); // do this now, in case an error handler deletes "this"
01377   RequestQueue requestQueue(fRequestsAwaitingHTTPTunneling);
01378   while ((request = requestQueue.dequeue()) != NULL) {
01379     handleRequestError(request);
01380     delete request;
01381   }
01382 }
01383 
01384 Boolean RTSPClient::setupHTTPTunneling2() {
01385   fHTTPTunnelingConnectionIsPending = False;
01386 
01387   // Send a HTTP "POST", to set up the client->server link.  (Note that we won't see a reply to the "POST".)
01388   return sendRequest(new RequestRecord(1, "POST", NULL)) != 0;
01389 }
01390 
01391 void RTSPClient::connectionHandler(void* instance, int /*mask*/) {
01392   RTSPClient* client = (RTSPClient*)instance;
01393   client->connectionHandler1();
01394 }
01395 
01396 void RTSPClient::connectionHandler1() {
01397   // Restore normal handling on our sockets:
01398   envir().taskScheduler().disableBackgroundHandling(fOutputSocketNum);
01399   envir().taskScheduler().setBackgroundHandling(fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION,
01400                                                 (TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler, this);
01401 
01402   // Move all requests awaiting connection into a new, temporary queue, to clear "fRequestsAwaitingConnection"
01403   // (so that "sendRequest()" doesn't get confused by "fRequestsAwaitingConnection" being nonempty, and enqueue them all over again).
01404   RequestQueue tmpRequestQueue(fRequestsAwaitingConnection);
01405   RequestRecord* request;
01406 
01407   // Find out whether the connection succeeded or failed:
01408   do {
01409     int err = 0;
01410     SOCKLEN_T len = sizeof err;
01411     if (getsockopt(fInputSocketNum, SOL_SOCKET, SO_ERROR, (char*)&err, &len) < 0 || err != 0) {
01412       envir().setResultErrMsg("Connection to server failed: ", err);
01413       if (fVerbosityLevel >= 1) envir() << "..." << envir().getResultMsg() << "\n";
01414       break;
01415     }
01416 
01417     // The connection succeeded.  If the connection came about from an attempt to set up RTSP-over-HTTP, finish this now:
01418     if (fVerbosityLevel >= 1) envir() << "...remote connection opened\n";
01419     if (fHTTPTunnelingConnectionIsPending && !setupHTTPTunneling2()) break;
01420 
01421     // Resume sending all pending requests:
01422     while ((request = tmpRequestQueue.dequeue()) != NULL) {
01423       sendRequest(request);
01424     }
01425     return;
01426   } while (0);
01427 
01428   // An error occurred.  Tell all pending requests about the error:
01429   resetTCPSockets(); // do this now, in case an error handler deletes "this"
01430   while ((request = tmpRequestQueue.dequeue()) != NULL) {
01431     handleRequestError(request);
01432     delete request;
01433   }
01434 }
01435 
01436 void RTSPClient::incomingDataHandler(void* instance, int /*mask*/) {
01437   RTSPClient* client = (RTSPClient*)instance;
01438   client->incomingDataHandler1();
01439 }
01440 
01441 void RTSPClient::incomingDataHandler1() {
01442   struct sockaddr_in dummy; // 'from' address - not used
01443 
01444   int bytesRead = readSocket(envir(), fInputSocketNum, (unsigned char*)&fResponseBuffer[fResponseBytesAlreadySeen], fResponseBufferBytesLeft, dummy);
01445   handleResponseBytes(bytesRead);
01446 }
01447 
01448 static char* getLine(char* startOfLine) {
01449   // returns the start of the next line, or NULL if none.  Note that this modifies the input string to add '\0' characters.
01450   for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
01451     // Check for the end of line: \r\n (but also accept \r or \n by itself):
01452     if (*ptr == '\r' || *ptr == '\n') {
01453       // We found the end of the line
01454       if (*ptr == '\r') {
01455         *ptr++ = '\0';
01456         if (*ptr == '\n') ++ptr;
01457       } else {
01458         *ptr++ = '\0';
01459       }
01460       return ptr;
01461     }
01462   }
01463 
01464   return NULL;
01465 }
01466 
01467 void RTSPClient::handleResponseBytes(int newBytesRead) {
01468   do {
01469     if (newBytesRead >= 0 && (unsigned)newBytesRead < fResponseBufferBytesLeft) break; // data was read OK; process it below
01470 
01471     if (newBytesRead >= (int)fResponseBufferBytesLeft) {
01472       // We filled up our response buffer.  Treat this as an error (for the first response handler):
01473       envir().setResultMsg("RTSP response was truncated. Increase \"RTSPClient::responseBufferSize\"");
01474     }
01475 
01476     // An error occurred while reading our TCP socket.  Call all pending response handlers, indicating this error.
01477     // (However, the "RTSP response was truncated" error is applied to the first response handler only.)
01478     resetResponseBuffer();
01479     RequestRecord* request;
01480     if (newBytesRead > 0) { // The "RTSP response was truncated" error
01481       if ((request = fRequestsAwaitingResponse.dequeue()) != NULL) {
01482         handleRequestError(request);
01483         delete request;
01484       }
01485     } else {
01486       RequestQueue requestQueue(fRequestsAwaitingResponse);
01487       resetTCPSockets(); // do this now, in case an error handler deletes "this"
01488 
01489       while ((request = requestQueue.dequeue()) != NULL) {
01490         handleRequestError(request);
01491         delete request;
01492       }
01493     }
01494     return;
01495   } while (0);
01496 
01497   fResponseBufferBytesLeft -= newBytesRead;
01498   fResponseBytesAlreadySeen += newBytesRead;
01499   fResponseBuffer[fResponseBytesAlreadySeen] = '\0';
01500   if (fVerbosityLevel >= 1 && newBytesRead > 1) envir() << "Received " << newBytesRead << " new bytes of response data.\n";
01501   
01502   unsigned numExtraBytesAfterResponse = 0;
01503   Boolean responseSuccess = False; // by default
01504   do {
01505     // Data was read OK.  Look through the data that we've read so far, to see if it contains <CR><LF><CR><LF>.
01506     // (If not, wait for more data to arrive.)
01507     Boolean endOfHeaders = False;
01508     char const* ptr = fResponseBuffer;
01509     if (fResponseBytesAlreadySeen > 3) {
01510       char const* const ptrEnd = &fResponseBuffer[fResponseBytesAlreadySeen-3];
01511       while (ptr < ptrEnd) {
01512         if (*ptr++ == '\r' && *ptr++ == '\n' && *ptr++ == '\r' && *ptr++ == '\n') {
01513           // This is it
01514           endOfHeaders = True;
01515           break;
01516         }
01517       }
01518     }
01519     
01520     if (!endOfHeaders) return; // subsequent reads will be needed to get the complete response
01521     
01522     // Now that we have the complete response headers (ending with <CR><LF><CR><LF>), parse them to get the response code, CSeq,
01523     // and various other header parameters.  To do this, we first make a copy of the received header data, because we'll be
01524     // modifying it by adding '\0' bytes.
01525     char* headerDataCopy;
01526     unsigned responseCode = 200;
01527     char const* responseStr = NULL;
01528     RequestRecord* foundRequest = NULL;
01529     char const* sessionParamsStr = NULL;
01530     char const* transportParamsStr = NULL;
01531     char const* scaleParamsStr = NULL;
01532     char const* rangeParamsStr = NULL;
01533     char const* rtpInfoParamsStr = NULL;
01534     char const* wwwAuthenticateParamsStr = NULL;
01535     char const* publicParamsStr = NULL;
01536     char* bodyStart = NULL;
01537     unsigned numBodyBytes = 0;
01538     responseSuccess = False;
01539     do {
01540       headerDataCopy = new char[responseBufferSize];
01541       strncpy(headerDataCopy, fResponseBuffer, fResponseBytesAlreadySeen);
01542       headerDataCopy[fResponseBytesAlreadySeen] = '\0';
01543       
01544       char* lineStart = headerDataCopy;
01545       char* nextLineStart = getLine(lineStart);
01546       if (!parseResponseCode(lineStart, responseCode, responseStr)) {
01547         // This does not appear to be a RTSP response; perhaps it's a RTSP request instead?
01548         handleIncomingRequest();
01549         break; // we're done with this data
01550       }
01551       
01552       // Scan through the headers, handling the ones that we're interested in:
01553       Boolean reachedEndOfHeaders;
01554       unsigned cseq = 0;
01555       unsigned contentLength = 0;
01556       
01557       while (1) {
01558         reachedEndOfHeaders = True; // by default; may get changed below
01559         lineStart = nextLineStart;
01560         if (lineStart == NULL) break;
01561         
01562         nextLineStart = getLine(lineStart);
01563         if (lineStart[0] == '\0') break; // this is a blank line
01564         reachedEndOfHeaders = False;
01565         
01566         char const* headerParamsStr; 
01567         if (checkForHeader(lineStart, "CSeq:", 5, headerParamsStr)) {
01568           if (sscanf(headerParamsStr, "%u", &cseq) != 1 || cseq <= 0) {
01569             envir().setResultMsg("Bad \"CSeq:\" header: \"", lineStart, "\"");
01570             break;
01571           }
01572           // Find the handler function for "cseq":
01573           RequestRecord* request;
01574           while ((request = fRequestsAwaitingResponse.dequeue()) != NULL) {
01575             if (request->cseq() < cseq) { // assumes that the CSeq counter will never wrap around
01576               // We never received (and will never receive) a response for this handler, so delete it:
01577               if (fVerbosityLevel >= 1 && strcmp(request->commandName(), "POST") != 0) {
01578                 envir() << "WARNING: The server did not respond to our \"" << request->commandName() << "\" request (CSeq: "
01579                         << request->cseq() << ").  The server appears to be buggy (perhaps not handling pipelined requests properly).\n";
01580               }
01581               delete request;
01582             } else if (request->cseq() == cseq) {
01583               // This is the handler that we want. Remove its record, but remember it, so that we can later call its handler:
01584               foundRequest = request;
01585               break;
01586             } else { // request->cseq() > cseq
01587               // No handler was registered for this response, so ignore it.
01588               break;
01589             }
01590           }
01591         } else if (checkForHeader(lineStart, "Content-Length:", 15, headerParamsStr)) {
01592           if (sscanf(headerParamsStr, "%u", &contentLength) != 1) {
01593             envir().setResultMsg("Bad \"Content-Length:\" header: \"", lineStart, "\"");
01594             break;
01595           }
01596         } else if (checkForHeader(lineStart, "Content-Base:", 13, headerParamsStr)) {
01597           setBaseURL(headerParamsStr);
01598         } else if (checkForHeader(lineStart, "Session:", 8, sessionParamsStr)) {
01599         } else if (checkForHeader(lineStart, "Transport:", 10, transportParamsStr)) {
01600         } else if (checkForHeader(lineStart, "Scale:", 6, scaleParamsStr)) {
01601         } else if (checkForHeader(lineStart, "Range:", 6, rangeParamsStr)) {
01602         } else if (checkForHeader(lineStart, "RTP-Info:", 9, rtpInfoParamsStr)) {
01603         } else if (checkForHeader(lineStart, "WWW-Authenticate:", 17, headerParamsStr)) {
01604           // If we've already seen a "WWW-Authenticate:" header, then we replace it with this new one only if
01605           // the new one specifies "Digest" authentication:
01606           if (wwwAuthenticateParamsStr == NULL || _strncasecmp(headerParamsStr, "Digest", 6) == 0) {
01607             wwwAuthenticateParamsStr = headerParamsStr;
01608           }
01609         } else if (checkForHeader(lineStart, "Public:", 7, publicParamsStr)) {
01610         } else if (checkForHeader(lineStart, "Allow:", 6, publicParamsStr)) {
01611           // Note: we accept "Allow:" instead of "Public:", so that "OPTIONS" requests made to HTTP servers will work.
01612         } else if (checkForHeader(lineStart, "Location:", 9, headerParamsStr)) {
01613           setBaseURL(headerParamsStr);
01614         }
01615       }
01616       if (!reachedEndOfHeaders) break; // an error occurred
01617       
01618       if (foundRequest == NULL) {
01619         // Hack: The response didn't have a "CSeq:" header; assume it's for our most recent request:
01620         foundRequest = fRequestsAwaitingResponse.dequeue();
01621       }
01622       
01623       // If we saw a "Content-Length:" header, then make sure that we have the amount of data that it specified:
01624       unsigned bodyOffset = nextLineStart == NULL ? fResponseBytesAlreadySeen : nextLineStart - headerDataCopy;
01625       bodyStart = &fResponseBuffer[bodyOffset];
01626       numBodyBytes = fResponseBytesAlreadySeen - bodyOffset;
01627       if (contentLength > numBodyBytes) {
01628         // We need to read more data.  First, make sure we have enough space for it:
01629         unsigned numExtraBytesNeeded = contentLength - numBodyBytes;
01630         unsigned remainingBufferSize = responseBufferSize - fResponseBytesAlreadySeen;
01631         if (numExtraBytesNeeded > remainingBufferSize) {
01632           char tmpBuf[200];
01633           sprintf(tmpBuf, "Response buffer size (%d) is too small for \"Content-Length:\" %d (need a buffer size of >= %d bytes\n",
01634                   responseBufferSize, contentLength, fResponseBytesAlreadySeen + numExtraBytesNeeded);
01635           envir().setResultMsg(tmpBuf);
01636           break;
01637         }
01638         
01639         if (fVerbosityLevel >= 1) {
01640           envir() << "Have received " << fResponseBytesAlreadySeen << " total bytes of a "
01641                   << (foundRequest != NULL ? foundRequest->commandName() : "(unknown)")
01642                   << " RTSP response; awaiting " << numExtraBytesNeeded << " bytes more.\n";
01643         }
01644         delete[] headerDataCopy;
01645         if (foundRequest != NULL) fRequestsAwaitingResponse.putAtHead(foundRequest);// put our request record back; we need it again
01646         return; // We need to read more data
01647       }
01648       
01649       // We now have a complete response (including all bytes specified by the "Content-Length:" header, if any).
01650       char* responseEnd = bodyStart + contentLength;
01651       numExtraBytesAfterResponse = &fResponseBuffer[fResponseBytesAlreadySeen] - responseEnd;
01652 
01653       if (fVerbosityLevel >= 1) {
01654         char saved = *responseEnd;
01655         *responseEnd = '\0';
01656         envir() << "Received a complete "
01657                 << (foundRequest != NULL ? foundRequest->commandName() : "(unknown)")
01658                 << " response:\n" << fResponseBuffer << "\n";
01659         if (numExtraBytesAfterResponse > 0) envir() << "\t(plus " << numExtraBytesAfterResponse << " additional bytes)\n";
01660         *responseEnd = saved;
01661       }
01662       
01663       if (foundRequest != NULL) {
01664         Boolean needToResendCommand = False; // by default...
01665         if (responseCode == 200) {
01666           // Do special-case response handling for some commands:
01667           if (strcmp(foundRequest->commandName(), "SETUP") == 0) {
01668             if (!handleSETUPResponse(*foundRequest->subsession(), sessionParamsStr, transportParamsStr, foundRequest->booleanFlags()&0x1)) break;
01669           } else if (strcmp(foundRequest->commandName(), "PLAY") == 0) {
01670             if (!handlePLAYResponse(*foundRequest->session(), *foundRequest->subsession(), scaleParamsStr, rangeParamsStr, rtpInfoParamsStr)) break;
01671           } else if (strcmp(foundRequest->commandName(), "TEARDOWN") == 0) {
01672             if (!handleTEARDOWNResponse(*foundRequest->session(), *foundRequest->subsession())) break;
01673           } else if (strcmp(foundRequest->commandName(), "GET_PARAMETER") == 0) {
01674             if (!handleGET_PARAMETERResponse(foundRequest->contentStr(), bodyStart)) break;
01675           }
01676         } else if (responseCode == 401 && handleAuthenticationFailure(wwwAuthenticateParamsStr)) {
01677           // We need to resend the command, with an "Authorization:" header:
01678           needToResendCommand = True;
01679           
01680           if (strcmp(foundRequest->commandName(), "GET") == 0) {
01681             // Note: If a HTTP "GET" command (for RTSP-over-HTTP tunneling) returns "401 Unauthorized", then we resend it
01682             // (with an "Authorization:" header), just as we would for a RTSP command.  However, we do so using a new TCP connection,
01683             // because some servers close the original connection after returning the "401 Unauthorized".
01684             resetTCPSockets(); // forces the opening of a new connection for the resent command
01685           }
01686         } else if (responseCode == 301 || responseCode == 302) { // redirection
01687           resetTCPSockets(); // because we need to connect somewhere else next
01688           needToResendCommand = True;
01689         }
01690         
01691         if (needToResendCommand) {
01692           resetResponseBuffer();
01693           if (!resendCommand(foundRequest)) break;
01694           delete[] headerDataCopy;
01695           return; // without calling our response handler; the response to the resent command will do that
01696         }
01697       }
01698       
01699       responseSuccess = True;
01700     } while (0);
01701     
01702     // If we have a handler function for this response, call it.
01703     // But first, reset our response buffer, in case the handler goes to the event loop, and we end up getting called recursively:
01704     if (numExtraBytesAfterResponse > 0) {
01705       // An unusual case; usually due to having received pipelined responses.  Move the extra bytes to the front of the buffer:
01706       char* responseEnd = &fResponseBuffer[fResponseBytesAlreadySeen - numExtraBytesAfterResponse];
01707       
01708       // But first: A hack to save a copy of the response 'body', in case it's needed below for "resultString":
01709       numBodyBytes -= numExtraBytesAfterResponse;
01710       if (numBodyBytes > 0) {
01711         char saved = *responseEnd;
01712         *responseEnd = '\0';
01713         bodyStart = strDup(bodyStart);
01714         *responseEnd = saved;
01715       }
01716       
01717       memmove(fResponseBuffer, responseEnd, numExtraBytesAfterResponse);
01718       fResponseBytesAlreadySeen = numExtraBytesAfterResponse;
01719       fResponseBufferBytesLeft = responseBufferSize - numExtraBytesAfterResponse;
01720       fResponseBuffer[numExtraBytesAfterResponse] = '\0';
01721     } else {
01722       resetResponseBuffer();
01723     }
01724     if (foundRequest != NULL && foundRequest->handler() != NULL) {
01725       int resultCode;
01726       char* resultString;
01727       if (responseSuccess) {
01728         if (responseCode == 200) {
01729           resultCode = 0;
01730           resultString = numBodyBytes > 0 ? strDup(bodyStart) : strDup(publicParamsStr);
01731           // Note: The "strDup(bodyStart)" call assumes that the body is encoded without interior '\0' bytes
01732         } else {
01733           resultCode = responseCode;
01734           resultString = strDup(responseStr);
01735           envir().setResultMsg(responseStr);
01736         }
01737         (*foundRequest->handler())(this, resultCode, resultString);
01738       } else {
01739         // An error occurred parsing the response, so call the handler, indicating an error:
01740         handleRequestError(foundRequest);
01741       }
01742     }
01743     delete foundRequest;
01744     delete[] headerDataCopy;
01745     if (numExtraBytesAfterResponse > 0 && numBodyBytes > 0) delete[] bodyStart;
01746   } while (numExtraBytesAfterResponse > 0 && responseSuccess);
01747 }
01748 
01749 
01751 
01752 RTSPClient::RequestRecord::RequestRecord(unsigned cseq, char const* commandName, responseHandler* handler,
01753                                          MediaSession* session, MediaSubsession* subsession, u_int32_t booleanFlags,
01754                                          double start, double end, float scale, char const* contentStr)
01755   : fNext(NULL), fCSeq(cseq), fCommandName(commandName), fSession(session), fSubsession(subsession), fBooleanFlags(booleanFlags),
01756     fStart(start), fEnd(end), fAbsStartTime(NULL), fAbsEndTime(NULL), fScale(scale), fContentStr(strDup(contentStr)), fHandler(handler) {
01757 }
01758 
01759 RTSPClient::RequestRecord::RequestRecord(unsigned cseq, responseHandler* handler,
01760                                          char const* absStartTime, char const* absEndTime, float scale,
01761                                          MediaSession* session, MediaSubsession* subsession)
01762   : fNext(NULL), fCSeq(cseq), fCommandName("PLAY"), fSession(session), fSubsession(subsession), fBooleanFlags(0),
01763     fStart(0.0f), fEnd(-1.0f), fAbsStartTime(strDup(absStartTime)), fAbsEndTime(strDup(absEndTime)), fScale(scale),
01764     fContentStr(NULL), fHandler(handler) {
01765 }
01766 
01767 RTSPClient::RequestRecord::~RequestRecord() {
01768   // Delete the rest of the list first:
01769   delete fNext;
01770 
01771   delete[] fAbsStartTime; delete[] fAbsEndTime;
01772   delete[] fContentStr;
01773 }
01774 
01775 
01777 
01778 RTSPClient::RequestQueue::RequestQueue()
01779   : fHead(NULL), fTail(NULL) {
01780 }
01781 
01782 RTSPClient::RequestQueue::RequestQueue(RequestQueue& origQueue)
01783   : fHead(NULL), fTail(NULL) {
01784   RequestRecord* request;
01785   while ((request = origQueue.dequeue()) != NULL) {
01786     enqueue(request);
01787   }
01788 }
01789 
01790 RTSPClient::RequestQueue::~RequestQueue() {
01791   delete fHead;
01792 }
01793 
01794 void RTSPClient::RequestQueue::enqueue(RequestRecord* request) {
01795   if (fTail == NULL) {
01796     fHead = request;
01797   } else {
01798     fTail->next() = request;
01799   }
01800   fTail = request;
01801 }
01802 
01803 RTSPClient::RequestRecord* RTSPClient::RequestQueue::dequeue() {
01804   RequestRecord* request = fHead;
01805   if (fHead == fTail) {
01806     fHead = NULL;
01807     fTail = NULL;
01808   } else {
01809     fHead = fHead->next();
01810   }
01811   if (request != NULL) request->next() = NULL;
01812   return request;
01813 }
01814 
01815 void RTSPClient::RequestQueue::putAtHead(RequestRecord* request) {
01816   request->next() = fHead;
01817   fHead = request;
01818   if (fTail == NULL) {
01819     fTail = request;
01820   }
01821 }
01822 
01823 RTSPClient::RequestRecord* RTSPClient::RequestQueue::findByCSeq(unsigned cseq) {
01824   RequestRecord* request;
01825   for (request = fHead; request != NULL; request = request->next()) {
01826     if (request->cseq() == cseq) return request;
01827   }
01828   return NULL;
01829 }
01830 
01831 
01833 
01834 HandlerServerForREGISTERCommand* HandlerServerForREGISTERCommand
01835 ::createNew(UsageEnvironment& env, onRTSPClientCreationFunc* creationFunc, Port ourPort,
01836             UserAuthenticationDatabase* authDatabase, int verbosityLevel, char const* applicationName) {
01837   int ourSocket = setUpOurSocket(env, ourPort);
01838   if (ourSocket == -1) return NULL;
01839 
01840   return new HandlerServerForREGISTERCommand(env, creationFunc, ourSocket, ourPort, authDatabase, verbosityLevel, applicationName);
01841 }
01842 
01843 HandlerServerForREGISTERCommand
01844 ::HandlerServerForREGISTERCommand(UsageEnvironment& env, onRTSPClientCreationFunc* creationFunc, int ourSocket, Port ourPort,
01845                                   UserAuthenticationDatabase* authDatabase, int verbosityLevel, char const* applicationName)
01846   : RTSPServer(env, ourSocket, ourPort, authDatabase, 30/*small reclamationTestSeconds*/),
01847     fCreationFunc(creationFunc), fVerbosityLevel(verbosityLevel), fApplicationName(strDup(applicationName)) {
01848 }
01849 
01850 HandlerServerForREGISTERCommand::~HandlerServerForREGISTERCommand() {
01851   delete[] fApplicationName;
01852 }
01853 
01854 RTSPClient* HandlerServerForREGISTERCommand
01855 ::createNewRTSPClient(char const* rtspURL, int verbosityLevel, char const* applicationName, int socketNumToServer) {
01856   // Default implementation: create a basic "RTSPClient":
01857   return RTSPClient::createNew(envir(), rtspURL, verbosityLevel, applicationName, 0, socketNumToServer);
01858 }
01859 
01860 char const* HandlerServerForREGISTERCommand::allowedCommandNames() {
01861   return "OPTIONS, REGISTER";
01862 }
01863 
01864 Boolean HandlerServerForREGISTERCommand::weImplementREGISTER(char const* proxyURLSuffix, char*& responseStr) {
01865   responseStr = NULL;
01866   return True;
01867 }
01868 
01869 void HandlerServerForREGISTERCommand::implementCmd_REGISTER(char const* url, char const* urlSuffix, int socketToRemoteServer,
01870                                                             Boolean deliverViaTCP, char const* /*proxyURLSuffix*/) {
01871   // Create a new "RTSPClient" object, and call our 'creation function' with it:
01872   RTSPClient* newRTSPClient = createNewRTSPClient(url, fVerbosityLevel, fApplicationName, socketToRemoteServer);
01873 
01874   if (fCreationFunc != NULL) (*fCreationFunc)(newRTSPClient, deliverViaTCP);
01875 }

Generated on Tue Mar 25 14:35:36 2014 for live by  doxygen 1.5.2