~vanvugt/ubuntu/oneiric/mediatomb/fix-770964-784431

« back to all changes in this revision

Viewing changes to src/server.cc

  • Committer: Bazaar Package Importer
  • Author(s): Andres Mejia
  • Date: 2008-02-02 01:42:48 UTC
  • Revision ID: james.westby@ubuntu.com-20080202014248-cjouolddb8gi2zkz
Tags: upstream-0.10.0.dfsg1
ImportĀ upstreamĀ versionĀ 0.10.0.dfsg1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*MT*
 
2
    
 
3
    MediaTomb - http://www.mediatomb.cc/
 
4
    
 
5
    server.cc - this file is part of MediaTomb.
 
6
    
 
7
    Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
 
8
                       Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
 
9
    
 
10
    Copyright (C) 2006-2007 Gena Batyan <bgeradz@mediatomb.cc>,
 
11
                            Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
 
12
                            Leonhard Wimmer <leo@mediatomb.cc>
 
13
    
 
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.
 
17
    
 
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.
 
22
    
 
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.
 
26
    
 
27
    $Id: server.cc 1356 2007-06-12 22:59:19Z jin_eld $
 
28
*/
 
29
 
 
30
/// \file server.cc
 
31
 
 
32
#ifdef HAVE_CONFIG_H
 
33
    #include "autoconfig.h"
 
34
#endif
 
35
 
 
36
#include "server.h"
 
37
#include "web_callbacks.h"
 
38
#include "content_manager.h"
 
39
#include "update_manager.h"
 
40
#include "dictionary.h"
 
41
#include "upnp_xml.h"
 
42
#include "tools.h"
 
43
 
 
44
using namespace zmm;
 
45
using namespace mxml;
 
46
 
 
47
Ref<Storage> Server::storage = nil;
 
48
 
 
49
SINGLETON_MUTEX(Server, false);
 
50
 
 
51
static int static_upnp_callback(Upnp_EventType eventtype, void *event, void *cookie)
 
52
{
 
53
    return Server::getInstance()->upnp_callback(eventtype, event, cookie);
 
54
}
 
55
 
 
56
void Server::static_cleanup_callback()
 
57
{
 
58
    if (storage != nil)
 
59
    {
 
60
        try
 
61
        {
 
62
            storage->threadCleanup();
 
63
        }
 
64
        catch (Exception ex) {}
 
65
    }
 
66
}
 
67
 
 
68
Server::Server() : Singleton<Server>()
 
69
{
 
70
    server_shutdown_flag = false;
 
71
}
 
72
 
 
73
void Server::init()
 
74
{
 
75
    virtual_directory = _(SERVER_VIRTUAL_DIR); 
 
76
    
 
77
    ContentDirectoryService::setStaticArgs(_(DESC_CDS_SERVICE_TYPE),
 
78
                                           _(DESC_CDS_SERVICE_ID));
 
79
    cds = ContentDirectoryService::getInstance();
 
80
                                                 
 
81
    ConnectionManagerService::setStaticArgs(_(DESC_CM_SERVICE_TYPE),
 
82
                                            _(DESC_CM_SERVICE_ID));
 
83
    cmgr = ConnectionManagerService::getInstance();
 
84
 
 
85
#if defined(ENABLE_MRREG)                                                    
 
86
    MRRegistrarService::setStaticArgs(_(DESC_MRREG_SERVICE_TYPE),
 
87
                                      _(DESC_MRREG_SERVICE_ID));
 
88
    mrreg = MRRegistrarService::getInstance();
 
89
#endif
 
90
 
 
91
    Ref<ConfigManager> config = ConfigManager::getInstance();
 
92
    
 
93
    serverUDN = config->getOption(CFG_SERVER_UDN);
 
94
    alive_advertisement = config->getIntOption(CFG_SERVER_ALIVE_INTERVAL);
 
95
}
 
96
 
 
97
void Server::upnp_init(String iface, String ip_address, int port)
 
98
{
 
99
    int ret = 0;        // general purpose error code
 
100
    String ip;
 
101
 
 
102
    log_debug("start\n");
 
103
 
 
104
    Ref<ConfigManager> config = ConfigManager::getInstance();
 
105
 
 
106
    if (!string_ok(iface))
 
107
        iface = config->getOption(CFG_SERVER_NETWORK_INTERFACE);
 
108
 
 
109
    if (!string_ok(ip_address))
 
110
        ip = config->getOption(CFG_SERVER_IP);
 
111
    else
 
112
        ip = ip_address;
 
113
 
 
114
    if (string_ok(ip) && string_ok(iface))
 
115
        throw _Exception(_("You can not specify interface and IP at the same time!"));
 
116
 
 
117
 
 
118
    if (!string_ok(ip))
 
119
        ip = interfaceToIP(iface);
 
120
 
 
121
    if (string_ok(iface) && !string_ok(ip))
 
122
        throw _Exception(_("Could not find interface: ") + iface);
 
123
 
 
124
    log_debug("interface: %s ip: %s\n", iface.c_str(), ip.c_str());
 
125
 
 
126
 
 
127
    if (port < 0)
 
128
    {
 
129
        port = config->getIntOption(CFG_SERVER_PORT);
 
130
    }
 
131
 
 
132
    if (port < 0)
 
133
        port = 0;
 
134
 
 
135
 
 
136
    void *cb = NULL;
 
137
    // this is important, so the storage lives a little longer when
 
138
    // shutdown is initiated
 
139
    storage = Storage::getInstance();
 
140
 
 
141
    if (storage->threadCleanupRequired())
 
142
        cb = (void *)static_cleanup_callback;
 
143
 
 
144
    ret = UpnpInit(ip.c_str(), port, config->getIntOption(CFG_SERVER_RETRIES_ON_TIMEOUT), cb);
 
145
 
 
146
    if (ret != UPNP_E_SUCCESS)
 
147
    {
 
148
        throw _UpnpException(ret, _("upnp_init: UpnpInit failed"));
 
149
    }
 
150
 
 
151
    port = UpnpGetServerPort();
 
152
 
 
153
    log_info("Initialized port: %d\n", port);
 
154
 
 
155
    if (!string_ok(ip))
 
156
    {
 
157
        ip = String(UpnpGetServerIpAddress());
 
158
    }
 
159
 
 
160
    log_info("Server bound to: %s\n", ip.c_str());
 
161
 
 
162
    virtual_url = _("http://") + ip + ":" + port + "/" + virtual_directory;
 
163
 
 
164
    // next set webroot directory
 
165
    String web_root = config->getOption(CFG_SERVER_WEBROOT);
 
166
 
 
167
    if (!string_ok(web_root))
 
168
    {
 
169
        throw _Exception(_("invalid web server root directory"));
 
170
    }
 
171
    
 
172
    ret = UpnpSetWebServerRootDir(web_root.c_str());
 
173
    if (ret != UPNP_E_SUCCESS)
 
174
    {
 
175
        throw _UpnpException(ret, _("upnp_init: UpnpSetWebServerRootDir failed"));
 
176
    }
 
177
 
 
178
    log_debug("webroot: %s\n", web_root.c_str()); 
 
179
 
 
180
        Ref<Array<StringBase> > arr = config->getStringArrayOption(CFG_SERVER_CUSTOM_HTTP_HEADERS);
 
181
 
 
182
        if (arr != nil)
 
183
        {
 
184
            String tmp;
 
185
            for (int i = 0; i < arr->size(); i++)
 
186
            {
 
187
                tmp = arr->get(i);
 
188
                if (string_ok(tmp))
 
189
                {
 
190
                    log_info("Adding HTTP header \"%s\"\n", tmp.c_str());
 
191
                    ret = UpnpAddCustomHTTPHeader(tmp.c_str());
 
192
                    if (ret != UPNP_E_SUCCESS)
 
193
                    {
 
194
                        throw _UpnpException(ret, _("upnp_init: UpnpAddCustomHTTPHeader failed"));
 
195
                    }
 
196
                }
 
197
            }
 
198
        }
 
199
 
 
200
    ret = UpnpAddVirtualDir(virtual_directory.c_str());
 
201
    if (ret != UPNP_E_SUCCESS)
 
202
    {
 
203
        throw _UpnpException(ret, _("upnp_init: UpnpAddVirtualDir failed"));
 
204
    }
 
205
 
 
206
    ret = register_web_callbacks();
 
207
    if (ret != UPNP_E_SUCCESS)
 
208
    {
 
209
        throw _UpnpException(ret, _("upnp_init: UpnpSetVirtualDirCallbacks failed"));
 
210
    }
 
211
 
 
212
    String presentationURL =  config->getOption(CFG_SERVER_PRESENTATION_URL);
 
213
    if (!string_ok(presentationURL))
 
214
    {
 
215
        presentationURL = _("http://") + ip + ":" + port + "/";
 
216
    }
 
217
    else
 
218
    {
 
219
        String appendto = 
 
220
           config->getOption(CFG_SERVER_APPEND_PRESENTATION_URL_TO);
 
221
        if (appendto == "ip")
 
222
        {
 
223
            presentationURL = _("http://") + ip + ":" + presentationURL;
 
224
        }
 
225
        else if (appendto == "port")
 
226
        {
 
227
            presentationURL = _("http://") + ip + ":" + port + "/" + 
 
228
                              presentationURL;
 
229
        } // else appendto is none and we take the URL as it entered by user
 
230
    }
 
231
 
 
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();
 
236
 
 
237
//    log_debug("DEVICE DESCRIPTION: \n%s\n", device_description.c_str());
 
238
 
 
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,
 
243
                                  &device_handle,
 
244
                                  &device_handle);
 
245
    if (ret != UPNP_E_SUCCESS)
 
246
    {
 
247
        throw _UpnpException(ret, _("upnp_init: UpnpRegisterRootDevice failed"));
 
248
    }
 
249
 
 
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"));
 
255
    }
 
256
    
 
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,
 
261
                                  &device_handle,
 
262
                                  &device_handle);
 
263
    if (ret != UPNP_E_SUCCESS)
 
264
    {
 
265
        throw _UpnpException(ret, _("upnp_init: UpnpRegisterRootDevice failed"));
 
266
    }
 
267
 
 
268
 
 
269
    // send advertisements every 180secs
 
270
    ret = UpnpSendAdvertisement(device_handle, alive_advertisement);
 
271
    if (ret != UPNP_E_SUCCESS)
 
272
    {
 
273
        throw _UpnpException(ret, _("upnp_init: UpnpSendAdvertisement failed"));
 
274
    }
 
275
       
 
276
    // initializing UpdateManager
 
277
    UpdateManager::getInstance();
 
278
    
 
279
    // initializing ContentManager
 
280
    ContentManager::getInstance();
 
281
    
 
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);
 
285
    
 
286
    log_debug("end\n");
 
287
}
 
288
 
 
289
bool Server::getShutdownStatus()
 
290
{
 
291
    return server_shutdown_flag;
 
292
}
 
293
 
 
294
void Server::shutdown()
 
295
{
 
296
    int ret = 0; // return code
 
297
    
 
298
    /*
 
299
    ContentManager::getInstance()->shutdown();
 
300
    UpdateManager::getInstance()->shutdown();
 
301
    Storage::getInstance()->shutdown();
 
302
    */
 
303
    
 
304
    server_shutdown_flag = true;
 
305
    
 
306
    log_debug("start\n");
 
307
    
 
308
    // unregister device
 
309
    
 
310
    ret = UpnpUnRegisterRootDevice(device_handle);
 
311
    if (ret != UPNP_E_SUCCESS) 
 
312
    {   
 
313
        throw _UpnpException(ret, _("upnp_cleanup: UpnpUnRegisterRootDevice failed"));
 
314
    }
 
315
    
 
316
    log_debug("now calling upnp finish\n");
 
317
    UpnpFinish();
 
318
    storage = nil;
 
319
 
 
320
    log_debug("end\n");
 
321
}
 
322
 
 
323
String Server::getVirtualURL()
 
324
{
 
325
    return virtual_url;
 
326
}
 
327
 
 
328
 
 
329
int Server::upnp_callback(Upnp_EventType eventtype, void *event, void *cookie)
 
330
{
 
331
    int ret = UPNP_E_SUCCESS; // general purpose return code
 
332
 
 
333
    log_debug("start\n");
 
334
 
 
335
    // check parameters
 
336
    if (event == NULL) {
 
337
        log_debug("upnp_callback: NULL event structure\n");
 
338
        return UPNP_E_BAD_REQUEST;
 
339
    }
 
340
 
 
341
//    log_info("event is ok\n");
 
342
    // get device wide mutex (have to figure out what the hell that is)
 
343
    AUTOLOCK(mutex);
 
344
 
 
345
//    log_info("got device mutex\n");
 
346
 
 
347
    // dispatch event based on event type
 
348
    switch (eventtype) {
 
349
 
 
350
        case UPNP_CONTROL_ACTION_REQUEST:
 
351
            // a CP is invoking an action
 
352
//            log_info("UPNP_CONTROL_ACTION_REQUEST\n");
 
353
            try
 
354
            {
 
355
                Ref<ActionRequest> request(new ActionRequest((struct Upnp_Action_Request *)event));
 
356
                upnp_actions(request);
 
357
                request->update();
 
358
               // set in update() ((struct Upnp_Action_Request *)event)->ErrCode = ret;
 
359
            }
 
360
            catch(UpnpException upnp_e)
 
361
            {
 
362
                ret = upnp_e.getErrorCode();
 
363
                ((struct Upnp_Action_Request *)event)->ErrCode = ret;
 
364
            }
 
365
            catch(Exception e)
 
366
            {
 
367
                log_info("Exception: %s\n", e.getMessage().c_str());
 
368
            }
 
369
            
 
370
            break;
 
371
            
 
372
        case UPNP_EVENT_SUBSCRIPTION_REQUEST:
 
373
            // a cp wants a subscription
 
374
//            log_info("UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
 
375
            try
 
376
            {
 
377
                Ref<SubscriptionRequest> request(new SubscriptionRequest((struct Upnp_Subscription_Request *)event));
 
378
                upnp_subscriptions(request);
 
379
            }
 
380
            catch(UpnpException upnp_e)
 
381
            {
 
382
                ret = upnp_e.getErrorCode();
 
383
            }
 
384
            
 
385
            break;
 
386
            
 
387
        default:
 
388
            // unhandled event type
 
389
            log_warning("unsupported event type: %d\n", eventtype);
 
390
            ret = UPNP_E_BAD_REQUEST;
 
391
            break;
 
392
    }
 
393
    
 
394
    log_debug("returning %d\n", ret);
 
395
    return ret;
 
396
}
 
397
 
 
398
UpnpDevice_Handle Server::getDeviceHandle()
 
399
{
 
400
    return device_handle; 
 
401
}
 
402
 
 
403
zmm::String Server::getIP()
 
404
{
 
405
    return String(UpnpGetServerIpAddress());
 
406
}
 
407
 
 
408
zmm::String Server::getPort()
 
409
{
 
410
    return String::from(UpnpGetServerPort());
 
411
}
 
412
 
 
413
void Server::upnp_actions(Ref<ActionRequest> request)
 
414
{
 
415
    log_debug("start\n");
 
416
 
 
417
    // make sure the request is for our device
 
418
    if (request->getUDN() != serverUDN)
 
419
    {
 
420
        // not for us
 
421
        throw _UpnpException(UPNP_E_BAD_REQUEST, 
 
422
                            _("upnp_actions: request not for this device"));
 
423
    }
 
424
 
 
425
    // we need to match the serviceID to one of our services
 
426
    if (request->getServiceID() == DESC_CM_SERVICE_ID)
 
427
    {
 
428
        // this call is for the lifetime stats service
 
429
//        log_debug("request for connection manager service\n");
 
430
        cmgr->process_action_request(request);
 
431
    } 
 
432
    else if (request->getServiceID() == DESC_CDS_SERVICE_ID) 
 
433
    {
 
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);
 
437
    } 
 
438
#if defined(ENABLE_MRREG)
 
439
    else if (request->getServiceID() == DESC_MRREG_SERVICE_ID)
 
440
    {
 
441
        mrreg->process_action_request(request);
 
442
    }
 
443
#endif
 
444
    else 
 
445
    {
 
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"));
 
450
    }
 
451
}
 
452
 
 
453
 
 
454
void Server::upnp_subscriptions(Ref<SubscriptionRequest> request)
 
455
{
 
456
    // make sure that the request is for our device
 
457
    if (request->getUDN() != serverUDN)
 
458
    {
 
459
        // not for us
 
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"));
 
463
    }
 
464
                                                             
 
465
    // we need to match the serviceID to one of our services
 
466
 
 
467
    if (request->getServiceID() == DESC_CDS_SERVICE_ID)
 
468
    {
 
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);
 
472
    }
 
473
    else if (request->getServiceID() == DESC_CM_SERVICE_ID)
 
474
    {
 
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);
 
478
    }
 
479
#if defined(ENABLE_MRREG)
 
480
    else if (request->getServiceID() == DESC_MRREG_SERVICE_ID)
 
481
    {
 
482
        mrreg->process_subscription_request(request);
 
483
    }
 
484
#endif
 
485
    else 
 
486
    {
 
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"));
 
491
    }
 
492
}