/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2024 Live Networks, Inc.  All rights reserved.
// A RTSP server
// Implementation of functionality related to the "REGISTER" and "DEREGISTER" commands

#include "RTSPServer.hh"
#include "RTSPCommon.hh"
#include "RTSPRegisterSender.hh"
#include "ProxyServerMediaSession.hh"
#include "GroupsockHelper.hh"

////////// Implementation of "RTSPServer::registerStream()": //////////

static void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward

// A class that represents the state of a "REGISTER" request in progress:
class RegisterRequestRecord: public RTSPRegisterSender {
public:
  RegisterRequestRecord(RTSPServer& ourServer, unsigned requestId,
			char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const* rtspURLToRegister,
			RTSPServer::responseHandlerForREGISTER* responseHandler, Authenticator* authenticator,
			Boolean requestStreamingViaTCP, char const* proxyURLSuffix)
    : RTSPRegisterSender(ourServer.envir(), remoteClientNameOrAddress, remoteClientPortNum, rtspURLToRegister,
			 rtspRegisterResponseHandler, authenticator,
			 requestStreamingViaTCP, proxyURLSuffix, True/*reuseConnection*/,
#ifdef DEBUG
			 1/*verbosityLevel*/,
#else
			 0/*verbosityLevel*/,
#endif
			 NULL),
      fOurServer(ourServer), fRequestId(requestId), fResponseHandler(responseHandler) {
    // Add ourself to our server's 'pending REGISTER or DEREGISTER requests' table:
    ourServer.fPendingRegisterOrDeregisterRequests->Add((char const*)this, this);
  }

  virtual ~RegisterRequestRecord() {
    // Remove ourself from the server's 'pending REGISTER or DEREGISTER requests' hash table before we go:
    fOurServer.fPendingRegisterOrDeregisterRequests->Remove((char const*)this);
  }

  void handleResponse(int resultCode, char* resultString) {
    if (resultCode == 0) {
      // The "REGISTER" request succeeded, so use the still-open RTSP socket to await incoming commands from the remote endpoint:
      int sock;
      struct sockaddr_storage remoteAddress;

      grabConnection(sock, remoteAddress);
      if (sock >= 0) {
	increaseSendBufferTo(envir(), sock, 50*1024); // in anticipation of streaming over it
	(void)fOurServer.createNewClientConnection(sock, remoteAddress);
      }
    }

    if (fResponseHandler != NULL) {
      // Call our (REGISTER-specific) response handler now:
      (*fResponseHandler)(&fOurServer, fRequestId, resultCode, resultString);
    } else {
      // We need to delete[] "resultString" before we leave:
      delete[] resultString;
    }

    // We're completely done with the REGISTER command now, so delete ourself now:
    Medium::close(this);
  }

private:
  RTSPServer& fOurServer;
  unsigned fRequestId;
  RTSPServer::responseHandlerForREGISTER* fResponseHandler;
};

static void rtspRegisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) {
  RegisterRequestRecord* registerRequestRecord = (RegisterRequestRecord*)rtspClient;

  registerRequestRecord->handleResponse(resultCode, resultString);
}

unsigned RTSPServer::registerStream(ServerMediaSession* serverMediaSession,
				    char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum,
				    responseHandlerForREGISTER* responseHandler,
				    char const* username, char const* password,
				    Boolean receiveOurStreamViaTCP, char const* proxyURLSuffix) {
  // Create a new "RegisterRequestRecord" that will send the "REGISTER" command.
  // (This object will automatically get deleted after we get a response to the "REGISTER" command, or if we're deleted.)
  Authenticator* authenticator = NULL;
  if (username != NULL) {
    if (password == NULL) password = "";
    authenticator = new Authenticator(username, password);
  }
  unsigned requestId = ++fRegisterOrDeregisterRequestCounter;
  char const* url = rtspURL(serverMediaSession);
  new RegisterRequestRecord(*this, requestId,
			    remoteClientNameOrAddress, remoteClientPortNum, url,
			    responseHandler, authenticator,
			    receiveOurStreamViaTCP, proxyURLSuffix);
  
  delete[] (char*)url; // we can do this here because it was copied to the "RegisterRequestRecord" 
  delete authenticator; // ditto
  return requestId;
}

////////// Implementation of "RTSPServer::deregisterStream()": //////////

static void rtspDeregisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString); // forward

// A class that represents the state of a "DEREGISTER" request in progress:
class DeregisterRequestRecord: public RTSPDeregisterSender {
public:
  DeregisterRequestRecord(RTSPServer& ourServer, unsigned requestId,
			  char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum, char const* rtspURLToDeregister,
			  RTSPServer::responseHandlerForDEREGISTER* responseHandler, Authenticator* authenticator,
			  char const* proxyURLSuffix)
    : RTSPDeregisterSender(ourServer.envir(), remoteClientNameOrAddress, remoteClientPortNum, rtspURLToDeregister,
			 rtspDeregisterResponseHandler, authenticator, proxyURLSuffix,
#ifdef DEBUG
			 1/*verbosityLevel*/,
#else
			 0/*verbosityLevel*/,
#endif
			 NULL),
      fOurServer(ourServer), fRequestId(requestId), fResponseHandler(responseHandler) {
    // Add ourself to our server's 'pending REGISTER or DEREGISTER requests' table:
    ourServer.fPendingRegisterOrDeregisterRequests->Add((char const*)this, this);
  }

  virtual ~DeregisterRequestRecord() {
    // Remove ourself from the server's 'pending REGISTER or DEREGISTER requests' hash table before we go:
    fOurServer.fPendingRegisterOrDeregisterRequests->Remove((char const*)this);
  }

  void handleResponse(int resultCode, char* resultString) {
    if (fResponseHandler != NULL) {
      // Call our (DEREGISTER-specific) response handler now:
      (*fResponseHandler)(&fOurServer, fRequestId, resultCode, resultString);
    } else {
      // We need to delete[] "resultString" before we leave:
      delete[] resultString;
    }

    // We're completely done with the DEREGISTER command now, so delete ourself now:
    Medium::close(this);
  }

private:
  RTSPServer& fOurServer;
  unsigned fRequestId;
  RTSPServer::responseHandlerForDEREGISTER* fResponseHandler;
};

static void rtspDeregisterResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) {
  DeregisterRequestRecord* deregisterRequestRecord = (DeregisterRequestRecord*)rtspClient;

  deregisterRequestRecord->handleResponse(resultCode, resultString);
}

unsigned RTSPServer::deregisterStream(ServerMediaSession* serverMediaSession,
				      char const* remoteClientNameOrAddress, portNumBits remoteClientPortNum,
				      responseHandlerForDEREGISTER* responseHandler,
				      char const* username, char const* password,
				      char const* proxyURLSuffix) {
  // Create a new "DeregisterRequestRecord" that will send the "DEREGISTER" command.
  // (This object will automatically get deleted after we get a response to the "DEREGISTER" command, or if we're deleted.)
  Authenticator* authenticator = NULL;
  if (username != NULL) {
    if (password == NULL) password = "";
    authenticator = new Authenticator(username, password);
  }
  unsigned requestId = ++fRegisterOrDeregisterRequestCounter;
  char const* url = rtspURL(serverMediaSession);
  new DeregisterRequestRecord(*this, requestId,
			      remoteClientNameOrAddress, remoteClientPortNum, url,
			      responseHandler, authenticator,
			      proxyURLSuffix);
  
  delete[] (char*)url; // we can do this here because it was copied to the "DeregisterRequestRecord"
  delete authenticator; // ditto
  return requestId;
}

Boolean RTSPServer::weImplementREGISTER(char const* /*cmd*//*"REGISTER" or "DEREGISTER"*/,
					char const* /*proxyURLSuffix*/, char*& responseStr) {
  // By default, servers do not implement our custom "REGISTER"/"DEREGISTER" commands:
  responseStr = NULL;
  return False;
}

void RTSPServer::implementCmd_REGISTER(char const* /*cmd*//*"REGISTER" or "DEREGISTER"*/,
				       char const* /*url*/, char const* /*urlSuffix*/, int /*socketToRemoteServer*/,
				       Boolean /*deliverViaTCP*/, char const* /*proxyURLSuffix*/) {
  // By default, this function is a 'noop'
}

// Special mechanism for handling our custom "REGISTER" command:

RTSPServer::RTSPClientConnection::ParamsForREGISTER
::ParamsForREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
		    RTSPServer::RTSPClientConnection* ourConnection, char const* url, char const* urlSuffix,
		    Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix)
  : fCmd(strDup(cmd)), fOurConnection(ourConnection), fURL(strDup(url)), fURLSuffix(strDup(urlSuffix)),
    fReuseConnection(reuseConnection), fDeliverViaTCP(deliverViaTCP), fProxyURLSuffix(strDup(proxyURLSuffix)) {
}

RTSPServer::RTSPClientConnection::ParamsForREGISTER::~ParamsForREGISTER() {
  delete[] (char*)fCmd; delete[] fURL; delete[] fURLSuffix; delete[] fProxyURLSuffix;
}

#define DELAY_USECS_AFTER_REGISTER_RESPONSE 100000 /*100ms*/

void RTSPServer
::RTSPClientConnection::handleCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
					   char const* url, char const* urlSuffix, char const* fullRequestStr,
					   Boolean reuseConnection, Boolean deliverViaTCP, char const* proxyURLSuffix) {
  char* responseStr;
  if (fOurRTSPServer.weImplementREGISTER(cmd, proxyURLSuffix, responseStr)) {
    // The "REGISTER"/"DEREGISTER" command - if we implement it - may require access control:
    if (!authenticationOK(cmd, urlSuffix, fullRequestStr)) return;
    
    // We implement the "REGISTER"/"DEREGISTER" command by first replying to it, then actually
    // handling it (in a separate event-loop task, that will get called after the reply has
    // been done).
    // Hack: If we're going to reuse the command's connection for subsequent RTSP commands, then we
    // delay the actual handling of the command slightly, to make it less likely that the first
    // subsequent RTSP command (e.g., "DESCRIBE") will end up in the client's reponse buffer before
    // the socket (at the far end) gets reused for RTSP command handling.
    setRTSPResponse(responseStr == NULL ? "200 OK" : responseStr);
    delete[] responseStr;
    
    ParamsForREGISTER* registerParams = new ParamsForREGISTER(cmd, this, url, urlSuffix, reuseConnection, deliverViaTCP, proxyURLSuffix);
    envir().taskScheduler().scheduleDelayedTask(reuseConnection ? DELAY_USECS_AFTER_REGISTER_RESPONSE : 0,
						(TaskFunc*)continueHandlingREGISTER, registerParams);
    ++fScheduledDelayedTask;
  } else if (responseStr != NULL) {
    setRTSPResponse(responseStr);
    delete[] responseStr;
  } else {
    handleCmd_notSupported();
  }
}

// A special version of "parseTransportHeader()", used just for parsing the "Transport:" header in an incoming "REGISTER" command:
void parseTransportHeaderForREGISTER(char const* buf,
				     Boolean &reuseConnection,
				     Boolean& deliverViaTCP,
				     char*& proxyURLSuffix) {
  // Initialize the result parameters to default values:
  reuseConnection = False;
  deliverViaTCP = False;
  proxyURLSuffix = NULL;
  
  // First, find "Transport:"
  while (1) {
    if (*buf == '\0') return; // not found
    if (*buf == '\r' && *(buf+1) == '\n' && *(buf+2) == '\r') return; // end of the headers => not found
    if (_strncasecmp(buf, "Transport:", 10) == 0) break;
    ++buf;
  }
  
  // Then, run through each of the fields, looking for ones we handle:
  char const* fields = buf + 10;
  while (*fields == ' ') ++fields;
  char* field = strDupSize(fields);
  while (sscanf(fields, "%[^;\r\n]", field) == 1) {
    if (strcmp(field, "reuse_connection") == 0) {
      reuseConnection = True;
    } else if (_strncasecmp(field, "preferred_delivery_protocol=udp", 31) == 0) {
      deliverViaTCP = False;
    } else if (_strncasecmp(field, "preferred_delivery_protocol=interleaved", 39) == 0) {
      deliverViaTCP = True;
    } else if (_strncasecmp(field, "proxy_url_suffix=", 17) == 0) {
      delete[] proxyURLSuffix;
      proxyURLSuffix = strDup(field+17);
    }
    
    fields += strlen(field);
    while (*fields == ';' || *fields == ' ' || *fields == '\t') ++fields; // skip over separating ';' chars or whitespace
    if (*fields == '\0' || *fields == '\r' || *fields == '\n') break;
  }
  delete[] field;
}

void RTSPServer::RTSPClientConnection::continueHandlingREGISTER(ParamsForREGISTER* params) {
  params->fOurConnection->continueHandlingREGISTER1(params);
}

void RTSPServer::RTSPClientConnection::continueHandlingREGISTER1(ParamsForREGISTER* params) {
  --fScheduledDelayedTask;

  // Reuse our socket if requested:
  int socketNumToBackEndServer = params->fReuseConnection ? fClientOutputSocket : -1;

  RTSPServer* ourServer = &fOurRTSPServer; // copy the pointer now, in case we "delete this" below
  
  if (socketNumToBackEndServer >= 0) {
    // Because our socket will no longer be used by the server to handle incoming requests, we can now delete this
    // "RTSPClientConnection" object.  We do this now, in case the "implementCmd_REGISTER()" call below would also end up
    // deleting this.
    fClientInputSocket = fClientOutputSocket = -1; // so the socket doesn't get closed when we get deleted
    delete this;
  } else if (!fIsActive && fRecursionCount <= 0 && fScheduledDelayedTask <= 0) {
    delete this;
  }
  
  ourServer->implementCmd_REGISTER(params->fCmd,
				   params->fURL, params->fURLSuffix, socketNumToBackEndServer,
				   params->fDeliverViaTCP, params->fProxyURLSuffix);
  delete params;
}


///////// RTSPServerWithREGISTERProxying implementation /////////

RTSPServerWithREGISTERProxying* RTSPServerWithREGISTERProxying
::createNew(UsageEnvironment& env, Port ourPort,
	    UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER,
	    unsigned reclamationSeconds,
	    Boolean streamRTPOverTCP, int verbosityLevelForProxying,
	    char const* backEndUsername, char const* backEndPassword) {
  int ourSocketIPv4 = setUpOurSocket(env, ourPort, AF_INET);
  int ourSocketIPv6 = setUpOurSocket(env, ourPort, AF_INET6);
  if (ourSocketIPv4 < 0 && ourSocketIPv6 < 0) return NULL;
  
  return new RTSPServerWithREGISTERProxying(env, ourSocketIPv4, ourSocketIPv6, ourPort,
					    authDatabase, authDatabaseForREGISTER,
					    reclamationSeconds,
					    streamRTPOverTCP, verbosityLevelForProxying,
					    backEndUsername, backEndPassword);
}

RTSPServerWithREGISTERProxying
::RTSPServerWithREGISTERProxying(UsageEnvironment& env, int ourSocketIPv4, int ourSocketIPv6, Port ourPort,
				 UserAuthenticationDatabase* authDatabase, UserAuthenticationDatabase* authDatabaseForREGISTER,
				 unsigned reclamationSeconds,
				 Boolean streamRTPOverTCP, int verbosityLevelForProxying,
				 char const* backEndUsername, char const* backEndPassword)
  : RTSPServer(env, ourSocketIPv4, ourSocketIPv6, ourPort, authDatabase, reclamationSeconds),
    fStreamRTPOverTCP(streamRTPOverTCP), fVerbosityLevelForProxying(verbosityLevelForProxying),
    fRegisteredProxyCounter(0), fAllowedCommandNames(NULL), fAuthDBForREGISTER(authDatabaseForREGISTER),
    fBackEndUsername(strDup(backEndUsername)), fBackEndPassword(strDup(backEndPassword)) {
}

RTSPServerWithREGISTERProxying::~RTSPServerWithREGISTERProxying() {
  delete[] fAllowedCommandNames;
  delete[] fBackEndUsername; delete[] fBackEndPassword;
}

char const* RTSPServerWithREGISTERProxying::allowedCommandNames() {
  if (fAllowedCommandNames == NULL) {
    char const* baseAllowedCommandNames = RTSPServer::allowedCommandNames();
    char const* newAllowedCommandName = ", REGISTER, DEREGISTER";
    fAllowedCommandNames = new char[strlen(baseAllowedCommandNames) + strlen(newAllowedCommandName) + 1/* for '\0' */];
    sprintf(fAllowedCommandNames, "%s%s", baseAllowedCommandNames, newAllowedCommandName);
  }
  return fAllowedCommandNames;
}

Boolean RTSPServerWithREGISTERProxying
::weImplementREGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
		      char const* proxyURLSuffix, char*& responseStr) {
  // First, check whether we have already proxied a stream as "proxyURLSuffix":
  if (proxyURLSuffix != NULL) {
    ServerMediaSession* sms = getServerMediaSession(proxyURLSuffix);
    if ((strcmp(cmd, "REGISTER") == 0 && sms != NULL) ||
	(strcmp(cmd, "DEREGISTER") == 0 && sms == NULL)) {
      responseStr = strDup("451 Invalid parameter");
      return False;
    }
  }

  // Otherwise, we will implement it:
  responseStr = NULL;
  return True;
}

void RTSPServerWithREGISTERProxying
::implementCmd_REGISTER(char const* cmd/*"REGISTER" or "DEREGISTER"*/,
			char const* url, char const* /*urlSuffix*/, int socketToRemoteServer,
			Boolean deliverViaTCP, char const* proxyURLSuffix) {
  // Continue setting up proxying for the specified URL.
  // By default:
  //    - We use "registeredProxyStream-N" as the (front-end) stream name (ignoring the back-end stream's 'urlSuffix'),
  //      unless "proxyURLSuffix" is non-NULL (in which case we use that)
  //    - There is no 'username' and 'password' for the back-end stream.  (Thus, access-controlled back-end streams will fail.)
  //    - If "fStreamRTPOverTCP" is True, then we request delivery over TCP, regardless of the value of "deliverViaTCP".
  //      (Otherwise, if "fStreamRTPOverTCP" is False, we use the value of "deliverViaTCP" to decide this.)
  // To change this default behavior, you will need to subclass "RTSPServerWithREGISTERProxying", and reimplement this function.
  
  char const* proxyStreamName;
  char proxyStreamNameBuf[100];
  if (proxyURLSuffix == NULL) {
    sprintf(proxyStreamNameBuf, "registeredProxyStream-%u", ++fRegisteredProxyCounter);
    proxyStreamName = proxyStreamNameBuf;
  } else {
    proxyStreamName = proxyURLSuffix;
  }

  if (strcmp(cmd, "REGISTER") == 0) {
    if (fStreamRTPOverTCP) deliverViaTCP = True;
    portNumBits tunnelOverHTTPPortNum = deliverViaTCP ? (portNumBits)(~0) : 0;
        // We don't support streaming from the back-end via RTSP/RTP/RTCP-over-HTTP; only via RTP/RTCP-over-TCP or RTP/RTCP-over-UDP

    ServerMediaSession* sms
      = ProxyServerMediaSession::createNew(envir(), this, url, proxyStreamName,
					   fBackEndUsername, fBackEndPassword,
					   tunnelOverHTTPPortNum, fVerbosityLevelForProxying, socketToRemoteServer);
    addServerMediaSession(sms);
  
    // (Regardless of the verbosity level) announce the fact that we're proxying this new stream, and the URL to use to access it:
    char* proxyStreamURL = rtspURL(sms);
    envir() << "Proxying the registered back-end stream \"" << url << "\".\n";
    envir() << "\tPlay this stream using the URL: " << proxyStreamURL << "\n";
    delete[] proxyStreamURL;
  } else { // "DEREGISTER"
    //    deleteServerMediaSession(lookupServerMediaSession(proxyStreamName));
    lookupServerMediaSession(proxyStreamName, &GenericMediaServer::deleteServerMediaSession);
  }
}

UserAuthenticationDatabase* RTSPServerWithREGISTERProxying::getAuthenticationDatabaseForCommand(char const* cmdName) {
  if (strcmp(cmdName, "REGISTER") == 0) return fAuthDBForREGISTER;

  return RTSPServer::getAuthenticationDatabaseForCommand(cmdName);
}
