~ubuntu-branches/ubuntu/trusty/liblivemedia/trusty

« back to all changes in this revision

Viewing changes to liveMedia/RTSPServer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessio Treglia
  • Date: 2011-08-26 17:17:25 UTC
  • mfrom: (1.4.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20110826171725-5gb8upxbu1j6i35v
Tags: 2011.08.22-1
* Team upload.
* New upstream release::
  - Updated the "RTSPServer" code to handle a rare condition whereby
    handleRequestBytes() gets called recursively. This can happen only
    when we handle a "DESCRIBE" by re-calling the event loop (e.g., to
    wait for SDP parameters to become available).)  If this happens,
    we need to make sure that we don't delete the "RTSPClientSession"
    object until we leave the outermost call.

Show diffs side-by-side

added added

removed removed

Lines of Context:
282
282
    fClientInputSocket(clientSocket), fClientOutputSocket(clientSocket), fClientAddr(clientAddr),
283
283
    fSessionCookie(NULL), fLivenessCheckTask(NULL),
284
284
    fIsMulticast(False), fSessionIsActive(True), fStreamAfterSETUP(False),
285
 
    fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL) {
 
285
    fTCPStreamIdCount(0), fNumStreamStates(0), fStreamStates(NULL), fRecursionCount(0) {
286
286
  // Arrange to handle incoming requests:
287
287
  resetRequestBuffer();
288
288
  envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
291
291
}
292
292
 
293
293
RTSPServer::RTSPClientSession::~RTSPClientSession() {
294
 
  // Turn off any liveness checking:
295
 
  envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
296
 
 
297
 
  // Turn off background read handling:
298
 
  envir().taskScheduler().turnOffBackgroundReadHandling(fClientInputSocket);
299
 
 
300
 
  if (fClientOutputSocket != fClientInputSocket) ::closeSocket(fClientOutputSocket);
301
 
  ::closeSocket(fClientInputSocket);
 
294
  closeSockets();
302
295
 
303
296
  if (fSessionCookie != NULL) {
304
297
    // We were being used for RTSP-over-HTTP tunneling.  Remove ourselves from the 'session cookie' hash table before we go:
318
311
  }
319
312
}
320
313
 
 
314
void RTSPServer::RTSPClientSession::closeSockets() {
 
315
  // Turn off any liveness checking:
 
316
  envir().taskScheduler().unscheduleDelayedTask(fLivenessCheckTask);
 
317
 
 
318
  // Turn off background read handling:
 
319
  envir().taskScheduler().turnOffBackgroundReadHandling(fClientInputSocket);
 
320
 
 
321
  if (fClientOutputSocket != fClientInputSocket) ::closeSocket(fClientOutputSocket);
 
322
  ::closeSocket(fClientInputSocket);
 
323
 
 
324
  fClientInputSocket = fClientOutputSocket = -1;
 
325
}
 
326
 
321
327
void RTSPServer::RTSPClientSession::reclaimStreamStates() {
322
328
  for (unsigned i = 0; i < fNumStreamStates; ++i) {
323
329
    if (fStreamStates[i].subsession != NULL) {
361
367
}
362
368
 
363
369
void RTSPServer::RTSPClientSession::handleRequestBytes(int newBytesRead) {
364
 
  noteLiveness();
365
 
 
366
 
  if (newBytesRead <= 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {
367
 
    // Either the client socket has died, or the request was too big for us.
368
 
    // Terminate this connection:
369
 
#ifdef DEBUG
370
 
    fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
371
 
#endif
372
 
    delete this;
373
 
    return;
374
 
  }
375
 
 
376
 
  Boolean endOfMsg = False;
377
 
  unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
378
 
#ifdef DEBUG
379
 
  ptr[newBytesRead] = '\0';
380
 
  fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes:%s\n", this, newBytesRead, ptr);
381
 
#endif
382
 
 
383
 
  if (fClientOutputSocket != fClientInputSocket) {
384
 
    // We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
385
 
    // We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes):
386
 
    unsigned numBytesToDecode = fBase64RemainderCount + newBytesRead;
387
 
    unsigned newBase64RemainderCount = numBytesToDecode%4;
388
 
    numBytesToDecode -= newBase64RemainderCount;
389
 
    if (numBytesToDecode > 0) {
390
 
      ptr[newBytesRead] = '\0';
391
 
      unsigned decodedSize;
392
 
      unsigned char* decodedBytes = base64Decode((char*)(ptr-fBase64RemainderCount), decodedSize);
393
 
#ifdef DEBUG
394
 
      fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
395
 
      for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
396
 
      fprintf(stderr, "\n");
397
 
#endif
398
 
 
399
 
      // Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
400
 
      unsigned char* to = ptr-fBase64RemainderCount;
401
 
      for (unsigned i = 0; i < decodedSize; ++i) *to++ = decodedBytes[i];
 
370
  ++fRecursionCount;
 
371
 
 
372
  do {
 
373
    noteLiveness();
 
374
    
 
375
    if (newBytesRead <= 0 || (unsigned)newBytesRead >= fRequestBufferBytesLeft) {
 
376
      // Either the client socket has died, or the request was too big for us.
 
377
      // Terminate this connection:
 
378
#ifdef DEBUG
 
379
      fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes (of %d); terminating connection!\n", this, newBytesRead, fRequestBufferBytesLeft);
 
380
#endif
 
381
      fSessionIsActive = False;
 
382
      break;
 
383
    }
 
384
    
 
385
    Boolean endOfMsg = False;
 
386
    unsigned char* ptr = &fRequestBuffer[fRequestBytesAlreadySeen];
 
387
#ifdef DEBUG
 
388
    ptr[newBytesRead] = '\0';
 
389
    fprintf(stderr, "RTSPClientSession[%p]::handleRequestBytes() read %d new bytes:%s\n", this, newBytesRead, ptr);
 
390
#endif
 
391
    
 
392
    if (fClientOutputSocket != fClientInputSocket) {
 
393
      // We're doing RTSP-over-HTTP tunneling, and input commands are assumed to have been Base64-encoded.
 
394
      // We therefore Base64-decode as much of this new data as we can (i.e., up to a multiple of 4 bytes):
 
395
      unsigned numBytesToDecode = fBase64RemainderCount + newBytesRead;
 
396
      unsigned newBase64RemainderCount = numBytesToDecode%4;
 
397
      numBytesToDecode -= newBase64RemainderCount;
 
398
      if (numBytesToDecode > 0) {
 
399
        ptr[newBytesRead] = '\0';
 
400
        unsigned decodedSize;
 
401
        unsigned char* decodedBytes = base64Decode((char*)(ptr-fBase64RemainderCount), decodedSize);
 
402
#ifdef DEBUG
 
403
        fprintf(stderr, "Base64-decoded %d input bytes into %d new bytes:", numBytesToDecode, decodedSize);
 
404
        for (unsigned k = 0; k < decodedSize; ++k) fprintf(stderr, "%c", decodedBytes[k]);
 
405
        fprintf(stderr, "\n");
 
406
#endif
 
407
        
 
408
        // Copy the new decoded bytes in place of the old ones (we can do this because there are fewer decoded bytes than original):
 
409
        unsigned char* to = ptr-fBase64RemainderCount;
 
410
        for (unsigned i = 0; i < decodedSize; ++i) *to++ = decodedBytes[i];
 
411
        
 
412
        // Then copy any remaining (undecoded) bytes to the end:
 
413
        for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
 
414
        
 
415
        newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
 
416
        delete[] decodedBytes;
 
417
      }
 
418
      fBase64RemainderCount = newBase64RemainderCount;
 
419
      if (fBase64RemainderCount > 0) break; // because we know that we have more input bytes still to receive
 
420
    }
 
421
    
 
422
    // Look for the end of the message: <CR><LF><CR><LF>
 
423
    unsigned char *tmpPtr = ptr;
 
424
    if (fRequestBytesAlreadySeen > 0) --tmpPtr;
 
425
    // in case the last read ended with a <CR>
 
426
    while (tmpPtr < &ptr[newBytesRead-1]) {
 
427
      if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
 
428
        if (tmpPtr - fLastCRLF == 2) { // This is it:
 
429
          endOfMsg = True;
 
430
          break;
 
431
        }
 
432
        fLastCRLF = tmpPtr;
 
433
      }
 
434
      ++tmpPtr;
 
435
    }
 
436
    
 
437
    fRequestBufferBytesLeft -= newBytesRead;
 
438
    fRequestBytesAlreadySeen += newBytesRead;
 
439
    
 
440
    if (!endOfMsg) break; // subsequent reads will be needed to complete the request
 
441
    
 
442
    // Parse the request string into command name and 'CSeq', then handle the command:
 
443
    fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
 
444
    char cmdName[RTSP_PARAM_STRING_MAX];
 
445
    char urlPreSuffix[RTSP_PARAM_STRING_MAX];
 
446
    char urlSuffix[RTSP_PARAM_STRING_MAX];
 
447
    char cseq[RTSP_PARAM_STRING_MAX];
 
448
    unsigned contentLength;
 
449
    if (parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen,
 
450
                               cmdName, sizeof cmdName,
 
451
                               urlPreSuffix, sizeof urlPreSuffix,
 
452
                               urlSuffix, sizeof urlSuffix,
 
453
                               cseq, sizeof cseq,
 
454
                               contentLength)) {
 
455
#ifdef DEBUG
 
456
      fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength);
 
457
#endif
 
458
      // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
 
459
      if (ptr + newBytesRead < tmpPtr + 2 + contentLength) break; // we still need more data; subsequent reads will give it to us 
402
460
      
403
 
      // Then copy any remaining (undecoded) bytes to the end:
404
 
      for (unsigned j = 0; j < newBase64RemainderCount; ++j) *to++ = (ptr-fBase64RemainderCount+numBytesToDecode)[j];
405
 
 
406
 
      newBytesRead = decodedSize + newBase64RemainderCount; // adjust to allow for the size of the new decoded data (+ remainder)
407
 
      delete[] decodedBytes;
408
 
    }
409
 
    fBase64RemainderCount = newBase64RemainderCount;
410
 
    if (fBase64RemainderCount > 0) return; // because we know that we have more input bytes still to receive
411
 
  }
412
 
 
413
 
  // Look for the end of the message: <CR><LF><CR><LF>
414
 
  unsigned char *tmpPtr = ptr;
415
 
  if (fRequestBytesAlreadySeen > 0) --tmpPtr;
416
 
      // in case the last read ended with a <CR>
417
 
  while (tmpPtr < &ptr[newBytesRead-1]) {
418
 
    if (*tmpPtr == '\r' && *(tmpPtr+1) == '\n') {
419
 
      if (tmpPtr - fLastCRLF == 2) { // This is it:
420
 
        endOfMsg = True;
421
 
        break;
422
 
      }
423
 
      fLastCRLF = tmpPtr;
424
 
    }
425
 
    ++tmpPtr;
426
 
  }
427
 
 
428
 
  fRequestBufferBytesLeft -= newBytesRead;
429
 
  fRequestBytesAlreadySeen += newBytesRead;
430
 
 
431
 
  if (!endOfMsg) return; // subsequent reads will be needed to complete the request
432
 
 
433
 
  // Parse the request string into command name and 'CSeq', then handle the command:
434
 
  fRequestBuffer[fRequestBytesAlreadySeen] = '\0';
435
 
  char cmdName[RTSP_PARAM_STRING_MAX];
436
 
  char urlPreSuffix[RTSP_PARAM_STRING_MAX];
437
 
  char urlSuffix[RTSP_PARAM_STRING_MAX];
438
 
  char cseq[RTSP_PARAM_STRING_MAX];
439
 
  unsigned contentLength;
440
 
  if (parseRTSPRequestString((char*)fRequestBuffer, fRequestBytesAlreadySeen,
441
 
                             cmdName, sizeof cmdName,
442
 
                             urlPreSuffix, sizeof urlPreSuffix,
443
 
                             urlSuffix, sizeof urlSuffix,
444
 
                             cseq, sizeof cseq,
445
 
                             contentLength)) {
446
 
#ifdef DEBUG
447
 
    fprintf(stderr, "parseRTSPRequestString() succeeded, returning cmdName \"%s\", urlPreSuffix \"%s\", urlSuffix \"%s\", CSeq \"%s\", Content-Length %u\n", cmdName, urlPreSuffix, urlSuffix, cseq, contentLength);
448
 
#endif
449
 
    // If there was a "Content-Length:" header, then make sure we've received all of the data that it specified:
450
 
    if (ptr + newBytesRead < tmpPtr + 2 + contentLength) return; // we still need more data; subsequent reads will give it to us 
451
 
 
452
 
    if (strcmp(cmdName, "OPTIONS") == 0) {
453
 
      handleCmd_OPTIONS(cseq);
454
 
    } else if (strcmp(cmdName, "DESCRIBE") == 0) {
455
 
      handleCmd_DESCRIBE(cseq, urlSuffix, (char const*)fRequestBuffer);
456
 
    } else if (strcmp(cmdName, "SETUP") == 0) {
457
 
      handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
458
 
    } else if (strcmp(cmdName, "TEARDOWN") == 0
459
 
               || strcmp(cmdName, "PLAY") == 0
460
 
               || strcmp(cmdName, "PAUSE") == 0
461
 
               || strcmp(cmdName, "GET_PARAMETER") == 0
462
 
               || strcmp(cmdName, "SET_PARAMETER") == 0) {
463
 
      handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq,
 
461
      if (strcmp(cmdName, "OPTIONS") == 0) {
 
462
        handleCmd_OPTIONS(cseq);
 
463
      } else if (strcmp(cmdName, "DESCRIBE") == 0) {
 
464
        handleCmd_DESCRIBE(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
 
465
      } else if (strcmp(cmdName, "SETUP") == 0) {
 
466
        handleCmd_SETUP(cseq, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
 
467
      } else if (strcmp(cmdName, "TEARDOWN") == 0
 
468
                 || strcmp(cmdName, "PLAY") == 0
 
469
                 || strcmp(cmdName, "PAUSE") == 0
 
470
                 || strcmp(cmdName, "GET_PARAMETER") == 0
 
471
                 || strcmp(cmdName, "SET_PARAMETER") == 0) {
 
472
        handleCmd_withinSession(cmdName, urlPreSuffix, urlSuffix, cseq, (char const*)fRequestBuffer);
 
473
      } else {
 
474
        handleCmd_notSupported(cseq);
 
475
      }
 
476
    } else {
 
477
#ifdef DEBUG
 
478
      fprintf(stderr, "parseRTSPRequestString() failed\n");
 
479
#endif
 
480
      // The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
 
481
      char sessionCookie[RTSP_PARAM_STRING_MAX];
 
482
      char acceptStr[RTSP_PARAM_STRING_MAX];
 
483
      if (parseHTTPRequestString(cmdName, sizeof cmdName,
 
484
                                 urlSuffix, sizeof urlPreSuffix,
 
485
                                 sessionCookie, sizeof sessionCookie,
 
486
                                 acceptStr, sizeof acceptStr)) {
 
487
#ifdef DEBUG
 
488
        fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
 
489
#endif
 
490
        // Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
 
491
        Boolean isValidHTTPCmd = True;
 
492
        if (sessionCookie[0] == '\0') {
 
493
          // There was no "x-sessionCookie:" header.  If there was an "Accept: application/x-rtsp-tunnelled" header,
 
494
          // then this is a bad tunneling request.  Otherwise, assume that it's an attempt to access the stream via HTTP.
 
495
          if (strcmp(acceptStr, "application/x-rtsp-tunnelled") == 0) {
 
496
            isValidHTTPCmd = False;
 
497
          } else {
 
498
            handleHTTPCmd_StreamingGET(urlSuffix, (char const*)fRequestBuffer);
 
499
          }
 
500
        } else if (strcmp(cmdName, "GET") == 0) {
 
501
          handleHTTPCmd_TunnelingGET(sessionCookie);
 
502
        } else if (strcmp(cmdName, "POST") == 0) {
 
503
          // We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
 
504
          // Check for this, and handle it if it exists:
 
505
          unsigned char const* extraData = fLastCRLF+4;
 
506
          unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] - extraData;
 
507
          if (handleHTTPCmd_TunnelingPOST(sessionCookie, extraData, extraDataSize)) {
 
508
            // We don't respond to the "POST" command, and we go away:
 
509
            fSessionIsActive = False;
 
510
            break;
 
511
          }
 
512
        } else {
 
513
          isValidHTTPCmd = False;
 
514
        }
 
515
        if (!isValidHTTPCmd) {
 
516
          handleHTTPCmd_notSupported();
 
517
        }
 
518
      } else {
 
519
#ifdef DEBUG
 
520
        fprintf(stderr, "parseHTTPRequestString() failed!\n");
 
521
#endif
 
522
        handleCmd_bad(cseq);
 
523
      }
 
524
    }
 
525
    
 
526
#ifdef DEBUG
 
527
    fprintf(stderr, "sending response: %s", fResponseBuffer);
 
528
#endif
 
529
    send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
 
530
    
 
531
    if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) {
 
532
      // The client has asked for streaming to commence now, rather than after a
 
533
      // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
 
534
      handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq,
464
535
                              (char const*)fRequestBuffer);
465
 
    } else {
466
 
      handleCmd_notSupported(cseq);
467
 
    }
468
 
  } else {
469
 
#ifdef DEBUG
470
 
    fprintf(stderr, "parseRTSPRequestString() failed\n");
471
 
#endif
472
 
    // The request was not (valid) RTSP, but check for a special case: HTTP commands (for setting up RTSP-over-HTTP tunneling):
473
 
    char sessionCookie[RTSP_PARAM_STRING_MAX];
474
 
    char acceptStr[RTSP_PARAM_STRING_MAX];
475
 
    if (parseHTTPRequestString(cmdName, sizeof cmdName,
476
 
                               urlSuffix, sizeof urlPreSuffix,
477
 
                               sessionCookie, sizeof sessionCookie,
478
 
                               acceptStr, sizeof acceptStr)) {
479
 
#ifdef DEBUG
480
 
      fprintf(stderr, "parseHTTPRequestString() succeeded, returning cmdName \"%s\", urlSuffix \"%s\", sessionCookie \"%s\", acceptStr \"%s\"\n", cmdName, urlSuffix, sessionCookie, acceptStr);
481
 
#endif
482
 
      // Check that the HTTP command is valid for RTSP-over-HTTP tunneling: There must be a 'session cookie'.
483
 
      Boolean isValidHTTPCmd = True;
484
 
      if (sessionCookie[0] == '\0') {
485
 
        // There was no "x-sessionCookie:" header.  If there was an "Accept: application/x-rtsp-tunnelled" header,
486
 
        // then this is a bad tunneling request.  Otherwise, assume that it's an attempt to access the stream via HTTP.
487
 
        if (strcmp(acceptStr, "application/x-rtsp-tunnelled") == 0) {
488
 
          isValidHTTPCmd = False;
489
 
        } else {
490
 
          handleHTTPCmd_StreamingGET(urlSuffix, (char const*)fRequestBuffer);
491
 
        }
492
 
      } else if (strcmp(cmdName, "GET") == 0) {
493
 
        handleHTTPCmd_TunnelingGET(sessionCookie);
494
 
      } else if (strcmp(cmdName, "POST") == 0) {
495
 
        // We might have received additional data following the HTTP "POST" command - i.e., the first Base64-encoded RTSP command.
496
 
        // Check for this, and handle it if it exists:
497
 
        unsigned char const* extraData = fLastCRLF+4;
498
 
        unsigned extraDataSize = &fRequestBuffer[fRequestBytesAlreadySeen] - extraData;
499
 
        if (handleHTTPCmd_TunnelingPOST(sessionCookie, extraData, extraDataSize)) {
500
 
          // We don't respond to the "POST" command, and we go away:
501
 
          delete this;
502
 
          return;
503
 
        }
504
 
      } else {
505
 
        isValidHTTPCmd = False;
506
 
      }
507
 
      if (!isValidHTTPCmd) {
508
 
        handleHTTPCmd_notSupported();
509
 
      }
510
 
    } else {
511
 
#ifdef DEBUG
512
 
    fprintf(stderr, "parseHTTPRequestString() failed!\n");
513
 
#endif
514
 
      handleCmd_bad(cseq);
515
 
    }
516
 
  }
517
 
 
518
 
#ifdef DEBUG
519
 
  fprintf(stderr, "sending response: %s", fResponseBuffer);
520
 
#endif
521
 
  send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);
522
 
 
523
 
  if (strcmp(cmdName, "SETUP") == 0 && fStreamAfterSETUP) {
524
 
    // The client has asked for streaming to commence now, rather than after a
525
 
    // subsequent "PLAY" command.  So, simulate the effect of a "PLAY" command:
526
 
    handleCmd_withinSession("PLAY", urlPreSuffix, urlSuffix, cseq,
527
 
                            (char const*)fRequestBuffer);
528
 
  }
529
 
 
530
 
  resetRequestBuffer(); // to prepare for any subsequent request
531
 
  if (!fSessionIsActive) delete this;
 
536
    }
 
537
    
 
538
    resetRequestBuffer(); // to prepare for any subsequent request
 
539
  } while (0);
 
540
 
 
541
  --fRecursionCount;
 
542
  if (!fSessionIsActive) {
 
543
    if (fRecursionCount > 0) closeSockets(); else delete this;
 
544
    // Note: The "fRecursionCount" test is for a pathological situation where we got called recursively while handling a command.
 
545
    // In such a case we don't want to actually delete ourself until we leave the outermost call.
 
546
  }
532
547
}
533
548
 
534
549
// Handler routines for specific RTSP commands:
570
585
}
571
586
 
572
587
void RTSPServer::RTSPClientSession
573
 
::handleCmd_DESCRIBE(char const* cseq, char const* urlSuffix,
 
588
::handleCmd_DESCRIBE(char const* cseq,
 
589
                     char const* urlPreSuffix, char const* urlSuffix,
574
590
                     char const* fullRequestStr) {
575
591
  char* sdpDescription = NULL;
576
592
  char* rtspURL = NULL;
577
593
  do {
578
 
      if (!authenticationOK("DESCRIBE", cseq, urlSuffix, fullRequestStr))
579
 
          break;
580
 
 
 
594
    char urlTotalSuffix[RTSP_PARAM_STRING_MAX];
 
595
    if (strlen(urlPreSuffix) + strlen(urlSuffix) + 2 > sizeof urlTotalSuffix) {
 
596
      handleCmd_bad(cseq);
 
597
      break;
 
598
    }
 
599
    urlTotalSuffix[0] = '\0';
 
600
    if (urlPreSuffix[0] != '\0') {
 
601
      strcat(urlTotalSuffix, urlPreSuffix);
 
602
      strcat(urlTotalSuffix, "/");
 
603
    }
 
604
    strcat(urlTotalSuffix, urlSuffix);
 
605
      
 
606
    if (!authenticationOK("DESCRIBE", cseq, urlTotalSuffix, fullRequestStr)) break;
 
607
    
581
608
    // We should really check that the request contains an "Accept:" #####
582
609
    // for "application/sdp", because that's what we're sending back #####
583
 
 
584
 
    // Begin by looking up the "ServerMediaSession" object for the
585
 
    // specified "urlSuffix":
586
 
    ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlSuffix);
 
610
    
 
611
    // Begin by looking up the "ServerMediaSession" object for the specified "urlTotalSuffix":
 
612
    ServerMediaSession* session = fOurServer.lookupServerMediaSession(urlTotalSuffix);
587
613
    if (session == NULL) {
588
614
      handleCmd_notFound(cseq);
589
615
      break;
590
616
    }
591
 
 
 
617
    
592
618
    // Then, assemble a SDP description for this session:
593
619
    sdpDescription = session->generateSDPDescription();
594
620
    if (sdpDescription == NULL) {
600
626
               "%s\r\n",
601
627
               cseq,
602
628
               dateHeader());
603
 
     break;
 
629
      break;
604
630
    }
605
631
    unsigned sdpDescriptionSize = strlen(sdpDescription);
606
 
 
 
632
    
607
633
    // Also, generate our RTSP URL, for the "Content-Base:" header
608
634
    // (which is necessary to ensure that the correct URL gets used in
609
635
    // subsequent "SETUP" requests).
610
636
    rtspURL = fOurServer.rtspURL(session, fClientInputSocket);
611
 
 
 
637
    
612
638
    snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
613
639
             "RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
614
640
             "%s"
726
752
    // Set up this session's state.
727
753
 
728
754
    // Look up the "ServerMediaSession" object for the specified stream:
729
 
    if (streamName[0] != '\0' ||
730
 
        fOurServer.lookupServerMediaSession("") != NULL) { // normal case
 
755
    if (streamName[0] != '\0' || fOurServer.lookupServerMediaSession("") != NULL) { // normal case
731
756
    } else { // weird case: there was no track id in the URL
732
757
      streamName = urlSuffix;
733
758
      trackId = NULL;