mms_dispatcher.c 20.5 KB
Newer Older
Slava Monich's avatar
Slava Monich committed
1
/*
2
 * Copyright (C) 2013-2016 Jolla Ltd.
3
 * Contact: Slava Monich <slava.monich@jolla.com>
Slava Monich's avatar
Slava Monich committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include "mms_dispatcher.h"
#include "mms_handler.h"
18
#include "mms_settings.h"
Slava Monich's avatar
Slava Monich committed
19 20 21 22 23 24 25 26 27 28 29 30
#include "mms_connection.h"
#include "mms_connman.h"
#include "mms_file_util.h"
#include "mms_codec.h"
#include "mms_util.h"
#include "mms_task.h"

#include <errno.h>

/* Logging */
#define MMS_LOG_MODULE_NAME mms_dispatcher_log
#include "mms_lib_log.h"
31
#include "mms_error.h"
Slava Monich's avatar
Slava Monich committed
32 33 34 35
MMS_LOG_MODULE_DEFINE("mms-dispatcher");

struct mms_dispatcher {
    gint ref_count;
36
    MMSSettings* settings;
Slava Monich's avatar
Slava Monich committed
37 38 39 40 41 42 43 44 45
    MMSTask* active_task;
    MMSTaskDelegate task_delegate;
    MMSHandler* handler;
    MMSConnMan* cm;
    MMSConnection* connection;
    MMSDispatcherDelegate* delegate;
    GQueue* tasks;
    guint next_run_id;
    guint network_idle_id;
46
    gulong handler_done_id;
47
    gulong connman_done_id;
48
    gulong connection_changed_id;
49
    gboolean started;
Slava Monich's avatar
Slava Monich committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
};

typedef void (*MMSDispatcherIdleCallbackProc)(MMSDispatcher* disp);
typedef struct mms_dispatcher_idle_callback {
    MMSDispatcher* dispatcher;
    MMSDispatcherIdleCallbackProc proc;
} MMSDispatcherIdleCallback;

inline static MMSDispatcher*
mms_dispatcher_from_task_delegate(MMSTaskDelegate* delegate)
    { return MMS_CAST(delegate,MMSDispatcher,task_delegate); }

static
void
mms_dispatcher_run(
    MMSDispatcher* disp);

/**
68
 * Checks if we are done and notifies the delegate if necessary
Slava Monich's avatar
Slava Monich committed
69 70 71
 */
static
void
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
mms_dispatcher_check_if_done(
    MMSDispatcher* disp)
{
    if (!mms_dispatcher_is_active(disp)) {
        /* Cancel pending runs */
        if (disp->next_run_id) {
            g_source_remove(disp->next_run_id);
            disp->next_run_id = 0;
        }
        /* Notify the delegate that we are done */
        if (disp->delegate && disp->delegate->fn_done && disp->started) {
            disp->started = FALSE;
            disp->delegate->fn_done(disp->delegate, disp);
        }
    }
}

/**
 * Drops the reference to the network connection
 */
static
void
mms_dispatcher_drop_connection(
Slava Monich's avatar
Slava Monich committed
95 96 97
    MMSDispatcher* disp)
{
    if (disp->connection) {
98
        MMS_ASSERT(!mms_connection_is_active(disp->connection));
99 100 101
        mms_connection_remove_handler(disp->connection,
            disp->connection_changed_id);
        disp->connection_changed_id = 0;
Slava Monich's avatar
Slava Monich committed
102 103
        mms_connection_unref(disp->connection);
        disp->connection = NULL;
104 105 106
        if (disp->network_idle_id) {
            g_source_remove(disp->network_idle_id);
            disp->network_idle_id = 0;
Slava Monich's avatar
Slava Monich committed
107 108
        }
    }
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
}

/**
 * Close the network connection
 */
static
void
mms_dispatcher_close_connection(
    MMSDispatcher* disp)
{
    if (disp->connection) {
        mms_connection_close(disp->connection);
        /* Assert that connection state changes are asynchronous */
        MMS_ASSERT(disp->connection);
        if (!mms_connection_is_active(disp->connection)) {
            mms_dispatcher_drop_connection(disp);
        }
        mms_dispatcher_check_if_done(disp);
Slava Monich's avatar
Slava Monich committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
    }
}

/**
 * Run loop callbacks
 */
static
void
mms_dispatcher_callback_free(
    gpointer data)
{
    MMSDispatcherIdleCallback* call = data;
    mms_dispatcher_unref(call->dispatcher);
    g_free(call);
}

static
gboolean
mms_dispatcher_idle_callback_cb(
    gpointer data)
{
    MMSDispatcherIdleCallback* call = data;
    call->proc(call->dispatcher);
    return FALSE;
}

static
guint
mms_dispatcher_callback_schedule(
    MMSDispatcher* disp,
    MMSDispatcherIdleCallbackProc proc)
{
    MMSDispatcherIdleCallback* call = g_new0(MMSDispatcherIdleCallback,1);
    call->dispatcher = mms_dispatcher_ref(disp);
    call->proc = proc;
    return g_idle_add_full(G_PRIORITY_HIGH, mms_dispatcher_idle_callback_cb,
        call, mms_dispatcher_callback_free);
}

static
guint
mms_dispatcher_timeout_callback_schedule(
    MMSDispatcher* disp,
    guint interval,
    MMSDispatcherIdleCallbackProc proc)
{
    MMSDispatcherIdleCallback* call = g_new0(MMSDispatcherIdleCallback,1);
    call->dispatcher = mms_dispatcher_ref(disp);
    call->proc = proc;
    return g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, interval,
        mms_dispatcher_idle_callback_cb, call, mms_dispatcher_callback_free);
}

/**
 * Network idle timeout
 */

static
void
mms_dispatcher_network_idle_run(
    MMSDispatcher* disp)
{
    MMS_ASSERT(disp->network_idle_id);
    disp->network_idle_id = 0;
    mms_dispatcher_close_connection(disp);
}

static
void
mms_dispatcher_network_idle_check(
    MMSDispatcher* disp)
{
    if (disp->connection && !disp->network_idle_id) {
        /* Schedule idle inactivity timeout callback */
        MMS_VERBOSE("Network connection is inactive");
        disp->network_idle_id = mms_dispatcher_timeout_callback_schedule(disp,
203
            disp->settings->config->idle_secs, mms_dispatcher_network_idle_run);
Slava Monich's avatar
Slava Monich committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
    }
}

static
void
mms_dispatcher_network_idle_cancel(
    MMSDispatcher* disp)
{
    if (disp->network_idle_id) {
        MMS_VERBOSE("Cancel network inactivity timeout");
        g_source_remove(disp->network_idle_id);
        disp->network_idle_id = 0;
    }
}

/**
 * Dispatcher run on a fresh stack
 */
static
void
mms_dispatcher_next_run(
    MMSDispatcher* disp)
{
    MMS_ASSERT(disp->next_run_id);
    MMS_ASSERT(!disp->active_task);
    disp->next_run_id = 0;
    if (!disp->active_task) {
        mms_dispatcher_run(disp);
    }
}

static
void
mms_dispatcher_next_run_schedule(
    MMSDispatcher* disp)
{
    if (disp->next_run_id) g_source_remove(disp->next_run_id);
    disp->next_run_id = mms_dispatcher_callback_schedule(disp,
        mms_dispatcher_next_run);
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
/**
 * Connection state callback
 */
static
void
mms_dispatcher_connection_state_changed(
    MMSConnection* conn,
    void* data)
{
    MMSDispatcher* disp = data;
    MMS_CONNECTION_STATE state = mms_connection_state(conn);
    MMS_DEBUG("%s %s", conn->imsi, mms_connection_state_name(conn));
    MMS_ASSERT(conn == disp->connection);
    if (state == MMS_CONNECTION_STATE_FAILED ||
        state == MMS_CONNECTION_STATE_CLOSED) {
        GList* entry;
        mms_dispatcher_close_connection(disp);
        for (entry = disp->tasks->head; entry; entry = entry->next) {
            MMSTask* task = entry->data;
            switch (task->state) {
            case MMS_TASK_STATE_NEED_CONNECTION:
            case MMS_TASK_STATE_NEED_USER_CONNECTION:
            case MMS_TASK_STATE_TRANSMITTING:
                if (!strcmp(conn->imsi, task->imsi)) {
                    mms_task_network_unavailable(task, TRUE);
                }
            default:
                break;
            }
        }
        mms_dispatcher_drop_connection(disp);
        mms_dispatcher_check_if_done(disp);
    }
    if (!disp->active_task) {
        mms_dispatcher_next_run_schedule(disp);
    }
}

Slava Monich's avatar
Slava Monich committed
283 284 285
/**
 * Set the delegate that receives dispatcher notifications.
 * One delegate per dispatcher.
286 287
 *
 * TODO: Replace this delegate stuff wuth glib signals
Slava Monich's avatar
Slava Monich committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
 */
void
mms_dispatcher_set_delegate(
    MMSDispatcher* disp,
    MMSDispatcherDelegate* delegate)
{
    MMS_ASSERT(!disp->delegate || !delegate);
    disp->delegate = delegate;
}

/**
 * Checks if dispatcher has something to do.
 */
gboolean
mms_dispatcher_is_active(
    MMSDispatcher* disp)
{
305
    return disp && (mms_connection_is_active(disp->connection) ||
306 307
        mms_handler_busy(disp->handler) || mms_connman_busy(disp->cm) ||
        disp->active_task || !g_queue_is_empty(disp->tasks));
Slava Monich's avatar
Slava Monich committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
}

/**
 * Picks the next task for processing. Reference is passed to the caller.
 * Caller must eventually dereference the task or place it back to the queue.
 */
static
MMSTask*
mms_dispatcher_pick_next_task(
    MMSDispatcher* disp)
{
    GList* entry;
    gboolean connection_in_use = FALSE;

    /* Check the current connection */
    if (disp->connection) {

        /* Don't interfere with the task transmiting the data */
        for (entry = disp->tasks->head; entry; entry = entry->next) {
            MMSTask* task = entry->data;
            if (task->state == MMS_TASK_STATE_TRANSMITTING) {
                MMS_ASSERT(!strcmp(task->imsi, disp->connection->imsi));
                return NULL;
            }
        }

        /* Look for another task that has use for the existing connection
         * before we close it */
        for (entry = disp->tasks->head; entry; entry = entry->next) {
            MMSTask* task = entry->data;
            if (task->state == MMS_TASK_STATE_NEED_CONNECTION ||
                task->state == MMS_TASK_STATE_NEED_USER_CONNECTION) {
                 if (!strcmp(task->imsi, disp->connection->imsi)) {
                     if (mms_connection_state(disp->connection) ==
                         MMS_CONNECTION_STATE_OPEN) {
                        /* Found a task that can use this connection */
                        g_queue_delete_link(disp->tasks, entry);
                        mms_dispatcher_network_idle_cancel(disp);
                        return task;
                     }
                     connection_in_use = TRUE;
                 }
            }
        }
    }

    if (connection_in_use) {
        /* Connection is needed but isn't open yet, make sure that network
         * inactivity timer is off while connection is being established */
        mms_dispatcher_network_idle_cancel(disp);
    } else {
        /* Then look for a task that needs any sort of network connection */
        for (entry = disp->tasks->head; entry; entry = entry->next) {
            MMSTask* task = entry->data;
            if ((task->state == MMS_TASK_STATE_NEED_CONNECTION ||
                 task->state == MMS_TASK_STATE_NEED_USER_CONNECTION)) {
364 365
                /* Closing the connection may take some time. If connection
                 * is still around, we will have to wait. */
Slava Monich's avatar
Slava Monich committed
366
                mms_dispatcher_close_connection(disp);
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
                if (!disp->connection) {
                    disp->connection = mms_connman_open_connection(disp->cm,
                        task->imsi, FALSE);
                    if (disp->connection) {
                        MMS_ASSERT(!disp->connection_changed_id);
                        disp->connection_changed_id =
                            mms_connection_add_state_change_handler(
                                disp->connection,
                                mms_dispatcher_connection_state_changed,
                                disp);
                        g_queue_delete_link(disp->tasks, entry);
                        return task;
                    } else {
                        mms_task_network_unavailable(task, FALSE);
                    }
Slava Monich's avatar
Slava Monich committed
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
                }
            }
        }
    }

    /* Finally look for a runnable task that doesn't need network */
    for (entry = disp->tasks->head; entry; entry = entry->next) {
        MMSTask* task = entry->data;
        if (task->state == MMS_TASK_STATE_READY ||
            task->state == MMS_TASK_STATE_DONE) {
            g_queue_delete_link(disp->tasks, entry);
            return task;
        }
    }

    /* Nothing found, we are done for now */
    return NULL;
}

/**
 * Task dispatch loop.
 */
static
void
mms_dispatcher_run(
    MMSDispatcher* disp)
{
    MMSTask* task;
    MMS_ASSERT(!disp->active_task);
    while ((task = mms_dispatcher_pick_next_task(disp)) != NULL) {
        MMS_DEBUG("%s %s", task->name, mms_task_state_name(task->state));
        disp->active_task = task;
        switch (task->state) {
        case MMS_TASK_STATE_READY:
            mms_task_run(task);
            break;

        case MMS_TASK_STATE_NEED_CONNECTION:
        case MMS_TASK_STATE_NEED_USER_CONNECTION:
            MMS_ASSERT(disp->connection);
            if (mms_connection_is_open(disp->connection)) {
                /* Connection is already active, send/receive the data */
                mms_task_transmit(task, disp->connection);
            }
            break;

        default:
            break;
        }

        if (task->state == MMS_TASK_STATE_DONE) {
            task->delegate = NULL;
            mms_task_unref(task);
        } else {
            g_queue_push_tail(disp->tasks, task);
        }
        disp->active_task = NULL;
    }

    if (disp->connection) {
        /* Check if network connection is being used */
        GList* entry;
        gboolean connection_in_use = FALSE;
        for (entry = disp->tasks->head; entry; entry = entry->next) {
            MMSTask* task = entry->data;
            if (task->state == MMS_TASK_STATE_NEED_CONNECTION ||
                task->state == MMS_TASK_STATE_NEED_USER_CONNECTION ||
                task->state == MMS_TASK_STATE_TRANSMITTING) {
                connection_in_use = TRUE;
                break;
            }
        }
        if (connection_in_use) {
            /* It's in use, disable idle inactivity callback */
            mms_dispatcher_network_idle_cancel(disp);
        } else {
            /* Make sure that network inactivity timer is ticking */
            mms_dispatcher_network_idle_check(disp);
        }
    }

463
    mms_dispatcher_check_if_done(disp);
Slava Monich's avatar
Slava Monich committed
464 465 466 467 468 469 470 471 472
}

/**
 * Starts task processing.
 */
gboolean
mms_dispatcher_start(
    MMSDispatcher* disp)
{
473 474
    const char* root_dir = disp->settings->config->root_dir;
    int err = g_mkdir_with_parents(root_dir, MMS_DIR_PERM);
Slava Monich's avatar
Slava Monich committed
475 476
    if (!err || errno == EEXIST) {
        if (!g_queue_is_empty(disp->tasks)) {
477
            disp->started = TRUE;
Slava Monich's avatar
Slava Monich committed
478 479 480 481
            mms_dispatcher_next_run_schedule(disp);
            return TRUE;
        }
    } else {
482
        MMS_ERR("Failed to create %s: %s", root_dir, strerror(errno));
Slava Monich's avatar
Slava Monich committed
483 484 485 486 487 488 489 490 491 492 493 494 495 496
    }
    return FALSE;
}

static
void
mms_dispatcher_queue_task(
    MMSDispatcher* disp,
    MMSTask* task)
{
    task->delegate = &disp->task_delegate;
    g_queue_push_tail(disp->tasks, mms_task_ref(task));
}

497
static
Slava Monich's avatar
Slava Monich committed
498
gboolean
499
mms_dispatcher_queue_and_unref_task(
Slava Monich's avatar
Slava Monich committed
500
    MMSDispatcher* disp,
501
    MMSTask* task)
Slava Monich's avatar
Slava Monich committed
502 503 504 505 506 507 508 509 510 511
{
    if (task) {
        mms_dispatcher_queue_task(disp, task);
        mms_task_unref(task);
        return TRUE;
    } else {
        return FALSE;
    }
}

512 513 514 515 516 517 518
/**
 * Creates a WAP push receive task and adds it to the queue.
 */
gboolean
mms_dispatcher_handle_push(
    MMSDispatcher* disp,
    const char* imsi,
519 520
    GBytes* push,
    GError** error)
521 522
{
    return mms_dispatcher_queue_and_unref_task(disp,
523
        mms_task_notification_new(disp->settings, disp->handler,
524
            imsi, push, error));
525 526
}

Slava Monich's avatar
Slava Monich committed
527 528 529 530 531 532 533 534 535
/**
 * Creates download task and adds it to the queue.
 */
gboolean
mms_dispatcher_receive_message(
    MMSDispatcher* disp,
    const char* id,
    const char* imsi,
    gboolean automatic,
536 537
    GBytes* bytes,
    GError** error)
Slava Monich's avatar
Slava Monich committed
538 539 540 541 542 543
{
    gboolean ok = FALSE;
    MMSPdu* pdu = mms_decode_bytes(bytes);
    if (pdu) {
        MMS_ASSERT(pdu->type == MMS_MESSAGE_TYPE_NOTIFICATION_IND);
        if (pdu->type == MMS_MESSAGE_TYPE_NOTIFICATION_IND) {
544
            ok = mms_dispatcher_queue_and_unref_task(disp,
545
                mms_task_retrieve_new(disp->settings, disp->handler,
546
                    id, imsi, pdu, error));
Slava Monich's avatar
Slava Monich committed
547 548 549
        }
        mms_message_free(pdu);
    } else {
550
        MMS_ERROR(error, MMS_LIB_ERROR_DECODE, "Failed to decode MMS PDU");
Slava Monich's avatar
Slava Monich committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564
    }
    return ok;
}

/**
 * Sends read report
 */
gboolean
mms_dispatcher_send_read_report(
    MMSDispatcher* disp,
    const char* id,
    const char* imsi,
    const char* message_id,
    const char* to,
565 566
    MMSReadStatus status,
    GError** error)
Slava Monich's avatar
Slava Monich committed
567
{
568
    return mms_dispatcher_queue_and_unref_task(disp,
569
        mms_task_read_new(disp->settings, disp->handler,
570
            id, imsi, message_id, to, status, error));
Slava Monich's avatar
Slava Monich committed
571 572
}

573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/**
 * Sends MMS message
 */
char*
mms_dispatcher_send_message(
    MMSDispatcher* disp,
    const char* id,
    const char* imsi,
    const char* to,
    const char* cc,
    const char* bcc,
    const char* subject,
    unsigned int flags,
    const MMSAttachmentInfo* parts,
    unsigned int nparts,
    GError** error)
{
    char* default_imsi = NULL;
    if (!imsi || !imsi[0]) {
        /* No IMSI specified - try the default one */
        imsi = default_imsi = mms_connman_default_imsi(disp->cm);
    }
    if (imsi) {
        if (mms_dispatcher_queue_and_unref_task(disp,
597
            mms_task_encode_new(disp->settings, disp->handler, id, imsi,
598 599 600 601 602 603 604 605 606 607
            to, cc, bcc, subject, flags, parts, nparts, error))) {
            return default_imsi ? default_imsi : g_strdup(imsi);
        }
    } else {
        MMS_ERROR(error, MMS_LIB_ERROR_NOSIM,
            "No IMSI is provided and none is available");
    }
    return NULL;
}

Slava Monich's avatar
Slava Monich committed
608 609 610 611 612 613 614 615 616 617 618
/**
 * Cancels al the activity associated with the specified message
 */
void
mms_dispatcher_cancel(
    MMSDispatcher* disp,
    const char* id)
{
    GList* entry;
    for (entry = disp->tasks->head; entry; entry = entry->next) {
        MMSTask* task = entry->data;
619
        if (mms_task_match_id(task, id)) {
Slava Monich's avatar
Slava Monich committed
620 621 622
            mms_task_cancel(task);
        }
    }
623

624
    if (mms_task_match_id(disp->active_task, id)) {
Slava Monich's avatar
Slava Monich committed
625 626
        mms_task_cancel(disp->active_task);
    }
627 628 629 630 631 632

    /* If we have cancelling all tasks, close the network connection
     * immediately to finish up as soon as possible. */
    if (!id && disp->connection) {
        mms_dispatcher_close_connection(disp);
    }
Slava Monich's avatar
Slava Monich committed
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
}

/**
 * Task delegate callbacks
 */
static
void
mms_dispatcher_delegate_task_queue(
    MMSTaskDelegate* delegate,
    MMSTask* task)
{
    MMSDispatcher* disp = mms_dispatcher_from_task_delegate(delegate);
    mms_dispatcher_queue_task(disp, task);
    if (!disp->active_task) {
        mms_dispatcher_next_run_schedule(disp);
    }
}

static
void
mms_dispatcher_delegate_task_state_changed(
    MMSTaskDelegate* delegate,
    MMSTask* task)
{
    MMSDispatcher* disp = mms_dispatcher_from_task_delegate(delegate);
    if (!disp->active_task) {
        mms_dispatcher_next_run_schedule(disp);
    }
}

663 664 665 666 667 668 669 670 671 672
/**
 * Handler state callback
 */
static
void
mms_dispatcher_handler_done(
    MMSHandler* handler,
    void* param)
{
    MMSDispatcher* disp = param;
673
    MMS_VERBOSE("Handler is inactive");
674 675 676
    mms_dispatcher_next_run_schedule(disp);
}

Slava Monich's avatar
Slava Monich committed
677
/**
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
 * Connman state callback
 */
static
void
mms_dispatcher_connman_done(
    MMSConnMan* cm,
    void* param)
{
    MMSDispatcher* disp = param;
    MMS_VERBOSE("Connman is inactive");
    mms_dispatcher_check_if_done(disp);
}

/**
 * Creates the dispatcher object. Caller must call mms_dispatcher_unref
Slava Monich's avatar
Slava Monich committed
693 694 695 696
 * when it no longer needs it.
 */
MMSDispatcher*
mms_dispatcher_new(
697
    MMSSettings* settings,
Slava Monich's avatar
Slava Monich committed
698 699 700 701 702
    MMSConnMan* cm,
    MMSHandler* handler)
{
    MMSDispatcher* disp = g_new0(MMSDispatcher, 1);
    disp->ref_count = 1;
703
    disp->settings = mms_settings_ref(settings);
Slava Monich's avatar
Slava Monich committed
704 705 706 707 708 709 710
    disp->tasks = g_queue_new();
    disp->handler = mms_handler_ref(handler);
    disp->cm = mms_connman_ref(cm);
    disp->task_delegate.fn_task_queue =
        mms_dispatcher_delegate_task_queue;
    disp->task_delegate.fn_task_state_changed =
        mms_dispatcher_delegate_task_state_changed;
711 712
    disp->handler_done_id = mms_handler_add_done_callback(handler,
        mms_dispatcher_handler_done, disp);
713 714
    disp->connman_done_id = mms_connman_add_done_callback(cm,
        mms_dispatcher_connman_done, disp);
Slava Monich's avatar
Slava Monich committed
715 716 717 718 719 720 721 722 723 724 725 726
    return disp;
}

/**
 * Deinitializer
 */
static
void
mms_dispatcher_finalize(
    MMSDispatcher* disp)
{
    MMSTask* task;
727 728
    const char* root_dir = disp->settings->config->root_dir;
    char* msg_dir = g_strconcat(root_dir, "/" MMS_MESSAGE_DIR "/", NULL);
Slava Monich's avatar
Slava Monich committed
729
    MMS_VERBOSE_("");
730
    mms_handler_remove_callback(disp->handler, disp->handler_done_id);
731
    mms_connman_remove_callback(disp->cm, disp->connman_done_id);
732
    mms_dispatcher_drop_connection(disp);
Slava Monich's avatar
Slava Monich committed
733 734 735 736 737 738
    while ((task = g_queue_pop_head(disp->tasks)) != NULL) {
        task->delegate = NULL;
        mms_task_cancel(task);
        mms_task_unref(task);
    }
    g_queue_free(disp->tasks);
739
    mms_settings_unref(disp->settings);
Slava Monich's avatar
Slava Monich committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
    mms_handler_unref(disp->handler);
    mms_connman_unref(disp->cm);

    /* Try to remove the message directory */
    remove(msg_dir);
    g_free(msg_dir);
}

/**
 * Reference counting. NULL argument is safely ignored.
 */
MMSDispatcher*
mms_dispatcher_ref(
    MMSDispatcher* disp)
{
    if (disp) {
        MMS_ASSERT(disp->ref_count > 0);
        g_atomic_int_inc(&disp->ref_count);
    }
    return disp;
}

void
mms_dispatcher_unref(
    MMSDispatcher* disp)
{
    if (disp) {
        MMS_ASSERT(disp->ref_count > 0);
        if (g_atomic_int_dec_and_test(&disp->ref_count)) {
            mms_dispatcher_finalize(disp);
            g_free(disp);
        }
    }
}

/*
 * Local Variables:
 * mode: C
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 */