📄 servhs.cpp.svn-base
字号:
// ------------------------------------------------// File : servhs.cpp// Date: 4-apr-2002// Author: giles// Desc: // Servent handshaking, TODO: should be in its own class//// (c) 2002 peercast.org// ------------------------------------------------// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.// This program 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 General Public License for more details.// ------------------------------------------------#include <stdlib.h>#include "servent.h"#include "servmgr.h"#include "html.h"#include "stats.h"#include "peercast.h"// -----------------------------------static void termArgs(char *str){ if (str) { int slen = strlen(str); for(int i=0; i<slen; i++) if (str[i]=='&') str[i] = 0; }}// -----------------------------------char *nextCGIarg(char *cp, char *cmd, char *arg){ if (!*cp) return NULL; // fetch command while (*cp) { char c = *cp++; if (c == '=') break; else *cmd++ = c; } *cmd = 0; // fetch arg while (*cp) { char c = *cp++; if (c == '&') break; else *arg++ = c; } *arg = 0; return cp;}// -----------------------------------bool getCGIargBOOL(char *a){ return (strcmp(a,"1")==0);}// -----------------------------------int getCGIargINT(char *a){ return atoi(a);}// -----------------------------------void Servent::handshakeHTTP(HTTP &http, bool isHTTP){ char *in = http.cmdLine; if (http.isRequest("GET /")) { char *fn = in+4; char *pt = strstr(fn,HTTP_PROTO1); if (pt) pt[-1] = 0; if (strncmp(fn,"/admin?",7)==0) { if (!isAllowed(ALLOW_HTML)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); LOG_DEBUG("Admin client"); handshakeCMD(fn+7); }else if (strncmp(fn,"/admin/?",8)==0) { if (!isAllowed(ALLOW_HTML)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); LOG_DEBUG("Admin client"); handshakeCMD(fn+8); }else if (strncmp(fn,"/admin.cgi",10)==0) { if (!isAllowed(ALLOW_BROADCAST)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); char *pwdArg = getCGIarg(fn,"pass="); char *songArg = getCGIarg(fn,"song="); char *mountArg = getCGIarg(fn,"mount="); char *urlArg = getCGIarg(fn,"url="); if (pwdArg && songArg) { int i; int slen = strlen(fn); for(i=0; i<slen; i++) if (fn[i]=='&') fn[i] = 0; Channel *c=NULL; for(i=0; i<ChanMgr::MAX_CHANNELS; i++) { c = &chanMgr->channels[i]; if ((c->status == Channel::S_BROADCASTING) && (c->info.srcType == ChanInfo::T_MP3) ) { // if we have a mount point then check for it, otherwise update all channels. if (mountArg) if (strcmp(c->mount,mountArg)) c = NULL; if (c) { c->info.track.title.set(songArg,String::T_ESC); if (urlArg) if (urlArg[0]) c->info.track.contact.set(urlArg,String::T_ESC); LOG_CHANNEL("Ch.%d Shoutcast update: %s",c->index,songArg); c->updateMeta(); } } } } }else if (strncmp(fn,"/pls/",5)==0) { Channel *c = getChannel(fn+5); if (c) handshakePLS(&c,1,false); }else if (strncmp(fn,"/mp3/",5)==0) { triggerChannel(fn+5,true); }else if (strncmp(fn,"/ogg/",5)==0) { triggerChannel(fn+5,true); }else if (strncmp(fn,"/stream/",8)==0) { triggerChannel(fn+8,true); }else if (strncmp(fn,"/channel/",9)==0) { triggerChannel(fn+9,false); }else if (strncmp(fn,"/rtsp/",6)==0) { //handshakeRTSP(fn+6); }else if (strcmp(fn,"/")==0) { if (!isAllowed(ALLOW_HTML)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); LOG_NETWORK("HTML client"); handshakeCMD(fn+1); }else{ LOG_ERROR("GET: %s",fn); throw HTTPException(HTTP_SC_BADREQUEST,400); } }else if (http.isRequest("GIV")) { if (!isAllowed(ALLOW_DATA)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); char *end = strstr(in+4,":"); if (!end) throw HTTPException(HTTP_SC_BADREQUEST,400); *end = 0; int index = atoi(in+4); // at the moment we don`t really care where the GIV came from, so just give to chan. no. if its waiting. Channel *ch = chanMgr->findPushChannel(index); if (ch) LOG_NETWORK("GIV %d to channel %d",index,ch->index); else LOG_NETWORK("GIV %d, no channels waiting",index); if (!ch) throw HTTPException(HTTP_SC_NOTFOUND,404); ch->pushSock = sock; ch->pushIndex = 0; sock = NULL; // release this servent but dont close socket. }else if (http.isRequest("PEERCAST CONNECT")) { if (!isAllowed(ALLOW_SERVENT)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); LOG_DEBUG("PEERCAST client"); processServent(); }else if (http.isRequest("GNUTELLA CONNECT")) { if (!isAllowed(ALLOW_SERVENT)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); if ((servMgr->allowGnutella) && !servMgr->inFull()) { LOG_DEBUG("GNUTELLA client"); processServent(); }else{ throw HTTPException(HTTP_SC_UNAVAILABLE,503); } }else if (http.isRequest("SOURCE")) { if (!isAllowed(ALLOW_BROADCAST)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); char *mount = NULL; char *ps; if (ps=strstr(in,"ICE/1.0")) { mount = in+7; *ps = 0; LOG_DEBUG("ICE 1.0 client to %s",mount?mount:"unknown"); }else{ mount = in+strlen(in); while (*--mount) if (*mount == '/') { mount[-1] = 0; // password preceeds break; } strcpy(loginPassword,in+7); LOG_DEBUG("ICY client to %s",mount?mount:"unknown"); } if (mount) strcpy(loginMount,mount); handshakeICY(Channel::SRC_ICECAST,isHTTP); sock = NULL; // socket is taken over by channel, so don`t close it }else if (http.isRequest(servMgr->password)) { if (!isAllowed(ALLOW_BROADCAST)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); strcpy(loginPassword,servMgr->password); // pwd already checked sock->writeLine("OK2"); sock->writeLine("icy-caps:11"); sock->writeLine(""); LOG_DEBUG("ShoutCast client"); handshakeICY(Channel::SRC_SHOUTCAST,isHTTP); sock = NULL; // socket is taken over by channel, so don`t close it }else if (http.isRequest("POST")) { handshakePOST(); }else { throw HTTPException(HTTP_SC_BADREQUEST,400); }}// -----------------------------------// find a channel in list, or search if not found.Channel *Servent::getChannel(char *str){ if (!isAllowed(ALLOW_DATA)) throw HTTPException(HTTP_SC_UNAUTHORIZED,401); if (servMgr->streamFull() && !isPrivate()) throw HTTPException(HTTP_SC_UNAVAILABLE,503); ChanInfo info; info.initNameID(str); servMgr->procConnectArgs(str,info.id); HTTP http(*sock); // remove file extension (only added for winamp) char *ext = strstr(str,"."); if (ext) *ext = 0; Channel *ch; ch=chanMgr->findChannel(info); // channel not in relay list, so find it hitlist if (!ch) if (!chanMgr->findAndRelay(info,&ch,1)) ch=NULL; if (!ch) throw HTTPException(HTTP_SC_NOTFOUND,404); bool isFull = servMgr->bitrateFull(ch->getBitrate()) || ch->isFull() || servMgr->streamFull(); if (isFull && !isPrivate()) throw HTTPException(HTTP_SC_UNAVAILABLE,503); return ch;}// -----------------------------------void Servent::handshakeIncoming(){ sock->timeout = 10000; setStatus(S_HANDSHAKE); char buf[1024]; sock->readLine(buf,sizeof(buf)); char sb[64]; sock->host.toStr(sb); if (stristr(buf,RTSP_PROTO1)) { LOG_DEBUG("RTSP from %s '%s'",sb,buf); RTSP rtsp(*sock); rtsp.initRequest(buf); handshakeRTSP(rtsp); }else if (stristr(buf,HTTP_PROTO1)) { LOG_DEBUG("HTTP from %s '%s'",sb,buf); HTTP http(*sock); http.initRequest(buf); handshakeHTTP(http,true); }else { LOG_DEBUG("Connect from %s '%s'",sb,buf); HTTP http(*sock); http.initRequest(buf); handshakeHTTP(http,false); }}// -----------------------------------void Servent::triggerChannel(char *n, bool isRaw) { if (isRaw) { // check that this servent can listen if (!canPreview()) throw HTTPException(HTTP_SC_UNAVAILABLE,503); } Channel *ch = getChannel(n); if (ch) { LOG_DEBUG("Channel client: OK"); type = T_STREAM; chanID = ch->info.id; ch->prefetchCnt = 20; // prefetch for 20 packets processStream(false,isRaw); }}// -----------------------------------void writePLSHeader(Stream &s){ s.writeLine(HTTP_SC_OK); s.writeLine("%s %s",HTTP_HS_SERVER,PCX_AGENT); s.writeLine("%s %s",HTTP_HS_CONTENT,MIME_XM3U); s.writeLine("Content-Disposition: inline; filename=playlist.m3u"); s.writeLine("Cache-Control: private" ); s.writeLine("%s %s",HTTP_HS_CONNECTION,"close"); s.writeLine("");}// -----------------------------------void Servent::handshakePLS(Channel **cl, int num, bool doneHandshake){ char url[256]; char in[128]; if (!doneHandshake) while (sock->readLine(in,128)); if (getLocalURL(url)) { writePLSHeader(*sock); PlayList *pls; pls = new PlayList(PlayList::T_PLS,num); pls->addChannels(url,cl,num); pls->write(*sock); delete pls; }}// -----------------------------------void Servent::handshakePLS(ChanHitList **cl, int num, bool doneHandshake){ char url[256]; char in[128]; if (!doneHandshake) while (sock->readLine(in,128)); if (getLocalURL(url)) { writePLSHeader(*sock); PlayList *pls; pls = new PlayList(PlayList::T_SCPLS,num); for(int i=0; i<num; i++) pls->addChannel(url,cl[i]->info); pls->write(*sock); delete pls; }}// -----------------------------------bool Servent::getLocalURL(char *str){ if (!sock) throw StreamException("Not connected"); char ipStr[64]; Host h; if (sock->host.localIP()) h = sock->getLocalHost(); else h = servMgr->serverHost; h.port = servMgr->serverHost.port; h.toStr(ipStr); sprintf(str,"http://%s",ipStr); return true;}// -----------------------------------// Warning: testing RTSP/RTP stuff below.// .. moved over to seperate app now.// -----------------------------------void Servent::handshakePOST(){#if 0 char tmp[1024]; while (sock->readLine(tmp,sizeof(tmp))) LOG_DEBUG("POST: %s",tmp); while (sock->active()) { // Quicktime (6) doesnt seem to flush its buffers, so we don`t receive a proper base64 body sometimes. // So we have to wait until theres some data pending before going ahead to read otherwise it`ll stall. while (!sock->readPending()) sys->sleepIdle(); int rl = sock->readPending(); if (rl > sizeof(tmp)-1) rl = sizeof(tmp)-1; sock->read(tmp,rl); tmp[rl]=0; MemoryStream mem(tmp,rl); mem.convertFromBase64(); char cmd[512],ln[512]; mem.readLine(cmd,sizeof(cmd)); LOG_DEBUG("RTSP-CMD: %s",cmd); if (strncmp(cmd,"DESCRIBE",8)==0) { while (mem.readLine(ln,sizeof(ln))) LOG_DEBUG("RTSP-ARG: %s",ln);#if 0 LOG_DEBUG("out"); sock->writeLine("RTSP/1.0 200 OK"); sock->writeLine("Content-Type: application/sdp"); sock->writeLine("Cache-Control: no-cache"); sock->writeLine("Content-length: 45"); sock->writeLine("Cseq:1"); sock->writeLine(""); sock->writeLine("o=StreamingServer 3241325783 1025857208000 IN IP4 192.168.1.3"); sock->writeLine("s=\test.mov"); sock->writeLine("m=audio 0 RTP/AVP 96"); sock->writeLine("b=AS:21"); sock->writeLine("");#endif sock->writeLine("RTSP/1.0 200 OK"); sock->writeLine("Server: DSS/4.1.1 (Build/412.36; Platform/Win32)"); sock->writeLine("Cseq:"); sock->writeLine("Last-Modified: Fri, 05 Jul 2002 08:20:08 GMT"); sock->writeLine("Cache-Control: must-revalidate"); sock->writeLine("Content-length: 360"); sock->writeLine("Date: Wed, 18 Sep 2002 08:16:24 GMT"); sock->writeLine("Expires: Wed, 18 Sep 2002 08:16:24 GMT"); sock->writeLine("Content-Type: application/sdp"); sock->writeLine("x-Accept-Retransmit: our-retransmit"); sock->writeLine("x-Accept-Dynamic-Rate: 1"); sock->writeLine("Content-Base: rtsp://localhost/test.mov/"); sock->writeLine("v=0"); sock->writeLine("o=StreamingServer 3241325783 1025857208000 IN IP4 192.168.1.3"); sock->writeLine("s=\test.mov"); sock->writeLine("u=http:///"); sock->writeLine("e=admin@"); sock->writeLine("c=IN IP4 0.0.0.0"); sock->writeLine("b=AS:288"); sock->writeLine("t=0 0"); sock->writeLine("a=control:*"); sock->writeLine("a=range:npt=0- 66.47000"); sock->writeLine("m=audio 0 RTP/AVP 96"); sock->writeLine("b=AS:21"); sock->writeLine("a=rtpmap:96 X-QDM/44100/2"); sock->writeLine("a=control:trackID=3"); sock->writeLine("a=x-bufferdelay:5.99"); sock->writeLine("m=video 0 RTP/AVP 97"); sock->writeLine("b=AS:267"); sock->writeLine("a=rtpmap:97 X-SV3V-ES/90000"); sock->writeLine("a=control:trackID=4"); } }#endif}#if 0RTSP/1.0 200 OKServer: DSS/4.1.1 (Build/412.36; Platform/Win32)Cseq:Last-Modified: Fri, 05 Jul 2002 08:20:08 GMT
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -