Index: server/connectionHTTP.h
===================================================================
RCS file: /var/cvsroot/streamdev/server/connectionHTTP.h,v
retrieving revision 1.4
diff -u -r1.4 connectionHTTP.h
--- server/connectionHTTP.h	2 Apr 2007 10:32:34 -0000	1.4
+++ server/connectionHTTP.h	12 Apr 2007 19:14:04 -0000
@@ -12,6 +12,7 @@
 
 class cChannel;
 class cStreamdevLiveStreamer;
+class cIConv;
 
 class cConnectionHTTP: public cServerConnection {
 private:
@@ -25,7 +26,11 @@
 	enum eHTTPJob {
 		hjTransfer,
 		hjListing,
+		hjListing_m3u,
+		hjListing_m3u_groups,
 	};
+	int     m_LastChannel;
+	cIConv *m_IConv;
 
 	std::string                       m_Request;
 	//std::map<std::string,std::string> m_Headers; TODO: later?
Index: server/connectionHTTP.c
===================================================================
RCS file: /var/cvsroot/streamdev/server/connectionHTTP.c,v
retrieving revision 1.10
diff -u -r1.10 connectionHTTP.c
--- server/connectionHTTP.c	26 Jan 2006 19:40:18 -0000	1.10
+++ server/connectionHTTP.c	12 Apr 2007 19:14:04 -0000
@@ -7,8 +7,70 @@
 #include "server/connectionHTTP.h"
 #include "server/setup.h"
 
+//
+// cIconv - simple iconv wrapper
+//
+
+#ifndef NO_ICONV
+#  include <iconv.h>
+
+class cIConv
+{
+private:
+	iconv_t m_ic;
+
+public:
+	cIConv(const char *SrcCharset = NULL, const char * DstCharset = NULL);
+	virtual ~cIConv();
+	cString Translate(const char *Text) const;
+};
+
+cIConv::cIConv(const char *SrcCharset, const char * DstCharset)
+{
+	DstCharset = DstCharset ?: "UTF-8";
+	SrcCharset = SrcCharset ?: I18nCharSets()[Setup.OSDLanguage];
+	m_ic = iconv_open(DstCharset, SrcCharset);
+	if(m_ic == (iconv_t)-1) 
+		esyslog("cIConv: iconv_open(\"%s\",\"%s\") failed: %m", SrcCharset, DstCharset);
+}
+
+cIConv::~cIConv()
+{
+	if(m_ic != (iconv_t)-1) 
+		iconv_close(m_ic);
+}
+
+cString cIConv::Translate(const char *Text) const
+{
+	if(m_ic != (iconv_t)-1) {
+		size_t inc  = strlen(Text);
+		size_t outc = inc<2048 ? 2048 : inc+1;
+#ifdef __APPLE__  
+		const char *in = Text;
+#else
+		char   *in   = (char*)Text;
+#endif
+		char   *buf  = (char*)malloc(outc+1);
+		char   *out  = buf;
+		if(iconv(m_ic, &in, &inc, &out, &outc) != (size_t)-1) {
+			*out = 0;
+			return cString(buf, true);
+		}
+		esyslog("cIConv: iconv(%s) failed: %m", Text);
+		free(buf);
+	}
+	return cString(Text);
+}
+#endif
+
+
+//
+// cConnectionHttp
+//
+
 cConnectionHTTP::cConnectionHTTP(void): 
 		cServerConnection("HTTP"),
+		m_IConv(NULL),
 		m_Status(hsRequest),
 		m_LiveStreamer(NULL),
 		m_Channel(NULL),
@@ -22,6 +84,9 @@
 cConnectionHTTP::~cConnectionHTTP() 
 {
 	delete m_LiveStreamer;
+#ifndef NO_ICONV
+	delete m_IConv;
+#endif
 }
 
 bool cConnectionHTTP::Command(char *Cmd) 
@@ -41,6 +106,7 @@
 		}
 		Dprintf("header\n");
 		return true;
+	default:;
 	}
 	return false; // ??? shouldn't happen
 }
@@ -57,6 +123,17 @@
 			    && Respond("<html><head><title>VDR Channel Listing</title></head>")
 			    && Respond("<body><ul>");
 
+		case hjListing_m3u_groups:
+		case hjListing_m3u:
+#ifndef NO_ICONV
+			if(!m_IConv)
+				m_IConv = new cIConv();
+#endif
+			return Respond("HTTP/1.0 200 OK")
+                            && Respond("Content-Type: audio/x-mpegurl")
+			    && Respond("")
+			    && Respond("#EXTM3U");
+
 		case hjTransfer:
 			if (m_Channel == NULL) {
 				DeferClose();
@@ -95,7 +172,7 @@
 
 void cConnectionHTTP::Flushed(void) 
 {
-	std::string line;
+	std::string line, name, url;
 
 	if (m_Status != hsBody)
 		return;
@@ -141,6 +218,49 @@
 		m_ListChannel = Channels.Next(m_ListChannel);
 		break;
 
+	case hjListing_m3u:
+		while(m_ListChannel && m_ListChannel->GroupSep())
+			m_ListChannel = Channels.Next(m_ListChannel);
+		if(m_ListChannel && m_ListChannel->Number() > m_LastChannel)
+			m_ListChannel = NULL;
+		// fall thru
+
+	case hjListing_m3u_groups:
+		if (m_Job == hjListing_m3u_groups)
+			while(m_ListChannel && !m_ListChannel->GroupSep())
+				m_ListChannel = Channels.Next(m_ListChannel);
+
+		if (m_ListChannel == NULL) {
+			DeferClose();
+			m_Status = hsFinished;
+			return;
+		}
+		
+#ifndef NO_ICONV
+		name = (std::string) *m_IConv->Translate(m_ListChannel->Name());
+#else
+		name = (std::string) m_ListChannel->Name();
+#endif
+		url = (std::string) "http://" + LocalIp() + ":" 
+		    + (const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/";
+		line = (std::string) "#EXTINF:0,";
+
+		if (m_Job == hjListing_m3u_groups) {
+			line += name + "\r\n"
+			     + url + (const char *)itoa(m_ListChannel->Number()) + "."
+			     + m_ListChannel->Name() + ".m3u";
+		}
+		if (m_Job == hjListing_m3u) {
+			line += (std::string)itoa(m_ListChannel->Number()) + " " + name + "\r\n"
+			     + url + (const char*)m_ListChannel->GetChannelID().ToString();
+		}
+
+		if (!Respond(line.c_str()))
+			DeferClose();
+
+		m_ListChannel = Channels.Next(m_ListChannel);
+		break;
+
 	case hjTransfer:
 		Dprintf("streamer start\n");
 		m_LiveStreamer->Start(this);
@@ -153,7 +273,7 @@
 {
 	const char *sp = Opts.c_str(), *ptr = sp, *ep;
 	const cChannel *chan;
-	int apid = 0, pos;
+	int apid = 0;
 
 	ptr = skipspace(ptr);
 	while (*ptr == '/')
@@ -184,10 +304,43 @@
 	std::string filespec = Opts.substr(ptr - sp, ep - ptr);
 	Dprintf("substr: %s\n", filespec.c_str());
 
+	int n = strlen(filespec.c_str());
+	if(filespec.substr(n-3, n-1) == ".ts") {
+		filespec = filespec.substr(0, n-3);
+		m_StreamType = stTS;
+	} else if(filespec.substr(n-4, n-1) == ".pes") {
+		filespec = filespec.substr(0, n-4);
+		m_StreamType = stPES;
+	} else if(filespec.substr(n-4, n-1) == ".vob") {
+		filespec = filespec.substr(0, n-4);
+		m_StreamType = stPS;
+	}
+
 	Dprintf("before channelfromstring\n");
 	if (filespec == "" || filespec.substr(0, 12) == "channels.htm") {
 		m_ListChannel = Channels.First();
 		m_Job = hjListing;
+	} else if (filespec.substr(0, 12) == "channels.m3u") {
+		m_ListChannel = Channels.First();
+		m_LastChannel = 0x7fff;
+		m_Job = hjListing_m3u;
+	} else if (filespec.substr(0, 17) == "channelgroups.m3u") {
+		m_ListChannel = Channels.First();
+		m_Job = hjListing_m3u_groups;
+	} else if (strstr(filespec.c_str(), ".m3u")) {
+		int first = atoi(filespec.c_str());
+		cChannel *ch = Channels.First();
+		m_LastChannel = 0x7fff;
+		while(ch && ch->Number() < first)
+                        ch = Channels.Next(ch);
+		while(ch && ch->GroupSep())
+			ch = Channels.Next(ch);
+		m_ListChannel = ch;
+		while(ch && !ch->GroupSep()) {
+			m_LastChannel = ch->Number();
+                        ch = Channels.Next(ch);
+		}
+		m_Job = hjListing_m3u;
 	} else if ((chan = ChannelFromString(filespec.c_str(), &apid)) != NULL) {
 		m_Channel = chan;
 		m_Apid = apid;
