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  }