github.com/divyam234/rclone@v1.64.1/cmd/serve/dlna/dlna_util.go (about) 1 package dlna 2 3 import ( 4 "crypto/md5" 5 "encoding/xml" 6 "fmt" 7 "io" 8 "log" 9 "net" 10 "net/http" 11 "net/http/httptest" 12 "net/http/httputil" 13 "os" 14 15 "github.com/anacrolix/dms/soap" 16 "github.com/anacrolix/dms/upnp" 17 "github.com/divyam234/rclone/fs" 18 ) 19 20 // Return a default "friendly name" for the server. 21 func makeDefaultFriendlyName() string { 22 hostName, err := os.Hostname() 23 if err != nil { 24 hostName = "" 25 } else { 26 hostName = " (" + hostName + ")" 27 } 28 return "rclone" + hostName 29 } 30 31 func makeDeviceUUID(unique string) string { 32 h := md5.New() 33 if _, err := io.WriteString(h, unique); err != nil { 34 log.Panicf("makeDeviceUUID write failed: %s", err) 35 } 36 buf := h.Sum(nil) 37 return upnp.FormatUUID(buf) 38 } 39 40 // Get all available active network interfaces. 41 func listInterfaces() []net.Interface { 42 ifs, err := net.Interfaces() 43 if err != nil { 44 log.Printf("list network interfaces: %v", err) 45 return []net.Interface{} 46 } 47 48 var active []net.Interface 49 for _, intf := range ifs { 50 if isAppropriatelyConfigured(intf) { 51 active = append(active, intf) 52 } 53 } 54 return active 55 } 56 57 func isAppropriatelyConfigured(intf net.Interface) bool { 58 return intf.Flags&net.FlagUp != 0 && intf.Flags&net.FlagMulticast != 0 && intf.MTU > 0 59 } 60 61 func didlLite(chardata string) string { 62 return `<DIDL-Lite` + 63 ` xmlns:dc="http://purl.org/dc/elements/1.1/"` + 64 ` xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"` + 65 ` xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"` + 66 ` xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">` + 67 chardata + 68 `</DIDL-Lite>` 69 } 70 71 func mustMarshalXML(value interface{}) []byte { 72 ret, err := xml.MarshalIndent(value, "", " ") 73 if err != nil { 74 log.Panicf("mustMarshalXML failed to marshal %v: %s", value, err) 75 } 76 return ret 77 } 78 79 // Marshal SOAP response arguments into a response XML snippet. 80 func marshalSOAPResponse(sa upnp.SoapAction, args map[string]string) []byte { 81 soapArgs := make([]soap.Arg, 0, len(args)) 82 for argName, value := range args { 83 soapArgs = append(soapArgs, soap.Arg{ 84 XMLName: xml.Name{Local: argName}, 85 Value: value, 86 }) 87 } 88 return []byte(fmt.Sprintf(`<u:%[1]sResponse xmlns:u="%[2]s">%[3]s</u:%[1]sResponse>`, 89 sa.Action, sa.ServiceURN.String(), mustMarshalXML(soapArgs))) 90 } 91 92 type loggingResponseWriter struct { 93 http.ResponseWriter 94 request *http.Request 95 committed bool 96 } 97 98 func (lrw *loggingResponseWriter) logRequest(code int, err interface{}) { 99 // Choose appropriate log level based on response status code. 100 var level fs.LogLevel 101 if code < 400 && err == nil { 102 level = fs.LogLevelInfo 103 } else { 104 level = fs.LogLevelError 105 } 106 107 if err == nil { 108 err = "" 109 } 110 111 fs.LogPrintf(level, lrw.request.URL, "%s %s %d %s %s", 112 lrw.request.RemoteAddr, lrw.request.Method, code, 113 lrw.request.Header.Get("SOAPACTION"), err) 114 } 115 116 func (lrw *loggingResponseWriter) WriteHeader(code int) { 117 lrw.committed = true 118 lrw.logRequest(code, nil) 119 lrw.ResponseWriter.WriteHeader(code) 120 } 121 122 // HTTP handler that logs requests and any errors or panics. 123 func logging(next http.Handler) http.Handler { 124 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 125 lrw := &loggingResponseWriter{ResponseWriter: w, request: r} 126 defer func() { 127 err := recover() 128 if err != nil { 129 if !lrw.committed { 130 lrw.logRequest(http.StatusInternalServerError, err) 131 http.Error(w, fmt.Sprint(err), http.StatusInternalServerError) 132 } else { 133 // Too late to send the error to client, but at least log it. 134 fs.Errorf(r.URL.Path, "Recovered panic: %v", err) 135 } 136 } 137 }() 138 next.ServeHTTP(lrw, r) 139 }) 140 } 141 142 // HTTP handler that logs complete request and response bodies for debugging. 143 // Error recovery and general request logging are left to logging(). 144 func traceLogging(next http.Handler) http.Handler { 145 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 146 dump, err := httputil.DumpRequest(r, true) 147 if err != nil { 148 serveError(nil, w, "error dumping request", err) 149 return 150 } 151 fs.Debugf(nil, "%s", dump) 152 153 recorder := httptest.NewRecorder() 154 next.ServeHTTP(recorder, r) 155 156 dump, err = httputil.DumpResponse(recorder.Result(), true) 157 if err != nil { 158 // log the error but ignore it 159 fs.Errorf(nil, "error dumping response: %v", err) 160 } else { 161 fs.Debugf(nil, "%s", dump) 162 } 163 164 // copy from recorder to the real response writer 165 for k, v := range recorder.Header() { 166 w.Header()[k] = v 167 } 168 w.WriteHeader(recorder.Code) 169 _, err = recorder.Body.WriteTo(w) 170 if err != nil { 171 // Network error 172 fs.Debugf(nil, "Error writing response: %v", err) 173 } 174 }) 175 } 176 177 // HTTP handler that sets headers. 178 func withHeader(name string, value string, next http.Handler) http.Handler { 179 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 180 w.Header().Set(name, value) 181 next.ServeHTTP(w, r) 182 }) 183 } 184 185 // serveError returns an http.StatusInternalServerError and logs the error 186 func serveError(what interface{}, w http.ResponseWriter, text string, err error) { 187 err = fs.CountError(err) 188 fs.Errorf(what, "%s: %v", text, err) 189 http.Error(w, text+".", http.StatusInternalServerError) 190 } 191 192 // Splits a path into (root, ext) such that root + ext == path, and ext is empty 193 // or begins with a period. Extended version of path.Ext(). 194 func splitExt(path string) (string, string) { 195 for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- { 196 if path[i] == '.' { 197 return path[:i], path[i:] 198 } 199 } 200 return path, "" 201 }