liveMedia/RTSPServer.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-2013 Live Networks, Inc.  All rights reserved.
00018 // A RTSP server
00019 // Implementation
00020 
00021 #include "RTSPServer.hh"
00022 #include "RTSPCommon.hh"
00023 #include "Base64.hh"
00024 #include <GroupsockHelper.hh>
00025 
00027 
00028 RTSPServer*
00029 RTSPServer::createNew(UsageEnvironment& env, Port ourPort,
00030                       UserAuthenticationDatabase* authDatabase,
00031                       unsigned reclamationTestSeconds) {
00032   int ourSocket = setUpOurSocket(env, ourPort);
00033   if (ourSocket == -1) return NULL;
00034 
00035   return new RTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
00036 }
00037 
00038 Boolean RTSPServer::lookupByName(UsageEnvironment& env,
00039                                  char const* name,
00040                                  RTSPServer*& resultServer) {
00041   resultServer = NULL; // unless we succeed
00042 
00043   Medium* medium;
00044   if (!Medium::lookupByName(env, name, medium)) return False;
00045 
00046   if (!medium->isRTSPServer()) {
00047     env.setResultMsg(name, " is not a RTSP server");
00048     return False;
00049   }
00050 
00051   resultServer = (RTSPServer*)medium;
00052   return True;
00053 }
00054 
00055 void RTSPServer::addServerMediaSession(ServerMediaSession* serverMediaSession) {
00056   if (serverMediaSession == NULL) return;
00057 
00058   char const* sessionName = serverMediaSession->streamName();
00059   if (sessionName == NULL) sessionName = "";
00060   removeServerMediaSession(sessionName); // in case an existing "ServerMediaSession" with this name already exists
00061 
00062   fServerMediaSessions->Add(sessionName, (void*)serverMediaSession);
00063 }
00064 
00065 ServerMediaSession* RTSPServer::lookupServerMediaSession(char const* streamName) {
00066   return (ServerMediaSession*)(fServerMediaSessions->Lookup(streamName));
00067 }
00068 
00069 void RTSPServer::removeServerMediaSession(ServerMediaSession* serverMediaSession) {
00070   if (serverMediaSession == NULL) return;
00071 
00072   fServerMediaSessions->Remove(serverMediaSession->streamName());
00073   if (serverMediaSession->referenceCount() == 0) {
00074     Medium::close(serverMediaSession);
00075   } else {
00076     serverMediaSession->deleteWhenUnreferenced() = True;
00077   }
00078 }
00079 
00080 void RTSPServer::removeServerMediaSession(char const* streamName) {
00081   removeServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
00082 }
00083 
00084 void RTSPServer::closeAllClientSessionsForServerMediaSession(ServerMediaSession* serverMediaSession) {
00085   if (serverMediaSession == NULL) return;
00086 
00087   HashTable::Iterator* iter = HashTable::Iterator::create(*fClientSessions);
00088   RTSPServer::RTSPClientSession* clientSession;
00089   char const* key; // dummy
00090   while ((clientSession = (RTSPServer::RTSPClientSession*)(iter->next(key))) != NULL) {
00091     if (clientSession->fOurServerMediaSession == serverMediaSession) {
00092       delete clientSession;
00093     }
00094   }
00095   delete iter;
00096 }
00097 
00098 void RTSPServer::closeAllClientSessionsForServerMediaSession(char const* streamName) {
00099   closeAllClientSessionsForServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
00100 }
00101 
00102 void RTSPServer::deleteServerMediaSession(ServerMediaSession* serverMediaSession) {
00103   if (serverMediaSession == NULL) return;
00104 
00105   closeAllClientSessionsForServerMediaSession(serverMediaSession);
00106   removeServerMediaSession(serverMediaSession);
00107 }
00108 
00109 void RTSPServer::deleteServerMediaSession(char const* streamName) {
00110   deleteServerMediaSession((ServerMediaSession*)(fServerMediaSessions->Lookup(streamName)));
00111 }
00112 
00113 char* RTSPServer
00114 ::rtspURL(ServerMediaSession const* serverMediaSession, int clientSocket) const {
00115   char* urlPrefix = rtspURLPrefix(clientSocket);
00116   char const* sessionName = serverMediaSession->streamName();
00117 
00118   char* resultURL = new char[strlen(urlPrefix) + strlen(sessionName) + 1];
00119   sprintf(resultURL, "%s%s", urlPrefix, sessionName);
00120 
00121   delete[] urlPrefix;
00122   return resultURL;
00123 }
00124 
00125 char* RTSPServer::rtspURLPrefix(int clientSocket) const {
00126   struct sockaddr_in ourAddress;
00127   if (clientSocket < 0) {
00128     // Use our default IP address in the URL:
00129     ourAddress.sin_addr.s_addr = ReceivingInterfaceAddr != 0
00130       ? ReceivingInterfaceAddr
00131       : ourIPAddress(envir()); // hack
00132   } else {
00133     SOCKLEN_T namelen = sizeof ourAddress;
00134     getsockname(clientSocket, (struct sockaddr*)&ourAddress, &namelen);
00135   }
00136 
00137   char urlBuffer[100]; // more than big enough for "rtsp://<ip-address>:<port>/"
00138 
00139   portNumBits portNumHostOrder = ntohs(fRTSPServerPort.num());
00140   if (portNumHostOrder == 554 /* the default port number */) {
00141     sprintf(urlBuffer, "rtsp://%s/", AddressString(ourAddress).val());
00142   } else {
00143     sprintf(urlBuffer, "rtsp://%s:%hu/",
00144             AddressString(ourAddress).val(), portNumHostOrder);
00145   }
00146 
00147   return strDup(urlBuffer);
00148 }
00149 
00150 UserAuthenticationDatabase* RTSPServer::setAuthenticationDatabase(UserAuthenticationDatabase* newDB) {
00151   UserAuthenticationDatabase* oldDB = fAuthDB;
00152   fAuthDB = newDB;
00153 
00154   return oldDB;
00155 }
00156 
00157 Boolean RTSPServer::setUpTunnelingOverHTTP(Port httpPort) {
00158   fHTTPServerSocket = setUpOurSocket(envir(), httpPort);
00159   if (fHTTPServerSocket >= 0) {
00160     fHTTPServerPort = httpPort;
00161     envir().taskScheduler().turnOnBackgroundReadHandling(fHTTPServerSocket,
00162                                                          (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerHTTP, this);
00163     return True;
00164   }
00165 
00166   return False;
00167 }
00168 
00169 portNumBits RTSPServer::httpServerPortNum() const {
00170   return ntohs(fHTTPServerPort.num());
00171 }
00172 
00173 #define LISTEN_BACKLOG_SIZE 20
00174 
00175 int RTSPServer::setUpOurSocket(UsageEnvironment& env, Port& ourPort) {
00176   int ourSocket = -1;
00177 
00178   do {
00179     // The following statement is enabled by default.
00180     // Don't disable it (by defining ALLOW_RTSP_SERVER_PORT_REUSE) unless you know what you're doing.
00181 #ifndef ALLOW_RTSP_SERVER_PORT_REUSE
00182     NoReuse dummy(env); // Don't use this socket if there's already a local server using it
00183 #endif
00184 
00185     ourSocket = setupStreamSocket(env, ourPort);
00186     if (ourSocket < 0) break;
00187 
00188     // Make sure we have a big send buffer:
00189     if (!increaseSendBufferTo(env, ourSocket, 50*1024)) break;
00190 
00191     // Allow multiple simultaneous connections:
00192     if (listen(ourSocket, LISTEN_BACKLOG_SIZE) < 0) {
00193       env.setResultErrMsg("listen() failed: ");
00194       break;
00195     }
00196 
00197     if (ourPort.num() == 0) {
00198       // bind() will have chosen a port for us; return it also:
00199       if (!getSourcePort(env, ourSocket, ourPort)) break;
00200     }
00201 
00202     return ourSocket;
00203   } while (0);
00204 
00205   if (ourSocket != -1) ::closeSocket(ourSocket);
00206   return -1;
00207 }
00208 
00209 Boolean RTSPServer
00210 ::specialClientAccessCheck(int /*clientSocket*/, struct sockaddr_in& /*clientAddr*/, char const* /*urlSuffix*/) {
00211   // default implementation
00212   return True;
00213 }
00214 
00215 Boolean RTSPServer
00216 ::specialClientUserAccessCheck(int /*clientSocket*/, struct sockaddr_in& /*clientAddr*/,
00217                                char const* /*urlSuffix*/, char const * /*username*/) {
00218   // default implementation; no further access restrictions:
00219   return True;
00220 }
00221 
00222 
00223 RTSPServer::RTSPServer(UsageEnvironment& env,
00224                        int ourSocket, Port ourPort,
00225                        UserAuthenticationDatabase* authDatabase,
00226                        unsigned reclamationTestSeconds)
00227   : Medium(env),
00228     fRTSPServerSocket(ourSocket), fRTSPServerPort(ourPort), fHTTPServerSocket(-1), fHTTPServerPort(0),
00229     fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
00230     fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
00231     fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
00232     fClientSessions(HashTable::create(STRING_HASH_KEYS)),
00233     fAuthDB(authDatabase), fReclamationTestSeconds(reclamationTestSeconds) {
00234   ignoreSigPipeOnSocket(ourSocket); // so that clients on the same host that are killed don't also kill us
00235 
00236   // Arrange to handle connections from others:
00237   env.taskScheduler().turnOnBackgroundReadHandling(fRTSPServerSocket,
00238                                                    (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP, this);
00239 }
00240 
00241 RTSPServer::~RTSPServer() {
00242   // Turn off background read handling:
00243   envir().taskScheduler().turnOffBackgroundReadHandling(fRTSPServerSocket);
00244   ::closeSocket(fRTSPServerSocket);
00245 
00246   envir().taskScheduler().turnOffBackgroundReadHandling(fHTTPServerSocket);
00247   ::closeSocket(fHTTPServerSocket);
00248 
00249   // Close all client connection objects:
00250   RTSPServer::RTSPClientConnection* connection;
00251   while ((connection = (RTSPServer::RTSPClientConnection*)fClientConnections->getFirst()) != NULL) {
00252     delete connection;
00253   }
00254   delete fClientConnections;
00255   delete fClientConnectionsForHTTPTunneling; // all content was already removed as a result of the loop above
00256 
00257   // Close all client session objects:
00258   RTSPServer::RTSPClientSession* clientSession;
00259   while ((clientSession = (RTSPServer::RTSPClientSession*)fClientSessions->getFirst()) != NULL) {
00260     delete clientSession;
00261   }
00262   delete fClientSessions;
00263 
00264   // Delete all server media sessions
00265   ServerMediaSession* serverMediaSession;
00266   while ((serverMediaSession = (ServerMediaSession*)fServerMediaSessions->getFirst()) != NULL) {
00267     removeServerMediaSession(serverMediaSession); // will delete it, because it no longer has any 'client session' objects using it
00268   }
00269   delete fServerMediaSessions;
00270 }
00271 
00272 Boolean RTSPServer::isRTSPServer() const {
00273   return True;
00274 }
00275 
00276 void RTSPServer::incomingConnectionHandlerRTSP(void* instance, int /*mask*/) {
00277   RTSPServer* server = (RTSPServer*)instance;
00278   server->incomingConnectionHandlerRTSP1();
00279 }
00280 void RTSPServer::incomingConnectionHandlerRTSP1() {
00281   incomingConnectionHandler(fRTSPServerSocket);
00282 }
00283 
00284 void RTSPServer::incomingConnectionHandlerHTTP(void* instance, int /*mask*/) {
00285   RTSPServer* server = (RTSPServer*)instance;
00286   server->incomingConnectionHandlerHTTP1();
00287 }
00288 void RTSPServer::incomingConnectionHandlerHTTP1() {
00289   incomingConnectionHandler(fHTTPServerSocket);
00290 }
00291 
00292 void RTSPServer::incomingConnectionHandler(int serverSocket) {
00293   struct sockaddr_in clientAddr;
00294   SOCKLEN_T clientAddrLen = sizeof clientAddr;
00295   int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
00296   if (clientSocket < 0) {
00297     int err = envir().getErrno();
00298     if (err != EWOULDBLOCK) {
00299         envir().setResultErrMsg("accept() failed: ");
00300     }
00301     return;
00302   }
00303   makeSocketNonBlocking(clientSocket);
00304   increaseSendBufferTo(envir(), clientSocket, 50*1024);
00305 
00306 #ifdef DEBUG
00307   envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
00308 #endif
00309 
00310   // Create a new object for handling this RTSP connection:
00311   (void)createNewClientConnection(clientSocket, clientAddr);
00312 }
00313 
00314 
00316 
00317 RTSPServer::RTSPClientConnection
00318 ::RTSPClientConnection(RTSPServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
00319   : fOurServer(ourServer), fIsActive(True),
00320     fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
00321     fRecursionCount(0), fOurSessionCookie(NULL) {
00322   // Add ourself to our 'client connections' table:
00323   fOurServer.fClientConnections->Add((char const*)this, this);
00324 
00325   // Arrange to handle incoming requests:
00326   resetRequestBuffer();
00327   envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
00328                                                 (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
00329 }
00330 
00331 RTSPServer::RTSPClientConnection::~RTSPClientConnection() {
00332   // Remove ourself from the server's 'client connections' hash table before we go:
00333   fOurServer.fClientConnections->Remove((char const*)this);
00334 
00335   if (fOurSessionCookie != NULL) {
00336     // We were being used for RTSP-over-HTTP tunneling. Also remove ourselves from the 'session cookie' hash table before we go:
00337     fOurServer.fClientConnectionsForHTTPTunneling->Remove(fOurSessionCookie);
00338     delete[] fOurSessionCookie;
00339   }
00340 
00341   closeSockets();
00342 }
00343 
00344 // Handler routines for specific RTSP commands:
00345 
00346 static char const* allowedCommandNames
00347 = "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER";
00348 
00349 void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS() {
00350   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00351            "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
00352            fCurrentCSeq, dateHeader(), allowedCommandNames);
00353 }
00354 
00355 void RTSPServer::RTSPClientConnection
00356 ::handleCmd_GET_PARAMETER(char const* /*fullRequestStr*/) {
00357   // By default, we implement "GET_PARAMETER" (on the entire server) just as a 'no op', and send back a dummy response.
00358   // (If you want to handle this type of "GET_PARAMETER" differently, you can do so by defining a subclass of "RTSPServer"
00359   // and "RTSPServer::RTSPClientConnection", and then reimplement this virtual function in your subclass.)
00360   setRTSPResponse("200 OK", LIVEMEDIA_LIBRARY_VERSION_STRING);
00361 }
00362 
00363 void RTSPServer::RTSPClientConnection
00364 ::handleCmd_SET_PARAMETER(char const* /*fullRequestStr*/) {
00365   // By default, we implement "SET_PARAMETER" (on the entire server) just as a 'no op', and send back an empty response.
00366   // (If you want to handle this type of "SET_PARAMETER" differently, you can do so by defining a subclass of "RTSPServer"
00367   // and "RTSPServer::RTSPClientConnection", and then reimplement this virtual function in your subclass.)
00368   setRTSPResponse("200 OK");
00369 }
00370 
00371 void RTSPServer::RTSPClientConnection
00372 ::handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
00373   char* sdpDescription = NULL;
00374   char* rtspURL = NULL;
00375   do {
00376     char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
00377     if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2 > sizeof urlTotalSuffix) {
00378       handleCmd_bad();
00379       break;
00380     }
00381     urlTotalSuffix[0] = '\0';
00382     if (urlPreSuffix[0] != '\0') {
00383       strcat(urlTotalSuffix, urlPreSuffix);
00384       strcat(urlTotalSuffix, "/");
00385     }
00386     strcat(urlTotalSuffix, urlSuffix);
00387       
00388     if (!authenticationOK("DESCRIBE", urlTotalSuffix, fullRequestStr)) break;
00389     
00390     // We should really check that the request contains an "Accept:" #####
00391     // for "application/sdp", because that's what we're sending back #####
00392     
00393     // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
00394     ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
00395     if (session == NULL) {
00396       handleCmd_notFound();
00397       break;
00398     }
00399     
00400     // Then, assemble a SDP description for this session:
00401     sdpDescription = session->generateSDPDescription();
00402     if (sdpDescription == NULL) {
00403       // This usually means that a file name that was specified for a
00404       // "ServerMediaSubsession" does not exist.
00405       setRTSPResponse("404 File Not Found, Or In Incorrect Format");
00406       break;
00407     }
00408     unsigned sdpDescriptionSize = strlen(sdpDescription);
00409     
00410     // Also, generate our RTSP URL, for the "Content-Base:" header
00411     // (which is necessary to ensure that the correct URL gets used in subsequent "SETUP" requests).
00412     rtspURL = fOurServer.rtspURL(session, fClientInputSocket);
00413     
00414     snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00415              "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
00416              "%s"
00417              "Content-Base: %s/\r\n"
00418              "Content-Type: application/sdp\r\n"
00419              "Content-Length: %d\r\n\r\n"
00420              "%s",
00421              fCurrentCSeq,
00422              dateHeader(),
00423              rtspURL,
00424              sdpDescriptionSize,
00425              sdpDescription);
00426   } while (0);
00427 
00428   delete[] sdpDescription;
00429   delete[] rtspURL;
00430 }
00431 
00432 static void lookForHeader(char const* headerName, char const* source, unsigned sourceLen, char* resultStr, unsigned resultMaxSize) {
00433   resultStr[0] = '\0';  // by default, return an empty string
00434   unsigned headerNameLen = strlen(headerName);
00435   for (int i = 0; i < (int)(sourceLen-headerNameLen); ++i) {
00436     if (strncmp(&source[i], headerName, headerNameLen) == 0 && source[i+headerNameLen] == ':') {
00437       // We found the header.  Skip over any whitespace, then copy the rest of the line to "resultStr":
00438       for (i += headerNameLen+1; i < (int)sourceLen && (source[i] == ' ' || source[i] == '\t'); ++i) {}
00439       for (unsigned j = i; j < sourceLen; ++j) {
00440         if (source[j] == '\r' || source[j] == '\n') {
00441           // We've found the end of the line.  Copy it to the result (if it will fit):
00442           if (j-i+1 > resultMaxSize) break;
00443           char const* resultSource = &source[i];
00444           char const* resultSourceEnd = &source[j];
00445           while (resultSource < resultSourceEnd) *resultStr++ = *resultSource++;
00446           *resultStr = '\0';
00447           break;
00448         }
00449       }
00450     }
00451   }
00452 }
00453 
00454 void RTSPServer::RTSPClientConnection::handleCmd_bad() {
00455   // Don't do anything with "fCurrentCSeq", because it might be nonsense
00456   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00457            "RTSP/1.0 400 Bad Request\r\n%sAllow: %s\r\n\r\n",
00458            dateHeader(), allowedCommandNames);
00459 }
00460 
00461 void RTSPServer::RTSPClientConnection::handleCmd_notSupported() {
00462   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00463            "RTSP/1.0 405 Method Not Allowed\r\nCSeq: %s\r\n%sAllow: %s\r\n\r\n",
00464            fCurrentCSeq, dateHeader(), allowedCommandNames);
00465 }
00466 
00467 void RTSPServer::RTSPClientConnection::handleCmd_notFound() {
00468   setRTSPResponse("404 Stream Not Found");
00469 }
00470 
00471 void RTSPServer::RTSPClientConnection::handleCmd_sessionNotFound() {
00472   setRTSPResponse("454 Session Not Found");
00473 }
00474 
00475 void RTSPServer::RTSPClientConnection::handleCmd_unsupportedTransport() {
00476   setRTSPResponse("461 Unsupported Transport");
00477 }
00478 
00479 Boolean RTSPServer::RTSPClientConnection::parseHTTPRequestString(char* resultCmdName, unsigned resultCmdNameMaxSize,
00480                                                                  char* urlSuffix, unsigned urlSuffixMaxSize,
00481                                                                  char* sessionCookie, unsigned sessionCookieMaxSize,
00482                                                                  char* acceptStr, unsigned acceptStrMaxSize) {
00483   // Check for the limited HTTP requests that we expect for specifying RTSP-over-HTTP tunneling.
00484   // This parser is currently rather dumb; it should be made smarter #####
00485   char const* reqStr = (char const*)fRequestBuffer;
00486   unsigned const reqStrSize = fRequestBytesAlreadySeen;
00487 
00488   // Read everything up to the first space as the command name:
00489   Boolean parseSucceeded = False;
00490   unsigned i;
00491   for (i = 0; i < resultCmdNameMaxSize-1 && i < reqStrSize; ++i) {
00492     char c = reqStr[i];
00493     if (c == ' ' || c == '\t') {
00494       parseSucceeded = True;
00495       break;
00496     }
00497 
00498     resultCmdName[i] = c;
00499   }
00500   resultCmdName[i] = '\0';
00501   if (!parseSucceeded) return False;
00502 
00503   // Look for the string "HTTP/", before the first \r or \n:
00504   parseSucceeded = False;
00505   for (; i < reqStrSize-5 && reqStr[i] != '\r' && reqStr[i] != '\n'; ++i) {
00506     if (reqStr[i] == 'H' && reqStr[i+1] == 'T' && reqStr[i+2]== 'T' && reqStr[i+3]== 'P' && reqStr[i+4]== '/') {
00507       i += 5; // to advance past the "HTTP/"
00508       parseSucceeded = True;
00509       break;
00510     }
00511   }
00512   if (!parseSucceeded) return False;
00513 
00514   // Get the 'URL suffix' that occurred before this:
00515   unsigned k = i-6;
00516   while (k > 0 && reqStr[k] == ' ') --k; // back up over white space
00517   unsigned j = k;
00518   while (j > 0 && reqStr[j] != ' ' && reqStr[j] != '/') --j;
00519   // The URL suffix is in position (j,k]:
00520   if (k - j + 1 > urlSuffixMaxSize) return False; // there's no room> 
00521   unsigned n = 0;
00522   while (++j <= k) urlSuffix[n++] = reqStr[j];
00523   urlSuffix[n] = '\0';
00524 
00525   // Look for various headers that we're interested in:
00526   lookForHeader("x-sessioncookie", &reqStr[i], reqStrSize-i, sessionCookie, sessionCookieMaxSize);
00527   lookForHeader("Accept", &reqStr[i], reqStrSize-i, acceptStr, acceptStrMaxSize);
00528 
00529   return True;
00530 }
00531 
00532 void RTSPServer::RTSPClientConnection::handleHTTPCmd_notSupported() {
00533   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00534            "HTTP/1.0 405 Method Not Allowed\r\n%s\r\n\r\n",
00535            dateHeader());
00536 }
00537 
00538 void RTSPServer::RTSPClientConnection::handleHTTPCmd_notFound() {
00539   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00540            "HTTP/1.0 404 Not Found\r\n%s\r\n\r\n",
00541            dateHeader());
00542 }
00543 
00544 void RTSPServer::RTSPClientConnection::handleHTTPCmd_TunnelingGET(char const* sessionCookie) {
00545   // Record ourself as having this 'session cookie', so that a subsequent HTTP "POST" command (with the same 'session cookie')
00546   // can find us:
00547   if (fOurServer.fClientConnectionsForHTTPTunneling == NULL) {
00548     fOurServer.fClientConnectionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
00549   }
00550   delete[] fOurSessionCookie; fOurSessionCookie = strDup(sessionCookie);
00551   fOurServer.fClientConnectionsForHTTPTunneling->Add(sessionCookie, (void*)this);
00552 #ifdef DEBUG
00553   fprintf(stderr, "Handled HTTP \"GET\" request (client output socket: %d)\n", fClientOutputSocket);
00554 #endif
00555 
00556   // Construct our response:
00557   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
00558            "HTTP/1.0 200 OK\r\n"
00559            "Date: Thu, 19 Aug 1982 18:30:00 GMT\r\n"
00560            "Cache-Control: no-cache\r\n"
00561            "Pragma: no-cache\r\n"
00562            "Content-Type: application/x-rtsp-tunnelled\r\n"
00563            "\r\n");
00564 }
00565 
00566 Boolean RTSPServer::RTSPClientConnection
00567 ::handleHTTPCmd_TunnelingPOST(char const* sessionCookie, unsigned char const* extraData, unsigned extraDataSize) {
00568   // Use the "sessionCookie" string to look up the separate "RTSPClientConnection" object that should have been used to handle
00569   // an earlier HTTP "GET" request:
00570   if (fOurServer.fClientConnectionsForHTTPTunneling == NULL) {
00571     fOurServer.fClientConnectionsForHTTPTunneling = HashTable::create(STRING_HASH_KEYS);
00572   }
00573   RTSPServer::RTSPClientConnection* prevClientConnection
00574     = (RTSPServer::RTSPClientConnection*)(fOurServer.fClientConnectionsForHTTPTunneling->Lookup(sessionCookie));
00575   if (prevClientConnection == NULL) {
00576     // There was no previous HTTP "GET" request; treat this "POST" request as bad:
00577     handleHTTPCmd_notSupported();
00578     fIsActive = False; // triggers deletion of ourself
00579     return False;
00580   }
00581 #ifdef DEBUG
00582   fprintf(stderr, "Handled HTTP \"POST\" request (client input socket: %d)\n", fClientInputSocket);
00583 #endif
00584 
00585   // Change the previous "RTSPClientSession" object's input socket to ours.  It will be used for subsequent requests:
00586   prevClientConnection->changeClientInputSocket(fClientInputSocket, extraData, extraDataSize);
00587   fClientInputSocket = fClientOutputSocket = -1; // so the socket doesn't get closed when we get deleted
00588   return True;
00589 }
00590 
00591 void RTSPServer::RTSPClientConnection::handleHTTPCmd_StreamingGET(char const* /*urlSuffix*/, char const* /*fullRequestStr*/) {
00592   // By default, we don't support requests to access streams via HTTP:
00593   handleHTTPCmd_notSupported();
00594 }
00595 
00596 void RTSPServer::RTSPClientConnection::resetRequestBuffer() {
00597   fRequestBytesAlreadySeen = 0;
00598   fRequestBufferBytesLeft = sizeof fRequestBuffer;
00599   fLastCRLF = &fRequestBuffer[-3]; // hack: Ensures that we don't think we have end-of-msg if the data starts with <CR><LF>
00600   fBase64RemainderCount = 0;
00601 }
00602 
00603 void RTSPServer::RTSPClientConnection::closeSockets() {
00604   // Turn off background handling on our input socket (and output socket, if different); then close it (or them):
00605   if (fClientOutputSocket != fClientInputSocket) {
00606     envir().taskScheduler().disableBackgroundHandling(fClientOutputSocket);
00607     ::closeSocket(fClientOutputSocket);
00608   }
00609 
00610   envir().taskScheduler().disableBackgroundHandling(fClientInputSocket);
00611   ::closeSocket(fClientInputSocket);
00612 
00613   fClientInputSocket = fClientOutputSocket = -1;
00614 }
00615 
00616 void RTSPServer::RTSPClientConnection::incomingRequestHandler(void* instance, int /*mask*/) {
00617   RTSPClientConnection* session = (RTSPClientConnection*)instance;
00618   session->incomingRequestHandler1();
00619 }
00620 
00621 void RTSPServer::RTSPClientConnection::incomingRequestHandler1() {
00622   struct sockaddr_in dummy; // 'from' address, meaningless in this case
00623 
00624   int bytesRead = readSocket(envir(), fClientInputSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
00625   handleRequestBytes(bytesRead);
00626 }
00627 
00628 void RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(void* instance, u_int8_t requestByte) {
00629   RTSPClientConnection* session = (RTSPClientConnection*)instance;
00630   session->handleAlternativeRequestByte1(requestByte);
00631 }
00632 
00633 void RTSPServer::RTSPClientConnection::handleAlternativeRequestByte1(u_int8_t requestByte) {
00634   if (requestByte == 0xFF) {
00635     // Hack: The new handler of the input TCP socket encountered an error reading it.  Indicate this:
00636     handleRequestBytes(-1);
00637   } else if (requestByte == 0xFE) {
00638     // Another hack: The new handler of the input TCP socket no longer needs it, so take back control of it:
00639     envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
00640                                                   (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
00641   } else {
00642     // Normal case: Add this character to our buffer; then try to handle the data that we have buffered so far:
00643     if (fRequestBufferBytesLeft == 0 || fRequestBytesAlreadySeen >= RTSP_BUFFER_SIZE) return;
00644     fRequestBuffer[fRequestBytesAlreadySeen] = requestByte;
00645     handleRequestBytes(1);
00646   }
00647 }
00648 
00649 void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
00650   int numBytesRemaining = 0;
00651   ++fRecursionCount;
00652 
00653   do {
00654     RTSPServer::RTSPClientSession* clientSession = NULL;
00655 
00656     if (newBytesRead < 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {
00657       // Either the client socket has died, or the request was too big for us.
00658       // Terminate this connection:
00659 #ifdef DEBUG
00660       fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
00661 #endif
00662       fIsActive = False;
00663       break;
00664     }
00665     
00666     Boolean endOfMsg = False;
00667     unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
00668 #ifdef DEBUG
00669     ptr[newBytesRead] = '\0';
00670     fprintf(stderr, "RTSPClientConnection[%p]::handleRequestBytes() %s %d new bytes:%s\n",
00671             this, numBytesRemaining > 0 ? "processing" : "read", newBytesRead, ptr);
00672 #endif
00673     
00674     if (fClientOutputSocket != fClientInputSocket) {
00675       // We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
00676       // We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes):
00677       unsigned numBytesToDecode = fBase64RemainderCount + newBytesRead;
00678       unsigned newBase64RemainderCount = numBytesToDecode%4;
00679       numBytesToDecode -= newBase64RemainderCount;
00680       if (numBytesToDecode > 0) {
00681         ptr[newBytesRead] = '\0';
00682         unsigned decodedSize;
00683         unsigned char* decodedBytes = base64Decode((char const*)(ptr-fBase64RemainderCount), decodedSize);
00684 #ifdef DEBUG
00685         fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
00686         for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
00687         fprintf(stderr, "\n");
00688 #endif
00689         
00690         // Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
00691         unsigned char* to = ptr-fBase64RemainderCount;
00692         for (unsigned i = 0; i < decodedSize; ++i) *to++ = decodedBytes[i];
00693         
00694         // Then copy any remaining (undecoded) bytes to the end:
00695         for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
00696         
00697         newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
00698         delete[] decodedBytes;
00699       }
00700       fBase64RemainderCount = newBase64RemainderCount;
00701       if (fBase64RemainderCount > 0) break; // because we know that we have more input bytes still to receive
00702     }
00703     
00704     // Look for the end of the message: <CR><LF><CR><LF>
00705     unsigned char *tmpPtr = fLastCRLF + 2;
00706     if (tmpPtr < fRequestBuffer) tmpPtr = fRequestBuffer;
00707     while (tmpPtr < &ptr[newBytesRead-1]) {
00708       if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
00709         if (tmpPtr - fLastCRLF == 2) { // This is it:
00710           endOfMsg = True;
00711           break;
00712         }
00713         fLastCRLF = tmpPtr;
00714       }
00715       ++tmpPtr;
00716     }
00717     
00718     fRequestBufferBytesLeft -= newBytesRead;
00719     fRequestBytesAlreadySeen += newBytesRead;
00720     
00721     if (!endOfMsg) break; // subsequent reads will be needed to complete the request
00722     
00723     // Parse the request string into command name and 'CSeq', then handle the command:
00724     fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
00725     char cmdName[RTSP_PARAM_STRING_MAX];
00726     char urlPreSuffix[RTSP_PARAM_STRING_MAX];
00727     char urlSuffix[RTSP_PARAM_STRING_MAX];
00728     char cseq[RTSP_PARAM_STRING_MAX];
00729     char sessionIdStr[RTSP_PARAM_STRING_MAX];
00730     unsigned contentLength = 0;
00731     fLastCRLF[2] = '\0'; // temporarily, for parsing
00732     Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer,
00733                                                     cmdName, sizeof cmdName,
00734                                                     urlPreSuffix, sizeof urlPreSuffix,
00735                                                     urlSuffix, sizeof urlSuffix,
00736                                                     cseq, sizeof cseq,
00737                                                     sessionIdStr, sizeof sessionIdStr,
00738                                                     contentLength);
00739     fLastCRLF[2] = '\r'; // restore its value
00740     if (parseSucceeded) {
00741 #ifdef DEBUG
00742       fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u, with %d bytes following the message.\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength, ptr + newBytesRead - (tmpPtr + 2));
00743 #endif
00744       // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
00745       if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us 
00746       
00747       // We now have a complete RTSP request.
00748       // Handle the specified command (beginning by checking those that don't require session ids):
00749       fCurrentCSeq = cseq;
00750       if (strcmp(cmdName, "OPTIONS") == 0) {
00751         handleCmd_OPTIONS();
00752       } else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0') {
00753         // The special "*" URL means: an operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:
00754         if (strcmp(cmdName, "GET_PARAMETER") == 0) {
00755           handleCmd_GET_PARAMETER((char const*)fRequestBuffer);
00756         } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
00757           handleCmd_SET_PARAMETER((char const*)fRequestBuffer);
00758         } else {
00759           handleCmd_notSupported();
00760         }
00761       } else if (strcmp(cmdName, "DESCRIBE") == 0) {
00762         handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00763       } else if (strcmp(cmdName, "SETUP") == 0) {
00764         if (sessionIdStr[0] == '\0') {
00765           // No session id was present in the request.  So create a new "RTSPClientSession" object for this request.
00766           // Choose a random (unused) 32-bit integer for the session id (it will be encoded as a 8-digit hex number).
00767           // (We avoid choosing session id 0, because that has a special use (by "OnDemandServerMediaSubsession").)
00768           u_int32_t sessionId;
00769           do {
00770             sessionId = (u_int32_t)our_random32();
00771             sprintf(sessionIdStr, "%08X", sessionId);
00772           } while (sessionId == 0 || fOurServer.fClientSessions->Lookup(sessionIdStr) != NULL);
00773           clientSession = fOurServer.createNewClientSession(sessionId);
00774           fOurServer.fClientSessions->Add(sessionIdStr, clientSession);
00775         } else {
00776           // The request included a session id.  Make sure it's one that we have already set up:
00777           clientSession = (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
00778 
00779           if (clientSession == NULL) {
00780             handleCmd_sessionNotFound();
00781           }
00782         }
00783         if (clientSession != NULL) clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00784       } else if (strcmp(cmdName, "TEARDOWN") == 0
00785                  || strcmp(cmdName, "PLAY") == 0
00786                  || strcmp(cmdName, "PAUSE") == 0
00787                  || strcmp(cmdName, "GET_PARAMETER") == 0
00788                  || strcmp(cmdName, "SET_PARAMETER") == 0) {
00789         RTSPServer::RTSPClientSession* clientSession
00790           = sessionIdStr[0] == '\0' ? NULL : (RTSPServer::RTSPClientSession*)(fOurServer.fClientSessions->Lookup(sessionIdStr));
00791         if (clientSession == NULL) {
00792           handleCmd_sessionNotFound();
00793         } else {
00794           clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00795         }
00796       } else {
00797         // The command is one that we don't handle:
00798         handleCmd_notSupported();
00799       }
00800     } else {
00801 #ifdef DEBUG
00802       fprintf(stderr, "parseRTSPRequestString() failed; checking now for HTTP commands (for RTSP-over-HTTP tunneling)...\n");
00803 #endif
00804       // The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
00805       char sessionCookie[RTSP_PARAM_STRING_MAX];
00806       char acceptStr[RTSP_PARAM_STRING_MAX];
00807       *fLastCRLF = '\0'; // temporarily, for parsing
00808       parseSucceeded = parseHTTPRequestString(cmdName, sizeof cmdName,
00809                                               urlSuffix, sizeof urlPreSuffix,
00810                                               sessionCookie, sizeof sessionCookie,
00811                                               acceptStr, sizeof acceptStr);
00812       *fLastCRLF = '\r';
00813       if (parseSucceeded) {
00814 #ifdef DEBUG
00815         fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
00816 #endif
00817         // Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
00818         Boolean isValidHTTPCmd = True;
00819         if (sessionCookie[0] == '\0') {
00820           // There was no "x-sessioncookie:" header.  If there was an "Accept: application/x-rtsp-tunnelled" header,
00821           // then this is a bad tunneling request.  Otherwise, assume that it's an attempt to access the stream via HTTP.
00822           if (strcmp(acceptStr, "application/x-rtsp-tunnelled") == 0) {
00823             isValidHTTPCmd = False;
00824           } else {
00825             handleHTTPCmd_StreamingGET(urlSuffix, (char const*)fRequestBuffer);
00826           }
00827         } else if (strcmp(cmdName, "GET") == 0) {
00828           handleHTTPCmd_TunnelingGET(sessionCookie);
00829         } else if (strcmp(cmdName, "POST") == 0) {
00830           // We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
00831           // Check for this, and handle it if it exists:
00832           unsigned char const* extraData = fLastCRLF+4;
00833           unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] - extraData;
00834           if (handleHTTPCmd_TunnelingPOST(sessionCookie, extraData, extraDataSize)) {
00835             // We don't respond to the "POST" command, and we go away:
00836             fIsActive = False;
00837             break;
00838           }
00839         } else {
00840           isValidHTTPCmd = False;
00841         }
00842         if (!isValidHTTPCmd) {
00843           handleHTTPCmd_notSupported();
00844         }
00845       } else {
00846 #ifdef DEBUG
00847         fprintf(stderr, "parseHTTPRequestString() failed!\n");
00848 #endif
00849         handleCmd_bad();
00850       }
00851     }
00852     
00853 #ifdef DEBUG
00854     fprintf(stderr, "sending response: %s", fResponseBuffer);
00855 #endif
00856     send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
00857     
00858     if (clientSession != NULL && clientSession->fStreamAfterSETUP && strcmp(cmdName, "SETUP") == 0) {
00859       // The client has asked for streaming to commence now, rather than after a
00860       // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
00861       clientSession->handleCmd_withinSession(this, "PLAY", urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
00862     }
00863     
00864     // Check whether there are extra bytes remaining in the buffer, after the end of the request (a rare case).
00865     // If so, move them to the front of our buffer, and keep processing it, because it might be a following, pipelined request.
00866     unsigned requestSize = (fLastCRLF+4-fRequestBuffer) + contentLength;
00867     numBytesRemaining = fRequestBytesAlreadySeen - requestSize;
00868     resetRequestBuffer(); // to prepare for any subsequent request
00869 
00870     if (numBytesRemaining > 0) {
00871       memmove(fRequestBuffer, &fRequestBuffer[requestSize], numBytesRemaining);
00872       newBytesRead = numBytesRemaining;
00873     }
00874   } while (numBytesRemaining > 0);
00875 
00876   --fRecursionCount;
00877   if (!fIsActive) {
00878     if (fRecursionCount > 0) closeSockets(); else delete this;
00879     // Note: The "fRecursionCount" test is for a pathological situation where we reenter the event loop and get called recursively
00880     // while handling a command (e.g., while handling a "DESCRIBE", to get a SDP description).
00881     // In such a case we don't want to actually delete ourself until we leave the outermost call.
00882   }
00883 }
00884 
00885 static Boolean parseAuthorizationHeader(char const* buf,
00886                                         char const*& username,
00887                                         char const*& realm,
00888                                         char const*& nonce, char const*& uri,
00889                                         char const*& response) {
00890   // Initialize the result parameters to default values:
00891   username = realm = nonce = uri = response = NULL;
00892 
00893   // First, find "Authorization:"
00894   while (1) {
00895     if (*buf == '\0') return False; // not found
00896     if (_strncasecmp(buf, "Authorization: Digest ", 22) == 0) break;
00897     ++buf;
00898   }
00899 
00900   // Then, run through each of the fields, looking for ones we handle:
00901   char const* fields = buf + 22;
00902   while (*fields == ' ') ++fields;
00903   char* parameter = strDupSize(fields);
00904   char* value = strDupSize(fields);
00905   while (1) {
00906     value[0] = '\0';
00907     if (sscanf(fields, "%[^=]=\"%[^\"]\"", parameter, value) != 2 &&
00908         sscanf(fields, "%[^=]=\"\"", parameter) != 1) {
00909       break;
00910     }
00911     if (strcmp(parameter, "username") == 0) {
00912       username = strDup(value);
00913     } else if (strcmp(parameter, "realm") == 0) {
00914       realm = strDup(value);
00915     } else if (strcmp(parameter, "nonce") == 0) {
00916       nonce = strDup(value);
00917     } else if (strcmp(parameter, "uri") == 0) {
00918       uri = strDup(value);
00919     } else if (strcmp(parameter, "response") == 0) {
00920       response = strDup(value);
00921     }
00922 
00923     fields += strlen(parameter) + 2 /*="*/ + strlen(value) + 1 /*"*/;
00924     while (*fields == ',' || *fields == ' ') ++fields;
00925         // skip over any separating ',' and ' ' chars
00926     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
00927   }
00928   delete[] parameter; delete[] value;
00929   return True;
00930 }
00931 
00932 Boolean RTSPServer::RTSPClientConnection
00933 ::authenticationOK(char const* cmdName, char const* urlSuffix, char const* fullRequestStr) {
00934 
00935   if (!fOurServer.specialClientAccessCheck(fClientInputSocket, fClientAddr, urlSuffix)) {
00936     setRTSPResponse("401 Unauthorized");
00937     return False;
00938   }
00939 
00940   // If we weren't set up with an authentication database, we're OK:
00941   if (fOurServer.fAuthDB == NULL) return True;
00942 
00943   char const* username = NULL; char const* realm = NULL; char const* nonce = NULL;
00944   char const* uri = NULL; char const* response = NULL;
00945   Boolean success = False;
00946 
00947   do {
00948     // To authenticate, we first need to have a nonce set up
00949     // from a previous attempt:
00950     if (fCurrentAuthenticator.nonce() == NULL) break;
00951 
00952     // Next, the request needs to contain an "Authorization:" header,
00953     // containing a username, (our) realm, (our) nonce, uri,
00954     // and response string:
00955     if (!parseAuthorizationHeader(fullRequestStr,
00956                                   username, realm, nonce, uri, response)
00957         || username == NULL
00958         || realm == NULL || strcmp(realm, fCurrentAuthenticator.realm()) != 0
00959         || nonce == NULL || strcmp(nonce, fCurrentAuthenticator.nonce()) != 0
00960         || uri == NULL || response == NULL) {
00961       break;
00962     }
00963 
00964     // Next, the username has to be known to us:
00965     char const* password = fOurServer.fAuthDB->lookupPassword(username);
00966 #ifdef DEBUG
00967     fprintf(stderr, "lookupPassword(%s) returned password %s\n", username, password);
00968 #endif
00969     if (password == NULL) break;
00970     fCurrentAuthenticator.
00971       setUsernameAndPassword(username, password,
00972                              fOurServer.fAuthDB->passwordsAreMD5());
00973 
00974     // Finally, compute a digest response from the information that we have,
00975     // and compare it to the one that we were given:
00976     char const* ourResponse
00977       = fCurrentAuthenticator.computeDigestResponse(cmdName, uri);
00978     success = (strcmp(ourResponse, response) == 0);
00979     fCurrentAuthenticator.reclaimDigestResponse(ourResponse);
00980   } while (0);
00981 
00982   delete[] (char*)realm; delete[] (char*)nonce;
00983   delete[] (char*)uri; delete[] (char*)response;
00984 
00985   if (success) {
00986     // The user has been authenticated.
00987     // Now allow subclasses a chance to validate the user against the IP address and/or URL suffix.
00988     if (!fOurServer.specialClientUserAccessCheck(fClientInputSocket, fClientAddr, urlSuffix, username)) {
00989       // Note: We don't return a "WWW-Authenticate" header here, because the user is valid,
00990       // even though the server has decided that they should not have access.
00991       setRTSPResponse("401 Unauthorized");
00992       delete[] (char*)username;
00993       return False;
00994     }
00995   }
00996   delete[] (char*)username;
00997   if (success) return True;
00998 
00999   // If we get here, we failed to authenticate the user.
01000   // Send back a "401 Unauthorized" response, with a new random nonce:
01001   fCurrentAuthenticator.setRealmAndRandomNonce(fOurServer.fAuthDB->realm());
01002   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01003            "RTSP/1.0 401 Unauthorized\r\n"
01004            "CSeq: %s\r\n"
01005            "%s"
01006            "WWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\"\r\n\r\n",
01007            fCurrentCSeq,
01008            dateHeader(),
01009            fCurrentAuthenticator.realm(), fCurrentAuthenticator.nonce());
01010   return False;
01011 }
01012 
01013 void RTSPServer::RTSPClientConnection
01014 ::setRTSPResponse(char const* responseStr) {
01015   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01016            "RTSP/1.0 %s\r\n"
01017            "CSeq: %s\r\n"
01018            "%s\r\n",
01019            responseStr,
01020            fCurrentCSeq,
01021            dateHeader());
01022 }
01023 
01024 void RTSPServer::RTSPClientConnection
01025 ::setRTSPResponse(char const* responseStr, u_int32_t sessionId) {
01026   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01027            "RTSP/1.0 %s\r\n"
01028            "CSeq: %s\r\n"
01029            "%s"
01030            "Session: %08X\r\n\r\n",
01031            responseStr,
01032            fCurrentCSeq,
01033            dateHeader(),
01034            sessionId);
01035 }
01036 
01037 void RTSPServer::RTSPClientConnection
01038 ::setRTSPResponse(char const* responseStr, char const* contentStr) {
01039   if (contentStr == NULL) contentStr = "";
01040   unsigned const contentLen = strlen(contentStr);
01041 
01042   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01043            "RTSP/1.0 %s\r\n"
01044            "CSeq: %s\r\n"
01045            "%s"
01046            "Content-Length: %d\r\n\r\n"
01047            "%s",
01048            responseStr,
01049            fCurrentCSeq,
01050            dateHeader(),
01051            contentLen,
01052            contentStr);
01053 }
01054 
01055 void RTSPServer::RTSPClientConnection
01056 ::setRTSPResponse(char const* responseStr, u_int32_t sessionId, char const* contentStr) {
01057   if (contentStr == NULL) contentStr = "";
01058   unsigned const contentLen = strlen(contentStr);
01059 
01060   snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
01061            "RTSP/1.0 %s\r\n"
01062            "CSeq: %s\r\n"
01063            "%s"
01064            "Session: %08X\r\n"
01065            "Content-Length: %d\r\n\r\n"
01066            "%s",
01067            responseStr,
01068            fCurrentCSeq,
01069            dateHeader(),
01070            sessionId,
01071            contentLen,
01072            contentStr);
01073 }
01074 
01075 void RTSPServer::RTSPClientConnection
01076 ::changeClientInputSocket(int newSocketNum, unsigned char const* extraData, unsigned extraDataSize) {
01077   envir().taskScheduler().disableBackgroundHandling(fClientInputSocket);
01078   fClientInputSocket = newSocketNum;
01079   envir().taskScheduler().setBackgroundHandling(fClientInputSocket, SOCKET_READABLE|SOCKET_EXCEPTION,
01080                                                 (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler, this);
01081 
01082   // Also write any extra data to our buffer, and handle it:
01083   if (extraDataSize > 0 && extraDataSize <= fRequestBufferBytesLeft/*sanity check; should always be true*/) {
01084     unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
01085     for (unsigned i = 0; i < extraDataSize; ++i) {
01086       ptr[i] = extraData[i];
01087     }
01088     handleRequestBytes(extraDataSize);
01089   }
01090 }
01091 
01092 
01094 
01095 RTSPServer::RTSPClientSession
01096 ::RTSPClientSession(RTSPServer& ourServer, u_int32_t sessionId)
01097   : fOurServer(ourServer), fOurSessionId(sessionId), fOurServerMediaSession(NULL), fIsMulticast(False), fStreamAfterSETUP(False),
01098     fTCPStreamIdCount(0), fLivenessCheckTask(NULL), fNumStreamStates(0), fStreamStates(NULL) {
01099   noteLiveness();
01100 }
01101 
01102 RTSPServer::RTSPClientSession::~RTSPClientSession() {
01103   // Turn off any liveness checking:
01104   envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
01105 
01106   // Remove ourself from the server's 'client sessions' hash table before we go:
01107   char sessionIdStr[9];
01108   sprintf(sessionIdStr, "%08X", fOurSessionId);
01109   fOurServer.fClientSessions->Remove(sessionIdStr);
01110 
01111   reclaimStreamStates();
01112 
01113   if (fOurServerMediaSession != NULL) {
01114     fOurServerMediaSession->decrementReferenceCount();
01115     if (fOurServerMediaSession->referenceCount() == 0
01116         && fOurServerMediaSession->deleteWhenUnreferenced()) {
01117       fOurServer.removeServerMediaSession(fOurServerMediaSession);
01118       fOurServerMediaSession = NULL;
01119     }
01120   }
01121 }
01122 
01123 void RTSPServer::RTSPClientSession::reclaimStreamStates() {
01124   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01125     if (fStreamStates[i].subsession != NULL) {
01126       fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
01127     }
01128   }
01129   delete[] fStreamStates; fStreamStates = NULL;
01130   fNumStreamStates = 0;
01131 }
01132 
01133 typedef enum StreamingMode {
01134   RTP_UDP,
01135   RTP_TCP,
01136   RAW_UDP
01137 } StreamingMode;
01138 
01139 static void parseTransportHeader(char const* buf,
01140                                  StreamingMode& streamingMode,
01141                                  char*& streamingModeString,
01142                                  char*& destinationAddressStr,
01143                                  u_int8_t& destinationTTL,
01144                                  portNumBits& clientRTPPortNum, // if UDP
01145                                  portNumBits& clientRTCPPortNum, // if UDP
01146                                  unsigned char& rtpChannelId, // if TCP
01147                                  unsigned char& rtcpChannelId // if TCP
01148                                  ) {
01149   // Initialize the result parameters to default values:
01150   streamingMode = RTP_UDP;
01151   streamingModeString = NULL;
01152   destinationAddressStr = NULL;
01153   destinationTTL = 255;
01154   clientRTPPortNum = 0;
01155   clientRTCPPortNum = 1;
01156   rtpChannelId = rtcpChannelId = 0xFF;
01157 
01158   portNumBits p1, p2;
01159   unsigned ttl, rtpCid, rtcpCid;
01160 
01161   // First, find "Transport:"
01162   while (1) {
01163     if (*buf == '\0') return; // not found
01164     if (*buf == '\r' && *(buf+1) == '\n' && *(buf+2) == '\r') return; // end of the headers => not found
01165     if (_strncasecmp(buf, "Transport:", 10) == 0) break;
01166     ++buf;
01167   }
01168 
01169   // Then, run through each of the fields, looking for ones we handle:
01170   char const* fields = buf + 10;
01171   while (*fields == ' ') ++fields;
01172   char* field = strDupSize(fields);
01173   while (sscanf(fields, "%[^;\r\n]", field) == 1) {
01174     if (strcmp(field, "RTP/AVP/TCP") == 0) {
01175       streamingMode = RTP_TCP;
01176     } else if (strcmp(field, "RAW/RAW/UDP") == 0 ||
01177                strcmp(field, "MP2T/H2221/UDP") == 0) {
01178       streamingMode = RAW_UDP;
01179       streamingModeString = strDup(field);
01180     } else if (_strncasecmp(field, "destination=", 12) == 0) {
01181       delete[] destinationAddressStr;
01182       destinationAddressStr = strDup(field+12);
01183     } else if (sscanf(field, "ttl%u", &ttl) == 1) {
01184       destinationTTL = (u_int8_t)ttl;
01185     } else if (sscanf(field, "client_port=%hu-%hu", &p1, &p2) == 2) {
01186         clientRTPPortNum = p1;
01187         clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p2; // ignore the second port number if the client asked for raw UDP
01188     } else if (sscanf(field, "client_port=%hu", &p1) == 1) {
01189         clientRTPPortNum = p1;
01190         clientRTCPPortNum = streamingMode == RAW_UDP ? 0 : p1 + 1;
01191     } else if (sscanf(field, "interleaved=%u-%u", &rtpCid, &rtcpCid) == 2) {
01192       rtpChannelId = (unsigned char)rtpCid;
01193       rtcpChannelId = (unsigned char)rtcpCid;
01194     }
01195 
01196     fields += strlen(field);
01197     while (*fields == ';') ++fields; // skip over separating ';' chars
01198     if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
01199   }
01200   delete[] field;
01201 }
01202 
01203 static Boolean parsePlayNowHeader(char const* buf) {
01204   // Find "x-playNow:" header, if present
01205   while (1) {
01206     if (*buf == '\0') return False; // not found
01207     if (_strncasecmp(buf, "x-playNow:", 10) == 0) break;
01208     ++buf;
01209   }
01210 
01211   return True;
01212 }
01213 
01214 void RTSPServer::RTSPClientSession
01215 ::handleCmd_SETUP(RTSPServer::RTSPClientConnection* ourClientConnection,
01216                   char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {
01217   // Normally, "urlPreSuffix" should be the session (stream) name, and "urlSuffix" should be the subsession (track) name.
01218   // However (being "liberal in what we accept"), we also handle 'aggregate' SETUP requests (i.e., without a track name),
01219   // in the special case where we have only a single track.  I.e., in this case, we also handle:
01220   //    "urlPreSuffix" is empty and "urlSuffix" is the session (stream) name, or
01221   //    "urlPreSuffix" concatenated with "urlSuffix" (with "/" inbetween) is the session (stream) name.
01222   char const* streamName = urlPreSuffix; // in the normal case
01223   char const* trackId = urlSuffix; // in the normal case
01224   char* concatenatedStreamName = NULL; // in the normal case
01225 
01226   noteLiveness();
01227   do {
01228     // First, make sure the specified stream name exists:
01229     ServerMediaSession* sms = fOurServer.lookupServerMediaSession(streamName);
01230     if (sms == NULL) {
01231       // Check for the special case (noted above), before we give up:
01232       if (urlPreSuffix[0] == '\0') {
01233         streamName = urlSuffix;
01234       } else {
01235         concatenatedStreamName = new char[strlen(urlPreSuffix) + strlen(urlSuffix) + 2]; // allow for the "/" and the trailing '\0'
01236         sprintf(concatenatedStreamName, "%s/%s", urlPreSuffix, urlSuffix);
01237         streamName = concatenatedStreamName;
01238       }
01239       trackId = NULL;
01240 
01241       // Check again:
01242       sms = fOurServer.lookupServerMediaSession(streamName);
01243     }
01244     if (sms == NULL) {
01245       if (fOurServerMediaSession == NULL) {
01246         // The client asked for a stream that doesn't exist (and this session descriptor has not been used before):
01247         ourClientConnection->handleCmd_notFound();
01248       } else {
01249         // The client asked for a stream that doesn't exist, but using a stream id for a stream that does exist. Bad request:
01250         ourClientConnection->handleCmd_bad();
01251       }
01252       break;
01253     } else {
01254       if (fOurServerMediaSession == NULL) {
01255         // We're accessing the "ServerMediaSession" for the first time.
01256         fOurServerMediaSession = sms;
01257         fOurServerMediaSession->incrementReferenceCount();
01258       } else if (sms != fOurServerMediaSession) {
01259         // The client asked for a stream that's different from the one originally requested for this stream id.  Bad request:
01260         ourClientConnection->handleCmd_bad();
01261         break;
01262       }
01263     }
01264 
01265     if (fStreamStates == NULL) {
01266       // This is the first "SETUP" for this session.  Set up our array of states for all of this session's subsessions (tracks):
01267       ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01268       for (fNumStreamStates = 0; iter.next() != NULL; ++fNumStreamStates) {} // begin by counting the number of subsessions (tracks)
01269 
01270       fStreamStates = new struct streamState[fNumStreamStates];
01271 
01272       iter.reset();
01273       ServerMediaSubsession* subsession;
01274       for (unsigned i = 0; i < fNumStreamStates; ++i) {
01275         subsession = iter.next();
01276         fStreamStates[i].subsession = subsession;
01277         fStreamStates[i].streamToken = NULL; // for now; it may be changed by the "getStreamParameters()" call that comes later
01278       }
01279     }
01280 
01281     // Look up information for the specified subsession (track):
01282     ServerMediaSubsession* subsession = NULL;
01283     unsigned streamNum;
01284     if (trackId != NULL && trackId[0] != '\0') { // normal case
01285       for (streamNum = 0; streamNum < fNumStreamStates; ++streamNum) {
01286         subsession = fStreamStates[streamNum].subsession;
01287         if (subsession != NULL && strcmp(trackId, subsession->trackId()) == 0) break;
01288       }
01289       if (streamNum >= fNumStreamStates) {
01290         // The specified track id doesn't exist, so this request fails:
01291         ourClientConnection->handleCmd_notFound();
01292         break;
01293       }
01294     } else {
01295       // Weird case: there was no track id in the URL.
01296       // This works only if we have only one subsession:
01297       if (fNumStreamStates != 1 || fStreamStates[0].subsession == NULL) {
01298         ourClientConnection->handleCmd_bad();
01299         break;
01300       }
01301       streamNum = 0;
01302       subsession = fStreamStates[streamNum].subsession;
01303     }
01304     // ASSERT: subsession != NULL
01305 
01306     // Look for a "Transport:" header in the request string, to extract client parameters:
01307     StreamingMode streamingMode;
01308     char* streamingModeString = NULL; // set when RAW_UDP streaming is specified
01309     char* clientsDestinationAddressStr;
01310     u_int8_t clientsDestinationTTL;
01311     portNumBits clientRTPPortNum, clientRTCPPortNum;
01312     unsigned char rtpChannelId, rtcpChannelId;
01313     parseTransportHeader(fullRequestStr, streamingMode, streamingModeString,
01314                          clientsDestinationAddressStr, clientsDestinationTTL,
01315                          clientRTPPortNum, clientRTCPPortNum,
01316                          rtpChannelId, rtcpChannelId);
01317     if ((streamingMode == RTP_TCP && rtpChannelId == 0xFF) ||
01318         (streamingMode != RTP_TCP && ourClientConnection->fClientOutputSocket != ourClientConnection->fClientInputSocket)) {
01319       // An anomolous situation, caused by a buggy client.  Either:
01320       //     1/ TCP streaming was requested, but with no "interleaving=" fields.  (QuickTime Player sometimes does this.), or
01321       //     2/ TCP streaming was not requested, but we're doing RTSP-over-HTTP tunneling (which implies TCP streaming).
01322       // In either case, we assume TCP streaming, and set the RTP and RTCP channel ids to proper values:
01323       streamingMode = RTP_TCP;
01324       rtpChannelId = fTCPStreamIdCount; rtcpChannelId = fTCPStreamIdCount+1;
01325     }
01326     if (streamingMode == RTP_TCP) fTCPStreamIdCount += 2;
01327 
01328     Port clientRTPPort(clientRTPPortNum);
01329     Port clientRTCPPort(clientRTCPPortNum);
01330 
01331     // Next, check whether a "Range:" or "x-playNow:" header is present in the request.
01332     // This isn't legal, but some clients do this to combine "SETUP" and "PLAY":
01333     double rangeStart = 0.0, rangeEnd = 0.0;
01334     char* absStart = NULL; char* absEnd = NULL;
01335     if (parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd)) {
01336       delete[] absStart; delete[] absEnd;
01337       fStreamAfterSETUP = True;
01338     } else if (parsePlayNowHeader(fullRequestStr)) {
01339       fStreamAfterSETUP = True;
01340     } else {
01341       fStreamAfterSETUP = False;
01342     }
01343 
01344     // Then, get server parameters from the 'subsession':
01345     int tcpSocketNum = streamingMode == RTP_TCP ? ourClientConnection->fClientOutputSocket : -1;
01346     netAddressBits destinationAddress = 0;
01347     u_int8_t destinationTTL = 255;
01348 #ifdef RTSP_ALLOW_CLIENT_DESTINATION_SETTING
01349     if (clientsDestinationAddressStr != NULL) {
01350       // Use the client-provided "destination" address.
01351       // Note: This potentially allows the server to be used in denial-of-service
01352       // attacks, so don't enable this code unless you're sure that clients are
01353       // trusted.
01354       destinationAddress = our_inet_addr(clientsDestinationAddressStr);
01355     }
01356     // Also use the client-provided TTL.
01357     destinationTTL = clientsDestinationTTL;
01358 #endif
01359     delete[] clientsDestinationAddressStr;
01360     Port serverRTPPort(0);
01361     Port serverRTCPPort(0);
01362 
01363     // Make sure that we transmit on the same interface that's used by the client (in case we're a multi-homed server):
01364     struct sockaddr_in sourceAddr; SOCKLEN_T namelen = sizeof sourceAddr;
01365     getsockname(ourClientConnection->fClientInputSocket, (struct sockaddr*)&sourceAddr, &namelen);
01366     netAddressBits origSendingInterfaceAddr = SendingInterfaceAddr;
01367     netAddressBits origReceivingInterfaceAddr = ReceivingInterfaceAddr;
01368     // NOTE: The following might not work properly, so we ifdef it out for now:
01369 #ifdef HACK_FOR_MULTIHOMED_SERVERS
01370     ReceivingInterfaceAddr = SendingInterfaceAddr = sourceAddr.sin_addr.s_addr;
01371 #endif
01372 
01373     subsession->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
01374                                     clientRTPPort, clientRTCPPort,
01375                                     tcpSocketNum, rtpChannelId, rtcpChannelId,
01376                                     destinationAddress, destinationTTL, fIsMulticast,
01377                                     serverRTPPort, serverRTCPPort,
01378                                     fStreamStates[streamNum].streamToken);
01379     SendingInterfaceAddr = origSendingInterfaceAddr;
01380     ReceivingInterfaceAddr = origReceivingInterfaceAddr;
01381     
01382     AddressString destAddrStr(destinationAddress);
01383     AddressString sourceAddrStr(sourceAddr);
01384     if (fIsMulticast) {
01385       switch (streamingMode) {
01386         case RTP_UDP:
01387           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01388                    "RTSP/1.0 200 OK\r\n"
01389                    "CSeq: %s\r\n"
01390                    "%s"
01391                    "Transport: RTP/AVP;multicast;destination=%s;source=%s;port=%d-%d;ttl=%d\r\n"
01392                    "Session: %08X\r\n\r\n",
01393                    ourClientConnection->fCurrentCSeq,
01394                    dateHeader(),
01395                    destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()), destinationTTL,
01396                    fOurSessionId);
01397           break;
01398         case RTP_TCP:
01399           // multicast streams can't be sent via TCP
01400           ourClientConnection->handleCmd_unsupportedTransport();
01401           break;
01402         case RAW_UDP:
01403           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01404                    "RTSP/1.0 200 OK\r\n"
01405                    "CSeq: %s\r\n"
01406                    "%s"
01407                    "Transport: %s;multicast;destination=%s;source=%s;port=%d;ttl=%d\r\n"
01408                    "Session: %08X\r\n\r\n",
01409                    ourClientConnection->fCurrentCSeq,
01410                    dateHeader(),
01411                    streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(serverRTPPort.num()), destinationTTL,
01412                    fOurSessionId);
01413           break;
01414       }
01415     } else {
01416       switch (streamingMode) {
01417         case RTP_UDP: {
01418           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01419                    "RTSP/1.0 200 OK\r\n"
01420                    "CSeq: %s\r\n"
01421                    "%s"
01422                    "Transport: RTP/AVP;unicast;destination=%s;source=%s;client_port=%d-%d;server_port=%d-%d\r\n"
01423                    "Session: %08X\r\n\r\n",
01424                    ourClientConnection->fCurrentCSeq,
01425                    dateHeader(),
01426                    destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(clientRTCPPort.num()), ntohs(serverRTPPort.num()), ntohs(serverRTCPPort.num()),
01427                    fOurSessionId);
01428           break;
01429         }
01430         case RTP_TCP: {
01431           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01432                    "RTSP/1.0 200 OK\r\n"
01433                    "CSeq: %s\r\n"
01434                    "%s"
01435                    "Transport: RTP/AVP/TCP;unicast;destination=%s;source=%s;interleaved=%d-%d\r\n"
01436                    "Session: %08X\r\n\r\n",
01437                    ourClientConnection->fCurrentCSeq,
01438                    dateHeader(),
01439                    destAddrStr.val(), sourceAddrStr.val(), rtpChannelId, rtcpChannelId,
01440                    fOurSessionId);
01441           break;
01442         }
01443         case RAW_UDP: {
01444           snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01445                    "RTSP/1.0 200 OK\r\n"
01446                    "CSeq: %s\r\n"
01447                    "%s"
01448                    "Transport: %s;unicast;destination=%s;source=%s;client_port=%d;server_port=%d\r\n"
01449                    "Session: %08X\r\n\r\n",
01450                    ourClientConnection->fCurrentCSeq,
01451                    dateHeader(),
01452                    streamingModeString, destAddrStr.val(), sourceAddrStr.val(), ntohs(clientRTPPort.num()), ntohs(serverRTPPort.num()),
01453                    fOurSessionId);
01454           break;
01455         }
01456       }
01457     }
01458     delete[] streamingModeString;
01459   } while (0);
01460 
01461   delete[] concatenatedStreamName;
01462 }
01463 
01464 void RTSPServer::RTSPClientSession
01465 ::handleCmd_withinSession(RTSPServer::RTSPClientConnection* ourClientConnection,
01466                           char const* cmdName,
01467                           char const* urlPreSuffix, char const* urlSuffix,
01468                           char const* fullRequestStr) {
01469   // This will either be:
01470   // - a non-aggregated operation, if "urlPreSuffix" is the session (stream)
01471   //   name and "urlSuffix" is the subsession (track) name, or
01472   // - an aggregated operation, if "urlSuffix" is the session (stream) name,
01473   //   or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,
01474   //   or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.
01475   // Begin by figuring out which of these it is:
01476   ServerMediaSubsession* subsession;
01477 
01478   noteLiveness();
01479   if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!
01480     ourClientConnection->handleCmd_notSupported();
01481     return;
01482   } else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {
01483     // Non-aggregated operation.
01484     // Look up the media subsession whose track id is "urlSuffix":
01485     ServerMediaSubsessionIterator iter(*fOurServerMediaSession);
01486     while ((subsession = iter.next()) != NULL) {
01487       if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success
01488     }
01489     if (subsession == NULL) { // no such track!
01490       ourClientConnection->handleCmd_notFound();
01491       return;
01492     }
01493   } else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||
01494              (urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {
01495     // Aggregated operation
01496     subsession = NULL;
01497   } else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {
01498     // Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:
01499     unsigned const urlPreSuffixLen = strlen(urlPreSuffix);
01500     if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0 &&
01501         fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/' &&
01502         strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen+1], urlSuffix) == 0) {
01503       subsession = NULL;
01504     } else {
01505       ourClientConnection->handleCmd_notFound();
01506       return;
01507     }
01508   } else { // the request doesn't match a known stream and/or track at all!
01509     ourClientConnection->handleCmd_notFound();
01510     return;
01511   }
01512 
01513   if (strcmp(cmdName, "TEARDOWN") == 0) {
01514     handleCmd_TEARDOWN(ourClientConnection, subsession);
01515   } else if (strcmp(cmdName, "PLAY") == 0) {
01516     handleCmd_PLAY(ourClientConnection, subsession, fullRequestStr);
01517   } else if (strcmp(cmdName, "PAUSE") == 0) {
01518     handleCmd_PAUSE(ourClientConnection, subsession);
01519   } else if (strcmp(cmdName, "GET_PARAMETER") == 0) {
01520     handleCmd_GET_PARAMETER(ourClientConnection, subsession, fullRequestStr);
01521   } else if (strcmp(cmdName, "SET_PARAMETER") == 0) {
01522     handleCmd_SET_PARAMETER(ourClientConnection, subsession, fullRequestStr);
01523   }
01524 }
01525 
01526 void RTSPServer::RTSPClientSession
01527 ::handleCmd_TEARDOWN(RTSPServer::RTSPClientConnection* ourClientConnection,
01528                      ServerMediaSubsession* subsession) {
01529   unsigned i;
01530   for (i = 0; i < fNumStreamStates; ++i) {
01531     if (subsession == NULL /* means: aggregated operation */
01532         || subsession == fStreamStates[i].subsession) {
01533       if (fStreamStates[i].subsession != NULL) {
01534         fStreamStates[i].subsession->deleteStream(fOurSessionId, fStreamStates[i].streamToken);
01535         fStreamStates[i].subsession = NULL;
01536       }
01537     }
01538   }
01539 
01540   ourClientConnection->setRTSPResponse("200 OK");
01541 
01542   // Optimization: If all subsessions have now been torn down, then we know that we can reclaim our object now.
01543   // (Without this optimization, however, this object would still get reclaimed later, as a result of a 'liveness' timeout.)
01544   Boolean noSubsessionsRemain = True;
01545   for (i = 0; i < fNumStreamStates; ++i) {
01546     if (fStreamStates[i].subsession != NULL) {
01547       noSubsessionsRemain = False;
01548       break;
01549     }
01550   }
01551   if (noSubsessionsRemain) delete this;
01552 }
01553 
01554 static Boolean parseScaleHeader(char const* buf, float& scale) {
01555   // Initialize the result parameter to a default value:
01556   scale = 1.0;
01557 
01558   // First, find "Scale:"
01559   while (1) {
01560     if (*buf == '\0') return False; // not found
01561     if (_strncasecmp(buf, "Scale:", 6) == 0) break;
01562     ++buf;
01563   }
01564 
01565   // Then, run through each of the fields, looking for ones we handle:
01566   char const* fields = buf + 6;
01567   while (*fields == ' ') ++fields;
01568   float sc;
01569   if (sscanf(fields, "%f", &sc) == 1) {
01570     scale = sc;
01571   } else {
01572     return False; // The header is malformed
01573   }
01574 
01575   return True;
01576 }
01577 
01578 void RTSPServer::RTSPClientSession
01579 ::handleCmd_PLAY(RTSPServer::RTSPClientConnection* ourClientConnection,
01580                  ServerMediaSubsession* subsession, char const* fullRequestStr) {
01581   char* rtspURL = fOurServer.rtspURL(fOurServerMediaSession, ourClientConnection->fClientInputSocket);
01582   unsigned rtspURLSize = strlen(rtspURL);
01583 
01584   // Parse the client's "Scale:" header, if any:
01585   float scale;
01586   Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);
01587 
01588   // Try to set the stream's scale factor to this value:
01589   if (subsession == NULL /*aggregate op*/) {
01590     fOurServerMediaSession->testScaleFactor(scale);
01591   } else {
01592     subsession->testScaleFactor(scale);
01593   }
01594 
01595   char buf[100];
01596   char* scaleHeader;
01597   if (!sawScaleHeader) {
01598     buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back
01599   } else {
01600     sprintf(buf, "Scale: %f\r\n", scale);
01601   }
01602   scaleHeader = strDup(buf);
01603 
01604   // Parse the client's "Range:" header, if any:
01605   float duration = 0.0;
01606   double rangeStart = 0.0, rangeEnd = 0.0;
01607   char* absStart = NULL; char* absEnd = NULL;
01608   Boolean sawRangeHeader = parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd);
01609 
01610   if (sawRangeHeader && absStart == NULL/*not seeking by 'absolute' time*/) {
01611     // Use this information, plus the stream's duration (if known), to create our own "Range:" header, for the response:
01612     duration = subsession == NULL /*aggregate op*/
01613       ? fOurServerMediaSession->duration() : subsession->duration();
01614     if (duration < 0.0) {
01615       // We're an aggregate PLAY, but the subsessions have different durations.
01616       // Use the largest of these durations in our header
01617       duration = -duration;
01618     }
01619 
01620     // Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header) have sane values
01621     // before we send back our own "Range:" header in our response:
01622     if (rangeStart < 0.0) rangeStart = 0.0;
01623     else if (rangeStart > duration) rangeStart = duration;
01624     if (rangeEnd < 0.0) rangeEnd = 0.0;
01625     else if (rangeEnd > duration) rangeEnd = duration;
01626     if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0) ||
01627         (scale < 0.0 && rangeStart < rangeEnd)) {
01628       // "rangeStart" and "rangeEnd" were the wrong way around; swap them:
01629       double tmp = rangeStart;
01630       rangeStart = rangeEnd;
01631       rangeEnd = tmp;
01632     }
01633   }
01634 
01635   // Create a "RTP-Info:" line.  It will get filled in from each subsession's state:
01636   char const* rtpInfoFmt =
01637     "%s" // "RTP-Info:", plus any preceding rtpInfo items
01638     "%s" // comma separator, if needed
01639     "url=%s/%s"
01640     ";seq=%d"
01641     ";rtptime=%u"
01642     ;
01643   unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);
01644   char* rtpInfo = strDup("RTP-Info: ");
01645   unsigned i, numRTPInfoItems = 0;
01646 
01647   // Do any required seeking/scaling on each subsession, before starting streaming.
01648   // (However, we don't do this if the "PLAY" request was for just a single subsession of a multiple-subsession stream;
01649   //  for such streams, seeking/scaling can be done only with an aggregate "PLAY".)
01650   for (i = 0; i < fNumStreamStates; ++i) {
01651     if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {
01652       if (sawScaleHeader) {
01653         if (fStreamStates[i].subsession != NULL) {
01654           fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale);
01655         }
01656       }
01657       if (sawRangeHeader) {
01658         if (absStart != NULL) {
01659           // Special case handling for seeking by 'absolute' time:
01660 
01661           if (fStreamStates[i].subsession != NULL) {
01662             fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);
01663           }
01664         } else {
01665           // Seeking by relative (NPT) time:
01666 
01667           double streamDuration = 0.0; // by default; means: stream until the end of the media
01668           if (rangeEnd > 0.0 && (rangeEnd+0.001) < duration) { // the 0.001 is because we limited the values to 3 decimal places
01669             // We want the stream to end early.  Set the duration we want:
01670             streamDuration = rangeEnd - rangeStart;
01671             if (streamDuration < 0.0) streamDuration = -streamDuration; // should happen only if scale < 0.0
01672           }
01673           if (fStreamStates[i].subsession != NULL) {
01674             u_int64_t numBytes;
01675             fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken,
01676                                                     rangeStart, streamDuration, numBytes);
01677           }
01678         }
01679       } else {
01680         // No "Range:" header was specified in the "PLAY", so we do a 'null' seek (i.e., we don't seek at all):
01681         if (fStreamStates[i].subsession != NULL) {
01682           fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken);
01683         }
01684       }
01685     }
01686   }
01687 
01688   // Create the "Range:" header that we'll send back in our response.
01689   // (Note that we do this after seeking, in case the seeking operation changed the range start time.)
01690   char* rangeHeader;
01691   if (!sawRangeHeader) {
01692     // There wasn't a "Range:" header in the request, so, in our response, begin the range with the current NPT (normal play time):
01693     float curNPT = 0.0;
01694     for (i = 0; i < fNumStreamStates; ++i) {
01695       if (subsession == NULL /* means: aggregated operation */
01696           || subsession == fStreamStates[i].subsession) {
01697         if (fStreamStates[i].subsession == NULL) continue;
01698         float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);
01699         if (npt > curNPT) curNPT = npt;
01700         // Note: If this is an aggregate "PLAY" on a multi-subsession stream, then it's conceivable that the NPTs of each subsession
01701         // may differ (if there has been a previous seek on just one subsession).  In this (unusual) case, we just return the
01702         // largest NPT; I hope that turns out OK...
01703       }
01704     }
01705 
01706     sprintf(buf, "Range: npt=%.3f-\r\n", curNPT);
01707   } else if (absStart != NULL) {
01708     // We're seeking by 'absolute' time:
01709     if (absEnd == NULL) {
01710       sprintf(buf, "Range: clock=%s-\r\n", absStart);
01711     } else {
01712       sprintf(buf, "Range: clock=%s-%s\r\n", absStart, absEnd);
01713     }
01714     delete[] absStart; delete[] absEnd;
01715   } else {
01716     // We're seeking by relative (NPT) time:
01717     if (rangeEnd == 0.0 && scale >= 0.0) {
01718       sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);
01719     } else {
01720       sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);
01721     }
01722   }
01723   rangeHeader = strDup(buf);
01724 
01725   // Now, start streaming:
01726   for (i = 0; i < fNumStreamStates; ++i) {
01727     if (subsession == NULL /* means: aggregated operation */
01728         || subsession == fStreamStates[i].subsession) {
01729       unsigned short rtpSeqNum = 0;
01730       unsigned rtpTimestamp = 0;
01731       if (fStreamStates[i].subsession == NULL) continue;
01732       fStreamStates[i].subsession->startStream(fOurSessionId,
01733                                                fStreamStates[i].streamToken,
01734                                                (TaskFunc*)noteClientLiveness, this,
01735                                                rtpSeqNum, rtpTimestamp,
01736                                                RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection);
01737       const char *urlSuffix = fStreamStates[i].subsession->trackId();
01738       char* prevRTPInfo = rtpInfo;
01739       unsigned rtpInfoSize = rtpInfoFmtSize
01740         + strlen(prevRTPInfo)
01741         + 1
01742         + rtspURLSize + strlen(urlSuffix)
01743         + 5 /*max unsigned short len*/
01744         + 10 /*max unsigned (32-bit) len*/
01745         + 2 /*allows for trailing \r\n at final end of string*/;
01746       rtpInfo = new char[rtpInfoSize];
01747       sprintf(rtpInfo, rtpInfoFmt,
01748               prevRTPInfo,
01749               numRTPInfoItems++ == 0 ? "" : ",",
01750               rtspURL, urlSuffix,
01751               rtpSeqNum,
01752               rtpTimestamp
01753               );
01754       delete[] prevRTPInfo;
01755     }
01756   }
01757   if (numRTPInfoItems == 0) {
01758     rtpInfo[0] = '\0';
01759   } else {
01760     unsigned rtpInfoLen = strlen(rtpInfo);
01761     rtpInfo[rtpInfoLen] = '\r';
01762     rtpInfo[rtpInfoLen+1] = '\n';
01763     rtpInfo[rtpInfoLen+2] = '\0';
01764   }
01765 
01766   // Fill in the response:
01767   snprintf((char*)ourClientConnection->fResponseBuffer, sizeof ourClientConnection->fResponseBuffer,
01768            "RTSP/1.0 200 OK\r\n"
01769            "CSeq: %s\r\n"
01770            "%s"
01771            "%s"
01772            "%s"
01773            "Session: %08X\r\n"
01774            "%s\r\n",
01775            ourClientConnection->fCurrentCSeq,
01776            dateHeader(),
01777            scaleHeader,
01778            rangeHeader,
01779            fOurSessionId,
01780            rtpInfo);
01781   delete[] rtpInfo; delete[] rangeHeader;
01782   delete[] scaleHeader; delete[] rtspURL;
01783 }
01784 
01785 void RTSPServer::RTSPClientSession
01786 ::handleCmd_PAUSE(RTSPServer::RTSPClientConnection* ourClientConnection,
01787                   ServerMediaSubsession* subsession) {
01788   for (unsigned i = 0; i < fNumStreamStates; ++i) {
01789     if (subsession == NULL /* means: aggregated operation */
01790         || subsession == fStreamStates[i].subsession) {
01791       if (fStreamStates[i].subsession != NULL) {
01792         fStreamStates[i].subsession->pauseStream(fOurSessionId, fStreamStates[i].streamToken);
01793       }
01794     }
01795   }
01796 
01797   ourClientConnection->setRTSPResponse("200 OK", fOurSessionId);
01798 }
01799 
01800 void RTSPServer::RTSPClientSession
01801 ::handleCmd_GET_PARAMETER(RTSPServer::RTSPClientConnection* ourClientConnection,
01802                           ServerMediaSubsession* /*subsession*/, char const* /*fullRequestStr*/) {
01803   // By default, we implement "GET_PARAMETER" just as a 'keep alive', and send back a dummy response.
01804   // (If you want to handle "GET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
01805   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
01806   ourClientConnection->setRTSPResponse("200 OK", fOurSessionId, LIVEMEDIA_LIBRARY_VERSION_STRING);
01807 }
01808 
01809 void RTSPServer::RTSPClientSession
01810 ::handleCmd_SET_PARAMETER(RTSPServer::RTSPClientConnection* ourClientConnection,
01811                           ServerMediaSubsession* /*subsession*/, char const* /*fullRequestStr*/) {
01812   // By default, we implement "SET_PARAMETER" just as a 'keep alive', and send back an empty response.
01813   // (If you want to handle "SET_PARAMETER" properly, you can do so by defining a subclass of "RTSPServer"
01814   // and "RTSPServer::RTSPClientSession", and then reimplement this virtual function in your subclass.)
01815   ourClientConnection->setRTSPResponse("200 OK", fOurSessionId);
01816 }
01817 
01818 RTSPServer::RTSPClientConnection*
01819 RTSPServer::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {
01820   return new RTSPClientConnection(*this, clientSocket, clientAddr);
01821 }
01822 
01823 RTSPServer::RTSPClientSession*
01824 RTSPServer::createNewClientSession(u_int32_t sessionId) {
01825   return new RTSPClientSession(*this, sessionId);
01826 }
01827 
01828 void RTSPServer::RTSPClientSession::noteLiveness() {
01829   if (fOurServer.fReclamationTestSeconds > 0) {
01830     envir().taskScheduler()
01831       .rescheduleDelayedTask(fLivenessCheckTask,
01832                              fOurServer.fReclamationTestSeconds*1000000,
01833                              (TaskFunc*)livenessTimeoutTask, this);
01834   }
01835 }
01836 
01837 void RTSPServer::RTSPClientSession
01838 ::noteClientLiveness(RTSPClientSession* clientSession) {
01839 #ifdef DEBUG
01840   char const* streamName
01841     = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
01842   fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\"): Liveness indication\n",
01843           clientSession->fOurSessionId, streamName);
01844 #endif
01845   clientSession->noteLiveness();
01846 }
01847 
01848 void RTSPServer::RTSPClientSession
01849 ::livenessTimeoutTask(RTSPClientSession* clientSession) {
01850   // If this gets called, the client session is assumed to have timed out,
01851   // so delete it:
01852 #ifdef DEBUG
01853   char const* streamName
01854     = (clientSession->fOurServerMediaSession == NULL) ? "???" : clientSession->fOurServerMediaSession->streamName();
01855   fprintf(stderr, "RTSP client session (id \"%08X\", stream name \"%s\") has timed out (due to inactivity)\n",
01856           clientSession->fOurSessionId, streamName);
01857 #endif
01858   delete clientSession;
01859 }
01860 
01861 
01863 
01864 RTSPServer::ServerMediaSessionIterator
01865 ::ServerMediaSessionIterator(RTSPServer& server)
01866   : fOurIterator((server.fServerMediaSessions == NULL)
01867                  ? NULL : HashTable::Iterator::create(*server.fServerMediaSessions)) {
01868 }
01869 
01870 RTSPServer::ServerMediaSessionIterator::~ServerMediaSessionIterator() {
01871   delete fOurIterator;
01872 }
01873 
01874 ServerMediaSession* RTSPServer::ServerMediaSessionIterator::next() {
01875   if (fOurIterator == NULL) return NULL;
01876 
01877   char const* key; // dummy
01878   return (ServerMediaSession*)(fOurIterator->next(key));
01879 }
01880 
01881 
01883 
01884 UserAuthenticationDatabase::UserAuthenticationDatabase(char const* realm,
01885                                                        Boolean passwordsAreMD5)
01886   : fTable(HashTable::create(STRING_HASH_KEYS)),
01887     fRealm(strDup(realm == NULL ? "LIVE555 Streaming Media" : realm)),
01888     fPasswordsAreMD5(passwordsAreMD5) {
01889 }
01890 
01891 UserAuthenticationDatabase::~UserAuthenticationDatabase() {
01892   delete[] fRealm;
01893 
01894   // Delete the allocated 'password' strings that we stored in the table, and then the table itself:
01895   char* password;
01896   while ((password = (char*)fTable->RemoveNext()) != NULL) {
01897     delete[] password;
01898   }
01899   delete fTable;
01900 }
01901 
01902 void UserAuthenticationDatabase::addUserRecord(char const* username,
01903                                                char const* password) {
01904   fTable->Add(username, (void*)(strDup(password)));
01905 }
01906 
01907 void UserAuthenticationDatabase::removeUserRecord(char const* username) {
01908   char* password = (char*)(fTable->Lookup(username));
01909   fTable->Remove(username);
01910   delete[] password;
01911 }
01912 
01913 char const* UserAuthenticationDatabase::lookupPassword(char const* username) {
01914   return (char const*)(fTable->Lookup(username));
01915 }

Generated on Mon Apr 29 13:28:03 2013 for live by  doxygen 1.5.2