00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include "autoconfig.h"
00034 #endif
00035
00036 #include "server.h"
00037 #include <sys/types.h>
00038 #include <sys/stat.h>
00039 #include <unistd.h>
00040 #include <string.h>
00041 #include <stdio.h>
00042 #include "common.h"
00043 #include "storage.h"
00044 #include "cds_objects.h"
00045 #include "process.h"
00046 #include "update_manager.h"
00047 #include "session_manager.h"
00048 #include "ixml.h"
00049 #include "file_io_handler.h"
00050 #include "dictionary.h"
00051 #include "file_request_handler.h"
00052 #include "metadata_handler.h"
00053 #include "tools.h"
00054 #include "play_hook.h"
00055
00056 #ifdef HAVE_LIBDVDNAV
00057 #include "dvd_io_handler.h"
00058 #include "fd_io_handler.h"
00059 #include "metadata/dvd_handler.h"
00060 #include "mpegremux_processor.h"
00061 #include "thread_executor.h"
00062 #include "io_handler_chainer.h"
00063 #endif
00064
00065 #ifdef EXTERNAL_TRANSCODING
00066 #include "transcoding/transcode_dispatcher.h"
00067 #endif
00068
00069 using namespace zmm;
00070 using namespace mxml;
00071
00072 FileRequestHandler::FileRequestHandler() : RequestHandler()
00073 {
00074 }
00075
00076 void FileRequestHandler::get_info(IN const char *filename, OUT struct File_Info *info)
00077 {
00078 log_debug("start\n");
00079
00080 String mimeType;
00081 int objectID;
00082 #ifdef EXTERNAL_TRANSCODING
00083 String tr_profile;
00084 #endif
00085
00086 struct stat statbuf;
00087 int ret = 0;
00088 bool is_srt = false;
00089
00090 String parameters = (filename + strlen(LINK_FILE_REQUEST_HANDLER));
00091
00092 Ref<Dictionary> dict(new Dictionary());
00093 dict->decodeSimple(parameters);
00094
00095 log_debug("full url (filename): %s, parameters: %s\n",
00096 filename, parameters.c_str());
00097
00098 String objID = dict->get(_("object_id"));
00099 if (objID == nil)
00100 {
00101
00102 throw _Exception(_("get_info: object_id not found"));
00103 }
00104 else
00105 objectID = objID.toInt();
00106
00107
00108
00109 Ref<Storage> storage = Storage::getInstance();
00110
00111 Ref<CdsObject> obj = storage->loadObject(objectID);
00112
00113 int objectType = obj->getObjectType();
00114
00115 if (!IS_CDS_ITEM(objectType))
00116 {
00117 throw _Exception(_("requested object is not an item"));
00118 }
00119
00120 Ref<CdsItem> item = RefCast(obj, CdsItem);
00121
00122 String path = item->getLocation();
00123
00124
00125 int res_id = 0;
00126 String s_res_id = dict->get(_(URL_RESOURCE_ID));
00127 #ifdef EXTERNAL_TRANSCODING
00128 if (string_ok(s_res_id) && (s_res_id != _(URL_VALUE_TRANSCODE_NO_RES_ID)))
00129 #else
00130 if (string_ok(s_res_id))
00131 #endif
00132 res_id = s_res_id.toInt();
00133 else
00134 res_id = -1;
00135
00136 String ext = dict->get(_("ext"));
00137 int edot = ext.rindex('.');
00138 if (edot > -1)
00139 ext = ext.substring(edot);
00140 if ((ext == ".srt") || (ext == ".ssa") ||
00141 (ext == ".smi") || (ext == ".sub"))
00142 {
00143 int dot = path.rindex('.');
00144 if (dot > -1)
00145 {
00146 path = path.substring(0, dot);
00147 }
00148
00149 path = path + ext;
00150 mimeType = _(MIMETYPE_TEXT);
00151
00152
00153 res_id = 0;
00154 is_srt = true;
00155 }
00156
00157 ret = stat(path.c_str(), &statbuf);
00158 if (ret != 0)
00159 {
00160 if (is_srt)
00161 throw SubtitlesNotFoundException(_("Subtitle file ") + path + " is not available.");
00162 else
00163 throw _Exception(_("Failed to open ") + path + " - " + strerror(errno));
00164
00165 }
00166
00167
00168 if (access(path.c_str(), R_OK) == 0)
00169 {
00170 info->is_readable = 1;
00171 }
00172 else
00173 {
00174 info->is_readable = 0;
00175 }
00176
00177 String header;
00178 log_debug("path: %s\n", path.c_str());
00179 int slash_pos = path.rindex(DIR_SEPARATOR);
00180 if (slash_pos >= 0)
00181 {
00182 if (slash_pos < path.length()-1)
00183 {
00184 slash_pos++;
00185
00186
00187 header = _("Content-Disposition: attachment; filename=\"") +
00188 path.substring(slash_pos) + _("\"");
00189 }
00190 }
00191
00192 #ifdef EXTERNAL_TRANSCODING
00193 tr_profile = dict->get(_(URL_PARAM_TRANSCODE_PROFILE_NAME));
00194 #endif
00195
00196 info->http_header = NULL;
00197
00198 log_debug("fetching resource id %d\n", res_id);
00199 String rh = dict->get(_(RESOURCE_HANDLER));
00200
00201 if (((res_id > 0) && (res_id < item->getResourceCount())) ||
00202 ((res_id > 0) && string_ok(rh)))
00203 {
00204
00205 log_debug("setting content length to unknown\n");
00207 info->file_length = -1;
00208
00209 int res_handler;
00210 if (string_ok(rh))
00211 res_handler = rh.toInt();
00212 else
00213 {
00214 Ref<CdsResource> resource = item->getResource(res_id);
00215 res_handler = resource->getHandlerType();
00216
00217 String protocolInfo = item->getResource(res_id)->getAttributes()->get(_("protocolInfo"));
00218 if (protocolInfo != nil)
00219 {
00220 mimeType = getMTFromProtocolInfo(protocolInfo);
00221 }
00222 }
00223
00224 Ref<MetadataHandler> h = MetadataHandler::createHandler(res_handler);
00225 if (!string_ok(mimeType))
00226 mimeType = h->getMimeType();
00227
00228 h->serveContent(item, res_id, &(info->file_length));
00229
00230 }
00231 else
00232 {
00233 #ifdef EXTERNAL_TRANSCODING
00234 if (!is_srt && string_ok(tr_profile))
00235 {
00236
00237 Ref<TranscodingProfile> tp = ConfigManager::getInstance()->getTranscodingProfileListOption(CFG_TRANSCODING_PROFILE_LIST)->getByName(tr_profile);
00238
00239 if (tp == nil)
00240 throw _Exception(_("Transcoding of file ") + path +
00241 " but no profile matching the name " +
00242 tr_profile + " found");
00243
00244 mimeType = tp->getTargetMimeType();
00245
00246 Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
00247 if (mappings->get(mimeType) == CONTENT_TYPE_PCM)
00248 {
00249 String freq = item->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY));
00250 String nrch = item->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS));
00251
00252 if (string_ok(freq))
00253 mimeType = mimeType + _(";rate=") + freq;
00254 if (string_ok(nrch))
00255 mimeType = mimeType + _(";channels=") + nrch;
00256 }
00257
00258 info->file_length = -1;
00259 }
00260 else
00261 #endif
00262 #ifdef HAVE_LIBDVDNAV
00263 if (!is_srt && item->getFlag(OBJECT_FLAG_DVD_IMAGE))
00264 {
00265 String tmp = dict->get(DVDHandler::renderKey(DVD_Title));
00266 if (!string_ok(tmp))
00267 throw _Exception(_("DVD Image requested but title parameter is missing!"));
00268 int title = tmp.toInt();
00269 if (title < 0)
00270 throw _Exception(_("DVD Image - requested invalid title!"));
00271
00272 tmp = dict->get(DVDHandler::renderKey(DVD_Chapter));
00273 if (!string_ok(tmp))
00274 throw _Exception(_("DVD Image requested but chapter parameter is missing!"));
00275 int chapter = tmp.toInt();
00276 if (chapter < 0)
00277 throw _Exception(_("DVD Image - requested invalid chapter!"));
00278
00279
00280 tmp = dict->get(DVDHandler::renderKey(DVD_AudioStreamID));
00281 if (!string_ok(tmp))
00282 throw _Exception(_("DVD Image requested but audio track parameter is missing!"));
00283 int audio_track = tmp.toInt();
00284 if (audio_track < 0)
00285 throw _Exception(_("DVD Image - requested invalid audio stream ID!"));
00286
00288 info->file_length = -1;
00289 header = nil;
00290 }
00291 else
00292 #endif
00293 {
00294 info->file_length = statbuf.st_size;
00295
00296
00297
00298 if (S_ISREG(statbuf.st_mode))
00299 {
00300 if (string_ok(header))
00301 header = header + _("\r\n");
00302
00303 header = header + _("Accept-Ranges: bytes");
00307 }
00308 }
00309
00310 if (!string_ok(mimeType))
00311 mimeType = item->getMimeType();
00312
00313
00314
00315 }
00316
00317 #ifdef EXTEND_PROTOCOLINFO
00318 header = getDLNAtransferHeader(mimeType, header);
00319 #endif
00320
00321 if (string_ok(header))
00322 info->http_header = ixmlCloneDOMString(header.c_str());
00323
00324 info->last_modified = statbuf.st_mtime;
00325 info->is_directory = S_ISDIR(statbuf.st_mode);
00326
00327 info->content_type = ixmlCloneDOMString(mimeType.c_str());
00328
00329
00330
00331
00332 log_debug("web_get_info(): end\n");
00333 }
00334
00335 Ref<IOHandler> FileRequestHandler::open(IN const char *filename, OUT struct File_Info *info, IN enum UpnpOpenFileMode mode)
00336 {
00337 int objectID;
00338 String mimeType;
00339 int ret;
00340 bool is_srt = false;
00341 #ifdef EXTERNAL_TRANSCODING
00342 String tr_profile;
00343 #endif
00344
00345 log_debug("start\n");
00346 struct stat statbuf;
00347
00348
00349
00350 if (mode != UPNP_READ)
00351 throw _Exception(_("UPNP_WRITE unsupported"));
00352
00353 String url_path, parameters;
00354
00355 parameters = (filename + strlen(LINK_FILE_REQUEST_HANDLER));
00356
00357 Ref<Dictionary> dict(new Dictionary());
00358 dict->decodeSimple(parameters);
00359 log_debug("full url (filename): %s, parameters: %s\n",
00360 filename, parameters.c_str());
00361
00362 String objID = dict->get(_("object_id"));
00363 if (objID == nil)
00364 {
00365 throw _Exception(_("object_id not found"));
00366 }
00367 else
00368 objectID = objID.toInt();
00369
00370 log_debug("Opening media file with object id %d\n", objectID);
00371 Ref<Storage> storage = Storage::getInstance();
00372
00373 Ref<CdsObject> obj = storage->loadObject(objectID);
00374
00375 int objectType = obj->getObjectType();
00376
00377 if (!IS_CDS_ITEM(objectType))
00378 {
00379 throw _Exception(_("requested object is not an item"));
00380 }
00381
00382
00383 int res_id = 0;
00384 String s_res_id = dict->get(_(URL_RESOURCE_ID));
00385 #ifdef EXTERNAL_TRANSCODING
00386 if (string_ok(s_res_id) && (s_res_id != _(URL_VALUE_TRANSCODE_NO_RES_ID)))
00387 #else
00388 if (string_ok(s_res_id))
00389 #endif
00390 res_id = s_res_id.toInt();
00391 else
00392 res_id = -1;
00393
00394
00395
00396 if (IS_CDS_ACTIVE_ITEM(objectType) && (res_id == 0))
00397 {
00398 Ref<CdsActiveItem> aitem = RefCast(obj, CdsActiveItem);
00399
00400 Ref<Element> inputElement = UpnpXML_DIDLRenderObject(obj, true);
00401
00402 inputElement->setAttribute(_(XML_DC_NAMESPACE_ATTR), _(XML_DC_NAMESPACE));
00403 inputElement->setAttribute(_(XML_UPNP_NAMESPACE_ATTR), _(XML_UPNP_NAMESPACE));
00404 String action = aitem->getAction();
00405 String input = inputElement->print();
00406 String output;
00407
00408 log_debug("Script input: %s\n", input.c_str());
00409 if(strncmp(action.c_str(), "http://", 7))
00410 {
00411 #ifdef TOMBDEBUG
00412 struct timespec before;
00413 getTimespecNow(&before);
00414 #endif
00415 output = run_simple_process(action, _("run"), input);
00416 #ifdef TOMBDEBUG
00417 long delta = getDeltaMillis(&before);
00418 log_debug("script executed in %ld milliseconds\n", delta);
00419 #endif
00420 }
00421 else
00422 {
00424 log_debug("fetching %s\n", action.c_str());
00425 output = input;
00426 }
00427 log_debug("Script output: %s\n", output.c_str());
00428
00429 Ref<CdsObject> clone = CdsObject::createObject(objectType);
00430 aitem->copyTo(clone);
00431
00432 UpnpXML_DIDLUpdateObject(clone, output);
00433
00434 if (! aitem->equals(clone, true))
00435 {
00436 Ref<UpdateManager> um = UpdateManager::getInstance();
00437 Ref<SessionManager> sm = SessionManager::getInstance();
00438
00439 log_debug("Item changed, updating database\n");
00440 int containerChanged = INVALID_OBJECT_ID;
00441 storage->updateObject(clone, &containerChanged);
00442 um->containerChanged(containerChanged);
00443 sm->containerChangedUI(containerChanged);
00444
00445 if (! aitem->equals(clone))
00446 {
00447 log_debug("Item changed visually, updating parent\n");
00448 um->containerChanged(clone->getParentID(), FLUSH_ASAP);
00449 }
00450 obj = clone;
00451 }
00452 else
00453 {
00454 log_debug("Item untouched...\n");
00455 }
00456 }
00457
00458 Ref<CdsItem> item = RefCast(obj, CdsItem);
00459
00460 String path = item->getLocation();
00461
00462 String ext = dict->get(_("ext"));
00463 int edot = ext.rindex('.');
00464 if (edot > -1)
00465 ext = ext.substring(edot);
00466 if ((ext == ".srt") || (ext == ".ssa") ||
00467 (ext == ".smi") || (ext == ".sub"))
00468 {
00469 int dot = path.rindex('.');
00470 if (dot > -1)
00471 {
00472 path = path.substring(0, dot);
00473 }
00474
00475 path = path + ext;
00476 mimeType = _(MIMETYPE_TEXT);
00477
00478 res_id = 0;
00479 is_srt = true;
00480 }
00481
00482 ret = stat(path.c_str(), &statbuf);
00483 if (ret != 0)
00484 {
00485 if (is_srt)
00486 throw SubtitlesNotFoundException(_("Subtitle file ") + path + " is not available.");
00487 else
00488 throw _Exception(_("Failed to open ") + path + " - " + strerror(errno));
00489 }
00490
00491 if (access(path.c_str(), R_OK) == 0)
00492 {
00493 info->is_readable = 1;
00494 }
00495 else
00496 {
00497 info->is_readable = 0;
00498 }
00499
00500 String header;
00501
00502 info->last_modified = statbuf.st_mtime;
00503 info->is_directory = S_ISDIR(statbuf.st_mode);
00504
00505
00506 log_debug("path: %s\n", path.c_str());
00507 int slash_pos = path.rindex(DIR_SEPARATOR);
00508 if (slash_pos >= 0)
00509 {
00510 if (slash_pos < path.length()-1)
00511 {
00512 slash_pos++;
00513
00514
00515 header = _("Content-Disposition: attachment; filename=\"") +
00516 path.substring(slash_pos) + _("\"");
00517 }
00518 }
00519 log_debug("fetching resource id %d\n", res_id);
00520 #ifdef EXTERNAL_TRANSCODING
00521 tr_profile = dict->get(_(URL_PARAM_TRANSCODE_PROFILE_NAME));
00522 if (string_ok(tr_profile))
00523 {
00524 if (res_id != (-1))
00525 throw _Exception(_("Invalid resource ID given!"));
00526 }
00527 else
00528 {
00529 if (res_id == -1)
00530 throw _Exception(_("Invalid resource ID given!"));
00531 }
00532 #endif
00533
00534 info->http_header = NULL;
00535
00536
00537
00538
00539
00540
00541 String rh = dict->get(_(RESOURCE_HANDLER));
00542 if (((res_id > 0) && (res_id < item->getResourceCount())) ||
00543 ((res_id > 0) && string_ok(rh)))
00544 {
00545 info->file_length = -1;
00546
00547 int res_handler;
00548 if (string_ok(rh))
00549 res_handler = rh.toInt();
00550 else
00551 {
00552 Ref<CdsResource> resource = item->getResource(res_id);
00553 res_handler = resource->getHandlerType();
00554
00555 String protocolInfo = item->getResource(res_id)->getAttributes()->get(_("protocolInfo"));
00556 if (protocolInfo != nil)
00557 {
00558 mimeType = getMTFromProtocolInfo(protocolInfo);
00559 }
00560 }
00561
00562 Ref<MetadataHandler> h = MetadataHandler::createHandler(res_handler);
00563
00564 if (!string_ok(mimeType))
00565 mimeType = h->getMimeType();
00566
00567 #ifdef EXTEND_PROTOCOLINFO
00568 header = getDLNAtransferHeader(mimeType, header);
00569 #endif
00570
00571 if (string_ok(header))
00572 info->http_header = ixmlCloneDOMString(header.c_str());
00573
00574 info->content_type = ixmlCloneDOMString(mimeType.c_str());
00575 Ref<IOHandler> io_handler = h->serveContent(item, res_id, &(info->file_length));
00576 io_handler->open(mode);
00577 return io_handler;
00578 }
00579 else
00580 {
00581 #ifdef EXTERNAL_TRANSCODING
00582 if (!is_srt && string_ok(tr_profile))
00583 {
00584 Ref<TranscodeDispatcher> tr_d(new TranscodeDispatcher());
00585 Ref<TranscodingProfile> tp = ConfigManager::getInstance()->getTranscodingProfileListOption(CFG_TRANSCODING_PROFILE_LIST)->getByName(tr_profile);
00586 return tr_d->open(tp, path, RefCast(item, CdsObject), info);
00587 }
00588 else
00589 #endif
00590 #ifdef HAVE_LIBDVDNAV
00591 if (!is_srt && item->getFlag(OBJECT_FLAG_DVD_IMAGE))
00592 {
00593 String tmp = dict->get(DVDHandler::renderKey(DVD_Title));
00594 if (!string_ok(tmp))
00595 throw _Exception(_("DVD Image requested but title parameter is missing!"));
00596 int title = tmp.toInt();
00597 if (title < 0)
00598 throw _Exception(_("DVD Image - requested invalid title!"));
00599
00600 tmp = dict->get(DVDHandler::renderKey(DVD_Chapter));
00601 if (!string_ok(tmp))
00602 throw _Exception(_("DVD Image requested but chapter parameter is missing!"));
00603 int chapter = tmp.toInt();
00604 if (chapter < 0)
00605 throw _Exception(_("DVD Image - requested invalid chapter!"));
00606
00607
00608 tmp = dict->get(DVDHandler::renderKey(DVD_AudioStreamID));
00609 if (!string_ok(tmp))
00610 throw _Exception(_("DVD Image requested but audio track parameter is missing!"));
00611 int audio_track = tmp.toInt();
00612 if (audio_track < 0)
00613 throw _Exception(_("DVD Image - requested invalid audio stream ID!"));
00614
00616 info->file_length = -1;
00617 info->force_chunked = 1;
00618 header = nil;
00619 if (mimeType == nil)
00620 mimeType = item->getMimeType();
00621
00622 info->content_type = ixmlCloneDOMString(mimeType.c_str());
00623 log_debug("Serving dvd image %s Title: %d Chapter: %d\n",
00624 path.c_str(), title, chapter);
00626 Ref<IOHandler> dvd_io_handler(new DVDIOHandler(path, title, chapter,
00627 audio_track));
00628
00629 int from_dvd_fd[2];
00630 if (pipe(from_dvd_fd) == -1)
00631 throw _Exception(_("Failed to create DVD input pipe!"));
00632
00633 int from_remux_fd[2];
00634 if (pipe(from_remux_fd) == -1)
00635 {
00636 close(from_dvd_fd[0]);
00637 close(from_dvd_fd[1]);
00638 throw _Exception(_("Failed to create remux output pipe!"));
00639 }
00640
00641 Ref<IOHandler> fd_writer(new FDIOHandler(from_dvd_fd[1]));
00642 Ref<ThreadExecutor> from_dvd(new IOHandlerChainer(dvd_io_handler,
00643 fd_writer, 16384));
00644
00645 Ref<IOHandler> fd_reader(new FDIOHandler(from_remux_fd[0]));
00646 fd_reader->open(mode);
00647
00648 Ref<MPEGRemuxProcessor> remux(new MPEGRemuxProcessor(from_dvd_fd[0],
00649 from_remux_fd[1],
00650 (unsigned char)audio_track));
00651
00652 RefCast(fd_reader, FDIOHandler)->addReference(RefCast(remux, Object));
00653 RefCast(fd_reader, FDIOHandler)->addReference(RefCast(from_dvd, Object));
00654 RefCast(fd_reader, FDIOHandler)->addReference(RefCast(fd_writer, Object));
00655 RefCast(fd_reader, FDIOHandler)->closeOther(fd_writer);
00656
00657 PlayHook::getInstance()->trigger(obj);
00658 return fd_reader;
00659 }
00660 else
00661 #endif
00662
00663 {
00664 if (mimeType == nil)
00665 mimeType = item->getMimeType();
00666
00667 info->file_length = statbuf.st_size;
00668 info->content_type = ixmlCloneDOMString(mimeType.c_str());
00669
00670 log_debug("Adding content disposition header: %s\n",
00671 header.c_str());
00672
00673
00674
00675 if (S_ISREG(statbuf.st_mode))
00676 {
00677 if (string_ok(header))
00678 header = header + _("\r\n");
00679
00680 header = header + _("Accept-Ranges: bytes");
00681 }
00682
00683 #ifdef EXTEND_PROTOCOLINFO
00684 header = getDLNAtransferHeader(mimeType, header);
00685 #endif
00686 if (string_ok(header))
00687 info->http_header = ixmlCloneDOMString(header.c_str());
00688
00689
00690 Ref<IOHandler> io_handler(new FileIOHandler(path));
00691 io_handler->open(mode);
00692 PlayHook::getInstance()->trigger(obj);
00693 return io_handler;
00694 }
00695 }
00696 }