3
MediaTomb - http://www.mediatomb.cc/
5
server.cc - this file is part of MediaTomb.
7
Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
8
Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
10
Copyright (C) 2006-2007 Gena Batyan <bgeradz@mediatomb.cc>,
11
Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
12
Leonhard Wimmer <leo@mediatomb.cc>
14
MediaTomb is free software; you can redistribute it and/or modify
15
it under the terms of the GNU General Public License version 2
16
as published by the Free Software Foundation.
18
MediaTomb is distributed in the hope that it will be useful,
19
but WITHOUT ANY WARRANTY; without even the implied warranty of
20
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
GNU General Public License for more details.
23
You should have received a copy of the GNU General Public License
24
version 2 along with MediaTomb; if not, write to the Free Software
25
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
27
$Id: server.cc 1356 2007-06-12 22:59:19Z jin_eld $
33
#include "autoconfig.h"
37
#include "web_callbacks.h"
38
#include "content_manager.h"
39
#include "update_manager.h"
40
#include "dictionary.h"
47
Ref<Storage> Server::storage = nil;
49
SINGLETON_MUTEX(Server, false);
51
static int static_upnp_callback(Upnp_EventType eventtype, void *event, void *cookie)
53
return Server::getInstance()->upnp_callback(eventtype, event, cookie);
56
void Server::static_cleanup_callback()
62
storage->threadCleanup();
64
catch (Exception ex) {}
68
Server::Server() : Singleton<Server>()
70
server_shutdown_flag = false;
75
virtual_directory = _(SERVER_VIRTUAL_DIR);
77
ContentDirectoryService::setStaticArgs(_(DESC_CDS_SERVICE_TYPE),
78
_(DESC_CDS_SERVICE_ID));
79
cds = ContentDirectoryService::getInstance();
81
ConnectionManagerService::setStaticArgs(_(DESC_CM_SERVICE_TYPE),
82
_(DESC_CM_SERVICE_ID));
83
cmgr = ConnectionManagerService::getInstance();
85
#if defined(ENABLE_MRREG)
86
MRRegistrarService::setStaticArgs(_(DESC_MRREG_SERVICE_TYPE),
87
_(DESC_MRREG_SERVICE_ID));
88
mrreg = MRRegistrarService::getInstance();
91
Ref<ConfigManager> config = ConfigManager::getInstance();
93
serverUDN = config->getOption(CFG_SERVER_UDN);
94
alive_advertisement = config->getIntOption(CFG_SERVER_ALIVE_INTERVAL);
97
void Server::upnp_init(String iface, String ip_address, int port)
99
int ret = 0; // general purpose error code
102
log_debug("start\n");
104
Ref<ConfigManager> config = ConfigManager::getInstance();
106
if (!string_ok(iface))
107
iface = config->getOption(CFG_SERVER_NETWORK_INTERFACE);
109
if (!string_ok(ip_address))
110
ip = config->getOption(CFG_SERVER_IP);
114
if (string_ok(ip) && string_ok(iface))
115
throw _Exception(_("You can not specify interface and IP at the same time!"));
119
ip = interfaceToIP(iface);
121
if (string_ok(iface) && !string_ok(ip))
122
throw _Exception(_("Could not find interface: ") + iface);
124
log_debug("interface: %s ip: %s\n", iface.c_str(), ip.c_str());
129
port = config->getIntOption(CFG_SERVER_PORT);
137
// this is important, so the storage lives a little longer when
138
// shutdown is initiated
139
storage = Storage::getInstance();
141
if (storage->threadCleanupRequired())
142
cb = (void *)static_cleanup_callback;
144
ret = UpnpInit(ip.c_str(), port, config->getIntOption(CFG_SERVER_RETRIES_ON_TIMEOUT), cb);
146
if (ret != UPNP_E_SUCCESS)
148
throw _UpnpException(ret, _("upnp_init: UpnpInit failed"));
151
port = UpnpGetServerPort();
153
log_info("Initialized port: %d\n", port);
157
ip = String(UpnpGetServerIpAddress());
160
log_info("Server bound to: %s\n", ip.c_str());
162
virtual_url = _("http://") + ip + ":" + port + "/" + virtual_directory;
164
// next set webroot directory
165
String web_root = config->getOption(CFG_SERVER_WEBROOT);
167
if (!string_ok(web_root))
169
throw _Exception(_("invalid web server root directory"));
172
ret = UpnpSetWebServerRootDir(web_root.c_str());
173
if (ret != UPNP_E_SUCCESS)
175
throw _UpnpException(ret, _("upnp_init: UpnpSetWebServerRootDir failed"));
178
log_debug("webroot: %s\n", web_root.c_str());
180
Ref<Array<StringBase> > arr = config->getStringArrayOption(CFG_SERVER_CUSTOM_HTTP_HEADERS);
185
for (int i = 0; i < arr->size(); i++)
190
log_info("Adding HTTP header \"%s\"\n", tmp.c_str());
191
ret = UpnpAddCustomHTTPHeader(tmp.c_str());
192
if (ret != UPNP_E_SUCCESS)
194
throw _UpnpException(ret, _("upnp_init: UpnpAddCustomHTTPHeader failed"));
200
ret = UpnpAddVirtualDir(virtual_directory.c_str());
201
if (ret != UPNP_E_SUCCESS)
203
throw _UpnpException(ret, _("upnp_init: UpnpAddVirtualDir failed"));
206
ret = register_web_callbacks();
207
if (ret != UPNP_E_SUCCESS)
209
throw _UpnpException(ret, _("upnp_init: UpnpSetVirtualDirCallbacks failed"));
212
String presentationURL = config->getOption(CFG_SERVER_PRESENTATION_URL);
213
if (!string_ok(presentationURL))
215
presentationURL = _("http://") + ip + ":" + port + "/";
220
config->getOption(CFG_SERVER_APPEND_PRESENTATION_URL_TO);
221
if (appendto == "ip")
223
presentationURL = _("http://") + ip + ":" + presentationURL;
225
else if (appendto == "port")
227
presentationURL = _("http://") + ip + ":" + port + "/" +
229
} // else appendto is none and we take the URL as it entered by user
232
// register root device with the library
233
String device_description =
234
_("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") +
235
UpnpXML_RenderDeviceDescription(presentationURL)->print();
237
// log_debug("DEVICE DESCRIPTION: \n%s\n", device_description.c_str());
239
// register root device with the library
240
ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, device_description.c_str(),
241
device_description.length() + 1, true,
242
static_upnp_callback,
245
if (ret != UPNP_E_SUCCESS)
247
throw _UpnpException(ret, _("upnp_init: UpnpRegisterRootDevice failed"));
250
// now unregister in order to cleanup previous instances
251
// that might still be there due to crash/unclean shutdown/network interruptions
252
ret = UpnpUnRegisterRootDevice(device_handle);
253
if (ret != UPNP_E_SUCCESS) {
254
throw _UpnpException(ret, _("upnp_init: unregistering failed"));
257
// and register again, we should be clean now
258
ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, device_description.c_str(),
259
device_description.length() + 1, true,
260
static_upnp_callback,
263
if (ret != UPNP_E_SUCCESS)
265
throw _UpnpException(ret, _("upnp_init: UpnpRegisterRootDevice failed"));
269
// send advertisements every 180secs
270
ret = UpnpSendAdvertisement(device_handle, alive_advertisement);
271
if (ret != UPNP_E_SUCCESS)
273
throw _UpnpException(ret, _("upnp_init: UpnpSendAdvertisement failed"));
276
// initializing UpdateManager
277
UpdateManager::getInstance();
279
// initializing ContentManager
280
ContentManager::getInstance();
282
config->writeBookmark(ip, String::from(port));
283
log_info("MediaTomb Web UI can be reached by following this link:\n");
284
log_info("http://%s:%d/\n", ip.c_str(), port);
289
bool Server::getShutdownStatus()
291
return server_shutdown_flag;
294
void Server::shutdown()
296
int ret = 0; // return code
299
ContentManager::getInstance()->shutdown();
300
UpdateManager::getInstance()->shutdown();
301
Storage::getInstance()->shutdown();
304
server_shutdown_flag = true;
306
log_debug("start\n");
310
ret = UpnpUnRegisterRootDevice(device_handle);
311
if (ret != UPNP_E_SUCCESS)
313
throw _UpnpException(ret, _("upnp_cleanup: UpnpUnRegisterRootDevice failed"));
316
log_debug("now calling upnp finish\n");
323
String Server::getVirtualURL()
329
int Server::upnp_callback(Upnp_EventType eventtype, void *event, void *cookie)
331
int ret = UPNP_E_SUCCESS; // general purpose return code
333
log_debug("start\n");
337
log_debug("upnp_callback: NULL event structure\n");
338
return UPNP_E_BAD_REQUEST;
341
// log_info("event is ok\n");
342
// get device wide mutex (have to figure out what the hell that is)
345
// log_info("got device mutex\n");
347
// dispatch event based on event type
350
case UPNP_CONTROL_ACTION_REQUEST:
351
// a CP is invoking an action
352
// log_info("UPNP_CONTROL_ACTION_REQUEST\n");
355
Ref<ActionRequest> request(new ActionRequest((struct Upnp_Action_Request *)event));
356
upnp_actions(request);
358
// set in update() ((struct Upnp_Action_Request *)event)->ErrCode = ret;
360
catch(UpnpException upnp_e)
362
ret = upnp_e.getErrorCode();
363
((struct Upnp_Action_Request *)event)->ErrCode = ret;
367
log_info("Exception: %s\n", e.getMessage().c_str());
372
case UPNP_EVENT_SUBSCRIPTION_REQUEST:
373
// a cp wants a subscription
374
// log_info("UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
377
Ref<SubscriptionRequest> request(new SubscriptionRequest((struct Upnp_Subscription_Request *)event));
378
upnp_subscriptions(request);
380
catch(UpnpException upnp_e)
382
ret = upnp_e.getErrorCode();
388
// unhandled event type
389
log_warning("unsupported event type: %d\n", eventtype);
390
ret = UPNP_E_BAD_REQUEST;
394
log_debug("returning %d\n", ret);
398
UpnpDevice_Handle Server::getDeviceHandle()
400
return device_handle;
403
zmm::String Server::getIP()
405
return String(UpnpGetServerIpAddress());
408
zmm::String Server::getPort()
410
return String::from(UpnpGetServerPort());
413
void Server::upnp_actions(Ref<ActionRequest> request)
415
log_debug("start\n");
417
// make sure the request is for our device
418
if (request->getUDN() != serverUDN)
421
throw _UpnpException(UPNP_E_BAD_REQUEST,
422
_("upnp_actions: request not for this device"));
425
// we need to match the serviceID to one of our services
426
if (request->getServiceID() == DESC_CM_SERVICE_ID)
428
// this call is for the lifetime stats service
429
// log_debug("request for connection manager service\n");
430
cmgr->process_action_request(request);
432
else if (request->getServiceID() == DESC_CDS_SERVICE_ID)
434
// this call is for the toaster control service
435
// log_debug("upnp_actions: request for content directory service\n");
436
cds->process_action_request(request);
438
#if defined(ENABLE_MRREG)
439
else if (request->getServiceID() == DESC_MRREG_SERVICE_ID)
441
mrreg->process_action_request(request);
446
// cp is asking for a nonexistent service, or for a service
447
// that does not support any actions
448
throw _UpnpException(UPNP_E_BAD_REQUEST,
449
_("Service does not exist or action not supported"));
454
void Server::upnp_subscriptions(Ref<SubscriptionRequest> request)
456
// make sure that the request is for our device
457
if (request->getUDN() != serverUDN)
460
// log_debug("upnp_subscriptions: request not for this device\n");
461
throw _UpnpException(UPNP_E_BAD_REQUEST,
462
_("upnp_actions: request not for this device"));
465
// we need to match the serviceID to one of our services
467
if (request->getServiceID() == DESC_CDS_SERVICE_ID)
469
// this call is for the content directory service
470
// log_debug("upnp_subscriptions: request for content directory service\n");
471
cds->process_subscription_request(request);
473
else if (request->getServiceID() == DESC_CM_SERVICE_ID)
475
// this call is for the connection manager service
476
// log_debug("upnp_subscriptions: request for connection manager service\n");
477
cmgr->process_subscription_request(request);
479
#if defined(ENABLE_MRREG)
480
else if (request->getServiceID() == DESC_MRREG_SERVICE_ID)
482
mrreg->process_subscription_request(request);
487
// cp asks for a nonexistent service or for a service that
488
// does not support subscriptions
489
throw _UpnpException(UPNP_E_BAD_REQUEST,
490
_("Service does not exist or subscriptions not supported"));