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 #ifdef HAVE_CURL
00037 #include <curl/curl.h>
00038 #endif
00039
00040 #ifdef HAVE_LASTFMLIB
00041 #include "lastfm_scrobbler.h"
00042 #endif
00043
00044 #include "server.h"
00045 #include "web_callbacks.h"
00046 #include "content_manager.h"
00047 #include "update_manager.h"
00048 #include "dictionary.h"
00049 #include "upnp_xml.h"
00050 #include "tools.h"
00051
00052 using namespace zmm;
00053 using namespace mxml;
00054
00055 Ref<Storage> Server::storage = nil;
00056
00057 SINGLETON_MUTEX(Server, false);
00058
00059 static int static_upnp_callback(Upnp_EventType eventtype, void *event, void *cookie)
00060 {
00061 return Server::getInstance()->upnp_callback(eventtype, event, cookie);
00062 }
00063
00064 void Server::static_cleanup_callback()
00065 {
00066 if (storage != nil)
00067 {
00068 try
00069 {
00070 storage->threadCleanup();
00071 }
00072 catch (Exception ex) {}
00073 }
00074 }
00075
00076 Server::Server() : Singleton<Server>()
00077 {
00078 server_shutdown_flag = false;
00079 }
00080
00081 void Server::init()
00082 {
00083 virtual_directory = _(SERVER_VIRTUAL_DIR);
00084
00085 ContentDirectoryService::setStaticArgs(_(DESC_CDS_SERVICE_TYPE),
00086 _(DESC_CDS_SERVICE_ID));
00087 cds = ContentDirectoryService::getInstance();
00088
00089 ConnectionManagerService::setStaticArgs(_(DESC_CM_SERVICE_TYPE),
00090 _(DESC_CM_SERVICE_ID));
00091 cmgr = ConnectionManagerService::getInstance();
00092
00093 #if defined(ENABLE_MRREG)
00094 MRRegistrarService::setStaticArgs(_(DESC_MRREG_SERVICE_TYPE),
00095 _(DESC_MRREG_SERVICE_ID));
00096 mrreg = MRRegistrarService::getInstance();
00097 #endif
00098
00099 Ref<ConfigManager> config = ConfigManager::getInstance();
00100
00101 serverUDN = config->getOption(CFG_SERVER_UDN);
00102 alive_advertisement = config->getIntOption(CFG_SERVER_ALIVE_INTERVAL);
00103
00104 #ifdef HAVE_CURL
00105 curl_global_init(CURL_GLOBAL_ALL);
00106 #endif
00107
00108 #ifdef HAVE_LASTFMLIB
00109 LastFm::getInstance();
00110 #endif
00111 }
00112
00113 void Server::upnp_init(String iface, String ip_address, int port)
00114 {
00115 int ret = 0;
00116 String ip;
00117
00118 log_debug("start\n");
00119
00120 Ref<ConfigManager> config = ConfigManager::getInstance();
00121
00122 if (!string_ok(iface))
00123 iface = config->getOption(CFG_SERVER_NETWORK_INTERFACE);
00124
00125 if (!string_ok(ip_address))
00126 ip = config->getOption(CFG_SERVER_IP);
00127 else
00128 ip = ip_address;
00129
00130 if (string_ok(ip) && string_ok(iface))
00131 throw _Exception(_("You can not specify interface and IP at the same time!"));
00132
00133
00134 if (!string_ok(ip))
00135 ip = interfaceToIP(iface);
00136
00137 if (string_ok(iface) && !string_ok(ip))
00138 throw _Exception(_("Could not find interface: ") + iface);
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152 if (port < 0)
00153 {
00154 port = config->getIntOption(CFG_SERVER_PORT);
00155 }
00156
00157 if (port < 0)
00158 port = 0;
00159
00160
00161 void *cb = NULL;
00162
00163
00164 storage = Storage::getInstance();
00165
00166 if (storage->threadCleanupRequired())
00167 cb = (void *)static_cleanup_callback;
00168
00169 ret = UpnpInit(ip.c_str(), port, 0, cb);
00170
00171 if (ret != UPNP_E_SUCCESS)
00172 {
00173 throw _UpnpException(ret, _("upnp_init: UpnpInit failed"));
00174 }
00175
00176 port = UpnpGetServerPort();
00177
00178 log_info("Initialized port: %d\n", port);
00179
00180 if (!string_ok(ip))
00181 {
00182 ip = UpnpGetServerIpAddress();
00183 }
00184
00185 log_info("Server bound to: %s\n", ip.c_str());
00186
00187 virtual_url = _("http://") + ip + ":" + port + "/" + virtual_directory;
00188
00189
00190 String web_root = config->getOption(CFG_SERVER_WEBROOT);
00191
00192 if (!string_ok(web_root))
00193 {
00194 throw _Exception(_("invalid web server root directory"));
00195 }
00196
00197 ret = UpnpSetWebServerRootDir(web_root.c_str());
00198 if (ret != UPNP_E_SUCCESS)
00199 {
00200 throw _UpnpException(ret, _("upnp_init: UpnpSetWebServerRootDir failed"));
00201 }
00202
00203 log_debug("webroot: %s\n", web_root.c_str());
00204
00205 Ref<Array<StringBase> > arr = config->getStringArrayOption(CFG_SERVER_CUSTOM_HTTP_HEADERS);
00206
00207 if (arr != nil)
00208 {
00209 String tmp;
00210 for (int i = 0; i < arr->size(); i++)
00211 {
00212 tmp = arr->get(i);
00213 if (string_ok(tmp))
00214 {
00215 log_info("Adding HTTP header \"%s\"\n", tmp.c_str());
00216 ret = UpnpAddCustomHTTPHeader(tmp.c_str());
00217 if (ret != UPNP_E_SUCCESS)
00218 {
00219 throw _UpnpException(ret, _("upnp_init: UpnpAddCustomHTTPHeader failed"));
00220 }
00221 }
00222 }
00223 }
00224
00225 ret = UpnpAddVirtualDir(virtual_directory.c_str());
00226 if (ret != UPNP_E_SUCCESS)
00227 {
00228 throw _UpnpException(ret, _("upnp_init: UpnpAddVirtualDir failed"));
00229 }
00230
00231 ret = register_web_callbacks();
00232 if (ret != UPNP_E_SUCCESS)
00233 {
00234 throw _UpnpException(ret, _("upnp_init: UpnpSetVirtualDirCallbacks failed"));
00235 }
00236
00237 String presentationURL = config->getOption(CFG_SERVER_PRESENTATION_URL);
00238 if (!string_ok(presentationURL))
00239 {
00240 presentationURL = _("http://") + ip + ":" + port + "/";
00241 }
00242 else
00243 {
00244 String appendto =
00245 config->getOption(CFG_SERVER_APPEND_PRESENTATION_URL_TO);
00246 if (appendto == "ip")
00247 {
00248 presentationURL = _("http://") + ip + ":" + presentationURL;
00249 }
00250 else if (appendto == "port")
00251 {
00252 presentationURL = _("http://") + ip + ":" + port + "/" +
00253 presentationURL;
00254 }
00255 }
00256
00257
00258 String device_description =
00259 _("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") +
00260 UpnpXML_RenderDeviceDescription(presentationURL)->print();
00261
00262
00263
00264
00265 ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, device_description.c_str(),
00266 device_description.length() + 1, true,
00267 static_upnp_callback,
00268 &device_handle,
00269 &device_handle);
00270 if (ret != UPNP_E_SUCCESS)
00271 {
00272 throw _UpnpException(ret, _("upnp_init: UpnpRegisterRootDevice failed"));
00273 }
00274
00275
00276
00277 ret = UpnpUnRegisterRootDevice(device_handle);
00278 if (ret != UPNP_E_SUCCESS) {
00279 throw _UpnpException(ret, _("upnp_init: unregistering failed"));
00280 }
00281
00282
00283 ret = UpnpRegisterRootDevice2(UPNPREG_BUF_DESC, device_description.c_str(),
00284 device_description.length() + 1, true,
00285 static_upnp_callback,
00286 &device_handle,
00287 &device_handle);
00288 if (ret != UPNP_E_SUCCESS)
00289 {
00290 throw _UpnpException(ret, _("upnp_init: UpnpRegisterRootDevice failed"));
00291 }
00292
00293
00294
00295 ret = UpnpSendAdvertisement(device_handle, alive_advertisement);
00296 if (ret != UPNP_E_SUCCESS)
00297 {
00298 throw _UpnpException(ret, _("upnp_init: UpnpSendAdvertisement failed"));
00299 }
00300
00301
00302 UpdateManager::getInstance();
00303
00304
00305 ContentManager::getInstance();
00306
00307 config->writeBookmark(ip, String::from(port));
00308 log_info("MediaTomb Web UI can be reached by following this link:\n");
00309 log_info("http://%s:%d/\n", ip.c_str(), port);
00310
00311 log_debug("end\n");
00312 }
00313
00314 bool Server::getShutdownStatus()
00315 {
00316 return server_shutdown_flag;
00317 }
00318
00319 void Server::shutdown()
00320 {
00321 int ret = 0;
00322
00323
00324
00325
00326
00327
00328
00329 server_shutdown_flag = true;
00330
00331 log_debug("start\n");
00332
00333
00334
00335 ret = UpnpUnRegisterRootDevice(device_handle);
00336 if (ret != UPNP_E_SUCCESS)
00337 {
00338 throw _UpnpException(ret, _("upnp_cleanup: UpnpUnRegisterRootDevice failed"));
00339 }
00340
00341 #ifdef HAVE_CURL
00342 curl_global_cleanup();
00343 #endif
00344
00345 log_debug("now calling upnp finish\n");
00346 UpnpFinish();
00347 storage = nil;
00348
00349 log_debug("end\n");
00350 }
00351
00352 String Server::getVirtualURL()
00353 {
00354 return virtual_url;
00355 }
00356
00357
00358 int Server::upnp_callback(Upnp_EventType eventtype, void *event, void *cookie)
00359 {
00360 int ret = UPNP_E_SUCCESS;
00361
00362 log_debug("start\n");
00363
00364
00365 if (event == NULL) {
00366 log_debug("upnp_callback: NULL event structure\n");
00367 return UPNP_E_BAD_REQUEST;
00368 }
00369
00370
00371
00372 AUTOLOCK(mutex);
00373
00374
00375
00376
00377 switch (eventtype) {
00378
00379 case UPNP_CONTROL_ACTION_REQUEST:
00380
00381
00382 try
00383 {
00384 Ref<ActionRequest> request(new ActionRequest((struct Upnp_Action_Request *)event));
00385 upnp_actions(request);
00386 request->update();
00387
00388 }
00389 catch(UpnpException upnp_e)
00390 {
00391 ret = upnp_e.getErrorCode();
00392 ((struct Upnp_Action_Request *)event)->ErrCode = ret;
00393 }
00394 catch(Exception e)
00395 {
00396 log_info("Exception: %s\n", e.getMessage().c_str());
00397 }
00398
00399 break;
00400
00401 case UPNP_EVENT_SUBSCRIPTION_REQUEST:
00402
00403
00404 try
00405 {
00406 Ref<SubscriptionRequest> request(new SubscriptionRequest((struct Upnp_Subscription_Request *)event));
00407 upnp_subscriptions(request);
00408 }
00409 catch(UpnpException upnp_e)
00410 {
00411 ret = upnp_e.getErrorCode();
00412 }
00413
00414 break;
00415
00416 default:
00417
00418 log_warning("unsupported event type: %d\n", eventtype);
00419 ret = UPNP_E_BAD_REQUEST;
00420 break;
00421 }
00422
00423 log_debug("returning %d\n", ret);
00424 return ret;
00425 }
00426
00427 UpnpDevice_Handle Server::getDeviceHandle()
00428 {
00429 return device_handle;
00430 }
00431
00432 zmm::String Server::getIP()
00433 {
00434 return UpnpGetServerIpAddress();
00435 }
00436
00437 zmm::String Server::getPort()
00438 {
00439 return String::from(UpnpGetServerPort());
00440 }
00441
00442 void Server::upnp_actions(Ref<ActionRequest> request)
00443 {
00444 log_debug("start\n");
00445
00446
00447 if (request->getUDN() != serverUDN)
00448 {
00449
00450 throw _UpnpException(UPNP_E_BAD_REQUEST,
00451 _("upnp_actions: request not for this device"));
00452 }
00453
00454
00455 if (request->getServiceID() == DESC_CM_SERVICE_ID)
00456 {
00457
00458
00459 cmgr->process_action_request(request);
00460 }
00461 else if (request->getServiceID() == DESC_CDS_SERVICE_ID)
00462 {
00463
00464
00465 cds->process_action_request(request);
00466 }
00467 #if defined(ENABLE_MRREG)
00468 else if (request->getServiceID() == DESC_MRREG_SERVICE_ID)
00469 {
00470 mrreg->process_action_request(request);
00471 }
00472 #endif
00473 else
00474 {
00475
00476
00477 throw _UpnpException(UPNP_E_BAD_REQUEST,
00478 _("Service does not exist or action not supported"));
00479 }
00480 }
00481
00482
00483 void Server::upnp_subscriptions(Ref<SubscriptionRequest> request)
00484 {
00485
00486 if (request->getUDN() != serverUDN)
00487 {
00488
00489
00490 throw _UpnpException(UPNP_E_BAD_REQUEST,
00491 _("upnp_actions: request not for this device"));
00492 }
00493
00494
00495
00496 if (request->getServiceID() == DESC_CDS_SERVICE_ID)
00497 {
00498
00499
00500 cds->process_subscription_request(request);
00501 }
00502 else if (request->getServiceID() == DESC_CM_SERVICE_ID)
00503 {
00504
00505
00506 cmgr->process_subscription_request(request);
00507 }
00508 #if defined(ENABLE_MRREG)
00509 else if (request->getServiceID() == DESC_MRREG_SERVICE_ID)
00510 {
00511 mrreg->process_subscription_request(request);
00512 }
00513 #endif
00514 else
00515 {
00516
00517
00518 throw _UpnpException(UPNP_E_BAD_REQUEST,
00519 _("Service does not exist or subscriptions not supported"));
00520 }
00521 }