This source file includes following definitions.
- NPT_SET_LOCAL_LOGGER
- AddSharedDirectory
- OnBrowseDirectChildren
- ServeFile
- GetFilePath
- BuildFromFilePath
- BuildFromFilePathAndHost
- GetResourceURI
- ShareVirtualResource
- ServeVirtualFile
#include "PltUPnP.h"
#include "GPACFileMediaServer.h"
#include "PltMediaItem.h"
#include "PltService.h"
#include "PltTaskManager.h"
#include "PltHttpServer.h"
#include "PltDidl.h"
#include "PltMetadataHandler.h"
#include "PltVersion.h"
#include <gpac/network.h>
NPT_SET_LOCAL_LOGGER("gpac.media.server.file")
GPAC_FileMediaServer::GPAC_FileMediaServer(const char* friendly_name,
bool show_ip,
const char* uuid,
NPT_UInt16 port) : PLT_FileMediaServer("", friendly_name, show_ip, uuid, port)
{
}
void GPAC_FileMediaServer::AddSharedDirectory(const char *path, const char *alias, Bool is_hidden)
{
u8 buf[10];
if (!alias) {
sprintf((char*)buf, "%08X", gf_crc_32((char*) path, (u32) strlen(path)));
alias = (const char *)buf;
}
m_Directories.Add(GPAC_MediaDirectory(alias, path, is_hidden));
}
NPT_Result
GPAC_FileMediaServer::OnBrowseDirectChildren(PLT_ActionReference& action,
const char* object_id,
const char* filter,
NPT_UInt32 start_index,
NPT_UInt32 req_count,
const char* sort_criteria,
const PLT_HttpRequestContext& context)
{
if (strcmp(object_id, "/") && strcmp(object_id, "\\") && strcmp(object_id, "0")) {
return PLT_MediaServer::OnBrowseDirectChildren(action, object_id, filter, start_index, req_count, sort_criteria, context);
}
unsigned long cur_index = 0;
unsigned long num_returned = 0;
unsigned long total_matches = 0;
NPT_String didl = didl_header;
PLT_MediaObjectReference item;
for (u32 i=0; i<m_Directories.GetItemCount(); i++) {
GPAC_MediaDirectory *alias;
m_Directories.Get(i, alias);
if (alias->m_Hide) continue;
item = BuildFromFilePath(alias->m_Path, context, true, false);
if (!item.IsNull()) {
if ((cur_index >= start_index) && ((num_returned < req_count) || (req_count == 0))) {
NPT_String tmp;
NPT_CHECK_SEVERE(PLT_Didl::ToDidl(*item.AsPointer(), filter, tmp));
didl += tmp;
num_returned++;
}
cur_index++;
total_matches++;
}
};
didl += didl_footer;
NPT_CHECK_SEVERE(action->SetArgumentValue("Result", didl));
NPT_CHECK_SEVERE(action->SetArgumentValue("NumberReturned", NPT_String::FromInteger(num_returned)));
NPT_CHECK_SEVERE(action->SetArgumentValue("TotalMatches", NPT_String::FromInteger(total_matches)));
NPT_CHECK_SEVERE(action->SetArgumentValue("UpdateId", "1"));
return NPT_SUCCESS;
}
NPT_Result
GPAC_FileMediaServer::ServeFile(const NPT_HttpRequest& request,
const NPT_HttpRequestContext& context,
NPT_HttpResponse& response,
const NPT_String& _file_path)
{
NPT_COMPILER_UNUSED(context);
NPT_String file_path = _file_path;
NPT_String uri_path = NPT_Uri::PercentDecode(request.GetUrl().GetPath());
NPT_String query = request.GetUrl().GetQuery();
if (! query.IsEmpty()) {
uri_path += "?";
uri_path += query;
}
if ((file_path.Find("/..") >= 0) || (file_path.Find("\\..") >= 0)) {
return NPT_FAILURE;
}
NPT_String file_id = (const char *) file_path + ((file_path[0]=='0') ? 2:1);
for (u32 i=0; i<m_Directories.GetItemCount(); i++) {
GPAC_MediaDirectory *dir;
m_Directories.Get(i, dir);
if (file_id.StartsWith(dir->m_Alias)) {
file_path = dir->m_Path + ((const char *) file_id + strlen(dir->m_Alias)+1);
break;
}
}
for (u32 i=0; i<m_VirtualFiles.GetItemCount(); i++) {
GPAC_VirtualFile vfile;
m_VirtualFiles.Get(i, vfile);
NPT_String path = uri_path;
path.Replace('/', NPT_FilePath::Separator);
if (path == vfile.m_URI) {
NPT_Result res;
NPT_Position start, end;
#if 0
PLT_HttpHelper::GetRange(request, start, end);
#else
start = end = -1;
#endif
res = ServeVirtualFile(response, &vfile, start, end, !request.GetMethod().Compare("HEAD"));
if (vfile.m_temporary) {
m_VirtualFiles.Remove(vfile);
}
return res;
}
}
NPT_String path = m_FileRoot;
if (path.Compare(uri_path.Left(path.GetLength()), true) == 0) {
#if 0
NPT_Position start, end;
PLT_HttpHelper::GetRange(request, start, end);
return PLT_HttpServer::ServeFile(response,
file_path,
start,
end,
!request.GetMethod().Compare("HEAD"));
#else
return PLT_HttpServer::ServeFile(request, context, response, NPT_FilePath::Create(m_FileRoot, file_path) );
#endif
}
return NPT_FAILURE;
}
NPT_Result
GPAC_FileMediaServer::GetFilePath(const char* object_id,
NPT_String& filepath)
{
if (!object_id) return NPT_ERROR_INVALID_PARAMETERS;
NPT_String obj_id = object_id + (object_id[0]=='0' ? 2 : 1);
filepath = "";
for (u32 i=0; i<m_Directories.GetItemCount(); i++) {
GPAC_MediaDirectory *dir;
m_Directories.Get(i, dir);
if (obj_id.StartsWith(dir->m_Alias)) {
filepath = dir->m_Path;
object_id += (object_id[0]=='0' ? 2 : 1);
if (!strcmp(object_id, dir->m_Alias)) {
object_id = "";
} else {
object_id += strlen(dir->m_Alias) + 1;
}
break;
}
}
if (NPT_StringLength(object_id) > 2 || object_id[0]!='0') {
filepath += (const char*)object_id + (object_id[0]=='0'?1:0);
}
return NPT_SUCCESS;
}
PLT_MediaObject*
GPAC_FileMediaServer::BuildFromFilePath(const NPT_String& filepath,
const PLT_HttpRequestContext &context,
bool with_count,
bool keep_extension_in_title,
bool allip)
{
return BuildFromFilePathAndHost(filepath, &context, with_count, keep_extension_in_title, NULL);
}
PLT_MediaObject*
GPAC_FileMediaServer::BuildFromFilePathAndHost(const NPT_String& __filepath,
const PLT_HttpRequestContext *context,
bool with_count ,
bool keep_extension_in_title ,
const char *host)
{
PLT_MediaItemResource resource;
PLT_MediaObject* object = NULL;
NPT_String filepath = __filepath;
unsigned int len;
NPT_FileInfo info;
NPT_CHECK_LABEL_FATAL(NPT_File::GetInfo(__filepath, &info), failure);
len = 0;
for (u32 i=0; i<m_Directories.GetItemCount(); i++) {
GPAC_MediaDirectory *dir;
m_Directories.Get(i, dir);
if (__filepath.StartsWith(dir->m_Path) && (dir->m_Path.GetLength() > len) ) {
char *fp = __filepath;
filepath = NPT_FilePath::Separator + dir->m_Alias + NPT_String( fp + strlen(dir->m_Path) - 1);
len = dir->m_Path.GetLength();
}
}
if (info.m_Type == NPT_FileInfo::FILE_TYPE_REGULAR) {
NPT_IpAddress ip;
object = new PLT_MediaItem();
object->m_Title = NPT_FilePath::BaseName(filepath, keep_extension_in_title);
if (object->m_Title.GetLength() == 0) goto failure;
resource.m_ProtocolInfo = PLT_ProtocolInfo(PLT_ProtocolInfo::GetProtocolInfo(filepath, true, context));
if (!resource.m_ProtocolInfo.IsValid()) goto failure;
resource.m_Size = info.m_Size;
NPT_String url = "0" + filepath;
if (host) {
ip.Parse(host);
NPT_List<NPT_NetworkInterface*> if_list;
NPT_List<NPT_NetworkInterface*>::Iterator net_if;
NPT_List<NPT_NetworkInterfaceAddress>::Iterator net_if_addr;
NPT_CHECK_LABEL_SEVERE(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, true), failure);
for (net_if = if_list.GetFirstItem(); net_if; net_if++) {
if ( (*net_if)->IsAddressInNetwork(ip) ) {
ip = (*net_if)->GetAddresses().GetFirstItem()->GetPrimaryAddress();
break;
}
}
} else if (context) {
ip = context->GetLocalAddress().GetIpAddress();
} else {
NPT_List<NPT_IpAddress> ips;
NPT_CHECK_LABEL_SEVERE(PLT_UPnPMessageHelper::GetIPAddresses(ips), failure);
NPT_List<NPT_IpAddress>::Iterator ipi = ips.GetFirstItem();
ip = *ipi;
}
#if 0
PLT_MetadataHandler* handler = NULL;
NPT_Result res = NPT_ContainerFind(
m_MetadataHandlers,
PLT_MetadataHandlerFinder(NPT_FilePath::FileExtension(filepath)),
handler);
if (NPT_SUCCEEDED(res) && handler) {
if (NPT_SUCCEEDED(handler->Load(filepath))) {
NPT_String newTitle;
if (handler->GetTitle(newTitle) != NULL) {
object->m_Title = newTitle;
}
handler->GetDescription(object->m_Description.long_description);
if (object->m_ExtraInfo.album_art_uri.GetLength() == 0) {
object->m_ExtraInfo.album_art_uri =
NPT_Uri::PercentEncode(BuildResourceUri(m_AlbumArtBaseUri, ip.ToString(), url),
NPT_Uri::UnsafeCharsToEncode);
}
handler->GetDuration(resource.m_Duration);
handler->GetProtection(resource.m_Protection);
}
}
#endif
object->m_ObjectClass.type = PLT_MediaItem::GetUPnPClass(filepath, context);
NPT_HttpUrl base_uri(m_FileRoot);
resource.m_Uri = BuildResourceUri(base_uri, ip.ToString(), url);
object->m_Resources.Add(resource);
} else {
object = new PLT_MediaContainer;
if ((filepath.Compare("/", true) == 0) || (filepath.Compare("\\", true) == 0)) {
object->m_Title = "Root";
} else {
filepath.TrimRight("/\\");
object->m_Title = NPT_FilePath::BaseName(filepath, keep_extension_in_title);
if (object->m_Title.GetLength() == 0) goto failure;
}
#ifndef _WIN32_WCE
NPT_Cardinal count = 0;
count = 0;
if (with_count ) {
NPT_List<NPT_String> entries;
NPT_File::ListDir(__filepath, entries);
count = entries.GetItemCount();
((PLT_MediaContainer*)object)->m_ChildrenCount = count;
}
#endif
object->m_ObjectClass.type = "object.container";
}
if ((filepath.Compare("/", true) == 0) || (filepath.Compare("\\", true) == 0)) {
object->m_ParentID = "-1";
object->m_ObjectID = "0";
} else {
NPT_String directory = NPT_FilePath::DirName(filepath);
if ((directory.Compare("/", true) == 0) || (directory.Compare("\\", true) == 0)) {
object->m_ParentID = "0";
} else {
object->m_ParentID = "0" + directory;
}
object->m_ObjectID = "0" + filepath;
}
return object;
failure:
delete object;
return NULL;
}
NPT_String GPAC_FileMediaServer::GetResourceURI(const char *url, const char *for_host)
{
char *abs_url;
NPT_String res = url, path;
abs_url = NULL;
for (u32 i=0; i<m_Directories.GetItemCount(); i++) {
GPAC_MediaDirectory *dir;
m_Directories.Get(i, dir);
abs_url = gf_url_concatenate(dir->m_Path, url);
FILE *f = gf_fopen(abs_url, "rt");
if (f) {
gf_fclose(f);
break;
}
gf_free((void*)abs_url);
abs_url = NULL;
}
if (!abs_url ) return res;
if (!strcmp(abs_url, url)) {
Bool found = GF_FALSE;
NPT_String newdir;
u32 nb_sep=0;
u32 len = (u32) strlen(abs_url);
u32 i=0;
while (i<=len) {
if ((abs_url[len-i]=='\\') || (abs_url[len-i]=='/') ) {
nb_sep++;
if (nb_sep==2) break;
}
i++;
}
if (nb_sep!=2) return "";
char sep = abs_url[len-i];
abs_url[len-i] = 0;
newdir = abs_url;
abs_url[len-i] = sep;
newdir.Replace('/', NPT_FilePath::Separator);
newdir += NPT_FilePath::Separator;
for (u32 i=0; i<m_Directories.GetItemCount(); i++) {
GPAC_MediaDirectory *dir;
m_Directories.Get(i, dir);
if (!strcmp(newdir, dir->m_Path)) {
found = GF_TRUE;
}
}
if (!found)
AddSharedDirectory(newdir, NULL, GF_TRUE);
}
path = abs_url;
path.Replace('/', NPT_FilePath::Separator);
gf_free((void*)abs_url);
PLT_MediaObject*mobj = BuildFromFilePathAndHost(path, NULL, true, false, for_host);
if (mobj) {
res = mobj->m_Resources[0].m_Uri;
delete mobj;
}
return res;
}
void GPAC_FileMediaServer::ShareVirtualResource(const char *res_uri, const char *res_val, const char *res_mime, Bool temporary)
{
NPT_String the_uri;
char *uri = (char *)res_uri;
char *sep = (char *) strstr(res_uri, "://");
if (sep) {
sep = strchr(sep+3, '/');
if (sep) uri = sep;
else uri = (char*)"/";
}
the_uri = "";
while (1) {
sep = strchr(uri, '%');
if (!sep) break;
if (!strnicmp(sep, "%3f", 3) || !strnicmp(sep, "%5c", 3) || !strnicmp(sep, "%2f", 3)) {
sep[0]=0;
the_uri += uri;
sep[0]='%';
if (!strnicmp(sep, "%3f", 3))
the_uri += "?";
else if (!strnicmp(sep, "%5c", 3))
the_uri += "/";
else
the_uri += " ";
uri= sep+3;
continue;
} else {
the_uri += "%";
uri= sep+1;
}
}
the_uri += uri;
the_uri.Replace('/', NPT_FilePath::Separator);
GPAC_VirtualFile vres(the_uri, res_val, res_mime, temporary);
m_VirtualFiles.Add(vres);
GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[UPnP] sharing virtual file %s as %s\n", res_uri, (const char *)the_uri));
}
NPT_Result
GPAC_FileMediaServer::ServeVirtualFile(NPT_HttpResponse& response,
GPAC_VirtualFile *vfile,
NPT_Position start,
NPT_Position end,
bool request_is_head)
{
NPT_LargeSize total_len;
NPT_Result result;
NPT_MemoryStream* memory_stream = new NPT_MemoryStream(vfile->m_Content.GetChars(), vfile->m_Content.GetLength() );
NPT_InputStreamReference stream(memory_stream);
if (NPT_FAILED(result = stream->GetSize(total_len)) ) {
response.SetStatus(404, "File Not Found");
return NPT_SUCCESS;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[UPnP] Serving virtual file %s\n", vfile->m_Content.GetChars()));
NPT_HttpEntity* entity = new NPT_HttpEntity();
entity->SetContentLength(total_len);
response.SetEntity(entity);
entity->SetContentType(vfile->m_MIME);
if (request_is_head) return NPT_SUCCESS;
if (start != (NPT_Position)-1 || end != (NPT_Position)-1) {
NPT_Position start_offset = (NPT_Position)-1, end_offset = total_len - 1, len;
if (start == (NPT_Position)-1 && end != (NPT_Position)-1) {
if (end >= total_len) {
start_offset = 0;
} else {
start_offset = total_len-end;
}
} else if (start != (NPT_Position)-1) {
start_offset = start;
if (end != (NPT_Position)-1 && end < start) {
end_offset = (NPT_Position)-1;
}
}
if (start_offset == (NPT_Position)-1 || end_offset == (NPT_Position)-1 ||
start_offset > end_offset || NPT_FAILED(stream->Seek(start_offset))) {
response.SetStatus(416, "Requested range not satisfiable");
} else {
len = end_offset - start_offset + 1;
response.SetStatus(206, "Partial Content");
entity->SetInputStream(stream);
entity->SetContentLength(len);
}
} else {
entity->SetInputStream(stream);
}
return NPT_SUCCESS;
}