/* NAME parseurl.go DESCRIPTION See Readme.md AUTHOR Saxon Nelson-Milton LICENSE parseurl.go is Copyright (C) 2017-2018 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It 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. You should have received a copy of the GNU General Public License along with revid in gpl.txt. If not, see http://www.gnu.org/licenses. Derived from librtmp under the GNU Lesser General Public License 2.1 Copyright (C) 2005-2008 Team XBMC http://www.xbmc.org Copyright (C) 2008-2009 Andrej Stepanchuk Copyright (C) 2009-2010 Howard Chu */ package rtmp import ( "fmt" "log" "strconv" "strings" "unsafe" ) // int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app); // parseurl.c +33 func C_RTMP_ParseURL(url *byte, host *C_AVal, port *uint16, playpath, app *C_AVal) (protocol int32, ok bool) { var p, end, col, ques, slash *byte // TODO: use our logger here // RTMP_Log(RTMP_LOGDEBUG, "Parsing..."); protocol = RTMP_PROTOCOL_RTMP *port = 0 playpath.av_len = 0 playpath.av_val = nil app.av_len = 0 app.av_val = nil p = strstr(url, goStrToCStr("://")) if p == nil { // TODO: use our logger here log.Println("RTMP URL: No :// in url!") return protocol, false } /* NOTE: the following code nees to be ported if we're using anything other than rtmp! { int len = (int)(p-url); if(len == 4 && strncasecmp(url, "rtmp", 4)==0) *protocol = RTMP_PROTOCOL_RTMP; else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0) *protocol = RTMP_PROTOCOL_RTMPT; else if(len == 5 && strncasecmp(url, "rtmps", 5)==0) *protocol = RTMP_PROTOCOL_RTMPS; else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0) *protocol = RTMP_PROTOCOL_RTMPE; else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0) *protocol = RTMP_PROTOCOL_RTMFP; else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0) *protocol = RTMP_PROTOCOL_RTMPTE; else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0) *protocol = RTMP_PROTOCOL_RTMPTS; else { RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n"); goto parsehost; } } */ // TODO: implement new logger here // RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol); // Get the hostname p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) // check for sudden death if *p == 0 { // TODO: use new logger here // RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!"); return protocol, false } end = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(strlen(p)))) col = strchr(p, ':') ques = strchr(p, '?') slash = strchr(p, '/') { var hostlen int32 if slash != nil { hostlen = int32(uintptr(unsafe.Pointer(slash)) - uintptr(unsafe.Pointer(p))) } else { hostlen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) } if col != nil && int32(uintptr(unsafe.Pointer(col))-uintptr(unsafe.Pointer(p))) < hostlen { hostlen = int32(uintptr(unsafe.Pointer(col)) - uintptr(unsafe.Pointer(p))) } if hostlen < 256 { host.av_val = (*byte)(unsafe.Pointer(p)) host.av_len = hostlen // TODO: use new logger with this //RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host.av_val); } else { // TODO: use new logger with this // RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!"); } p = (*byte)(incBytePtr(unsafe.Pointer(p), int(hostlen))) } // get port number if available if *p == ':' { var p2 uint32 p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) tmp, err := strconv.Atoi(cStrToGoStr(p)) if err != nil { return protocol, false } p2 = uint32(tmp) if p2 > 65535 { // TODO: use new logger with this // RTMP_Log(RTMP_LOGWARNING, "Invalid port number!"); } else { *port = uint16(p2) } } if slash == nil { // TODO: use new logger // RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!"); return protocol, true } p = (*byte)(incBytePtr(unsafe.Pointer(slash), 1)) { /* parse application * * rtmp://host[:port]/app[/appinstance][/...] * application = app[/appinstance] */ var slash2 *byte var slash3 *byte = nil var slash4 *byte = nil var applen, appnamelen int32 slash2 = strchr(p, '/') if slash2 != nil { slash3 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash2))+ uintptr(1))), '/') } if slash3 != nil { slash4 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash3))+ uintptr(1))), '/') } // ondemand, pass all parameters as app applen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) appnamelen = applen switch { case ques != nil && strstr(p, goStrToCStr("slist=")) != nil: appnamelen = int32(uintptr(unsafe.Pointer(ques)) - uintptr(unsafe.Pointer(p))) case strings.Compare(cStrToGoStr(p)[:9], "ondemand/") == 0: /* app = ondemand/foobar, only pass app=ondemand */ applen = 8 appnamelen = 8 default: switch { case slash4 != nil: appnamelen = int32(uintptr(unsafe.Pointer(slash4)) - uintptr( unsafe.Pointer(p))) case slash3 != nil: appnamelen = int32(uintptr(unsafe.Pointer(slash3)) - uintptr( unsafe.Pointer(p))) case slash2 != nil: appnamelen = int32(uintptr(unsafe.Pointer(slash2)) - uintptr( unsafe.Pointer(p))) } applen = appnamelen } app.av_val = (*byte)(unsafe.Pointer(p)) app.av_len = applen // TODO: use new logging here // RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p); p = (*byte)(incBytePtr(unsafe.Pointer(p), int(appnamelen))) } if *p == '/' { p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) } // NOTE: don't think we currently need this section - see 787 for this func if int(uintptr(unsafe.Pointer(end))-uintptr(unsafe.Pointer(p))) != 0 { var av C_AVal av.av_val = (*byte)(unsafe.Pointer(p)) av.av_len = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) // TODO: port THis //C.RTMP_ParsePlaypath(&av, playpath) C_RTMP_ParsePlaypath(&av, playpath) } return protocol, true } // void RTMP_ParsePlaypath(AVal *in, AVal *out); // parseurl.c +201 func C_RTMP_ParsePlaypath(in, out *C_AVal) { var addMP4 int32 = 0 var addMP3 int32 = 0 var subExt int32 = 0 playpath := in.av_val var temp, q *byte var ext *byte = nil ppstart := (*byte)(unsafe.Pointer(playpath)) var streamname, destptr, p *byte pplen := in.av_len out.av_val = nil out.av_len = 0 temp = strstr((*byte)(unsafe.Pointer(ppstart)), goStrToCStr("slist=")) if *ppstart == '?' && temp != nil { ppstart = (*byte)(incBytePtr(unsafe.Pointer(temp), 6)) pplen = strlen(ppstart) temp = strchr(ppstart, '&') if temp != nil { pplen = int32(uintptr(unsafe.Pointer(temp)) - uintptr(unsafe.Pointer(ppstart))) } } q = strchr(ppstart, '?') if pplen >= 4 { if q != nil { ext = (*byte)(decBytePtr(unsafe.Pointer(q), 4)) } else { ext = (*byte)(sliceToPtr((*[_Gi]byte)(unsafe.Pointer(ppstart))[uintptr(pplen)-4:])) } switch { case strings.Compare(cStrToGoStr(ext)[:4], ".f4v") == 0 || strings.Compare(cStrToGoStr(ext)[:4], ".mp4") == 0: addMP4 = 1 subExt = 1 case ppstart == (*byte)(unsafe.Pointer(playpath)) && strings.Compare( cStrToGoStr(ext)[:4], ".flv") == 0: subExt = 1 case strings.Compare(cStrToGoStr(ext)[:4], ".mp3") == 0: addMP3 = 1 subExt = 1 } } streamname = (*byte)(malloc(uintptr(pplen + 4 + 1))) if streamname == nil { return } destptr = streamname switch { case addMP4 != 0: if strings.Compare(cStrToGoStr(ppstart)[:4], "mp4") != 0 { memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp4:")), uintptr(len("mp4:"))) destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) } else { subExt = 0 } case addMP3 != 0: if strings.Compare(cStrToGoStr(ppstart)[:4], "mp3") != 0 { memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp3:")), uintptr(len("mp4:"))) destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) } else { subExt = 0 } } p = ppstart for pplen > 0 { if subExt != 0 && p == ext { p = (*byte)(incBytePtr(unsafe.Pointer(p), 4)) pplen -= 4 continue } if *p == '%' { var c uint32 fmt.Sscanf(cStrToGoStr((*byte)(incBytePtr(unsafe.Pointer(p), 1))), "%02x", &c) (*[_Gi]byte)(unsafe.Pointer(destptr))[0] = byte(c) destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) pplen -= 3 p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) } else { (*[_Gi]byte)(unsafe.Pointer(destptr))[0] = *p destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) pplen-- } } *destptr = '\x00' out.av_val = (*byte)(unsafe.Pointer(streamname)) out.av_len = int32(uintptr(unsafe.Pointer(destptr)) - uintptr(unsafe.Pointer( streamname))) }