mms_task_http.c 25.2 KB
Newer Older
1
/*
2
 * Copyright (C) 2013-2017 Jolla Ltd.
3
 * Contact: Slava Monich <slava.monich@jolla.com>
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * 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_task_http.h"
#include "mms_connection.h"
17
#include "mms_settings.h"
18
#include "mms_file_util.h"
19 20 21
#include "mms_transfer_list.h"

#include <gutil_misc.h>
22

23 24
#include <ctype.h>

25 26 27 28 29 30 31 32 33 34 35
#ifndef _WIN32
#  include <sys/ioctl.h>
#  include <arpa/inet.h>
#  include <net/if.h>
#endif

/* Appeared in libsoup somewhere between 2.41.5 and 2.41.90 */
#ifndef SOUP_SESSION_LOCAL_ADDRESS
#  define SOUP_SESSION_LOCAL_ADDRESS "local-address"
#endif

36 37 38 39 40
#if SOUP_CHECK_VERSION(2,42,0)
#  define soup_session_async_new_with_options soup_session_new_with_options
#  define soup_session_async_new soup_session_new
#endif

41
/* Logging */
42
#define GLOG_MODULE_NAME MMS_TASK_HTTP_LOG
43
#include "mms_lib_log.h"
44 45
#include <gutil_log.h>
GLOG_MODULE_DEFINE2("mms-task-http", MMS_TASK_LOG);
46 47 48 49 50 51 52 53 54

/* HTTP task state */
typedef enum _mms_http_state {
    MMS_HTTP_READY,         /* Ready to run */
    MMS_HTTP_ACTIVE,        /* Sending or receiving the data */
    MMS_HTTP_PAUSED,        /* Sleeping or waiting for connection */
    MMS_HTTP_DONE           /* HTTP transaction has been finished */
} MMS_HTTP_STATE;

55
#define MMS_HTTP_MAX_CHUNK (4046)
56

57 58 59 60 61 62 63 64 65
/* Soup message signals */
enum mms_http_soup_message_signals {
    MMS_SOUP_MESSAGE_SIGNAL_WROTE_HEADERS,
    MMS_SOUP_MESSAGE_SIGNAL_WROTE_CHUNK,
    MMS_SOUP_MESSAGE_SIGNAL_GOT_HEADERS,
    MMS_SOUP_MESSAGE_SIGNAL_GOT_CHUNK,
    MMS_SOUP_MESSAGE_SIGNAL_COUNT
};

66 67 68 69 70 71 72
/* Transfer context */
typedef struct mms_http_transfer {
    MMSConnection* connection;
    SoupSession* session;
    SoupMessage* message;
    int receive_fd;
    int send_fd;
73 74 75 76 77
    guint bytes_sent;
    guint bytes_received;
    guint bytes_to_send;
    guint bytes_to_receive;
    gulong msg_signal_id[MMS_SOUP_MESSAGE_SIGNAL_COUNT];
78 79 80
} MMSHttpTransfer;

/* Private state */
Slava Monich's avatar
Slava Monich committed
81
struct mms_task_http_priv {
82 83 84 85
    MMSHttpTransfer* tx;
    char* uri;
    char* send_path;
    char* receive_path;
86 87
    char* receive_file;
    char* transfer_type;
88
    MMS_HTTP_STATE transaction_state;
89
    MMS_CONNECTION_TYPE connection_type;
90 91
};

Slava Monich's avatar
Slava Monich committed
92
G_DEFINE_TYPE(MMSTaskHttp, mms_task_http, MMS_TYPE_TASK)
93 94 95 96 97 98
#define MMS_TYPE_TASK_HTTP (mms_task_http_get_type())
#define MMS_TASK_HTTP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
   MMS_TYPE_TASK_HTTP, MMSTaskHttp))
#define MMS_TASK_HTTP_GET_CLASS(obj)  \
    (G_TYPE_INSTANCE_GET_CLASS((obj), MMS_TYPE_TASK_HTTP, MMSTaskHttpClass))

99
static
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
SoupURI*
mms_http_uri_parse(
    const char* raw_uri)
{
    SoupURI* uri = NULL;
    if (raw_uri) {
        static const char* http = "http://";
        const char* uri_to_parse;
        char* tmp_uri = NULL;
        if (g_str_has_prefix(raw_uri, http)) {
            uri_to_parse = raw_uri;
        } else {
            uri_to_parse = tmp_uri = g_strconcat(http, raw_uri, NULL);
        }
        uri = soup_uri_new(uri_to_parse);
        if (!uri) {
116
            GERR("Could not parse %s as a URI", uri_to_parse);
117 118 119 120 121 122 123 124 125
        }
        g_free(tmp_uri);
    }
    return uri;
}

static
SoupSession*
mms_http_create_session(
126
    const MMSSettingsSimData* cfg,
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    MMSConnection* conn)
{
    SoupSession* session = NULL;

    /* Determine address of the MMS interface */
    if (conn->netif && conn->netif[0]) {
#ifndef _WIN32
        struct ifreq ifr;
        int fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd >= 0) {
            memset(&ifr, 0, sizeof(ifr));
            strncpy(ifr.ifr_name, conn->netif, IFNAMSIZ-1);
            if (ioctl(fd, SIOCGIFADDR, &ifr) >= 0) {
                SoupAddress* local_address = soup_address_new_from_sockaddr(
                    &ifr.ifr_addr, sizeof(ifr.ifr_addr));
142
#  if GUTIL_LOG_DEBUG
143 144 145 146 147 148 149 150 151 152 153 154 155
                char buf[128];
                int af = ifr.ifr_addr.sa_family;
                buf[0] = 0;
                if (af == AF_INET) {
                    struct sockaddr_in* addr = (void*)&ifr.ifr_addr;
                    inet_ntop(af, &addr->sin_addr, buf, sizeof(buf));
                } else if (af == AF_INET6) {
                    struct sockaddr_in6* addr = (void*)&ifr.ifr_addr;
                    inet_ntop(af, &addr->sin6_addr, buf, sizeof(buf));
                } else {
                    snprintf(buf, sizeof(buf), "<address family %d>", af);
                }
                buf[sizeof(buf)-1] = 0;
156 157 158
                GDEBUG("MMS interface address %s", buf);
#  endif /* GUTIL_LOG_DEBUG */
                GASSERT(local_address);
159 160 161 162 163
                session = soup_session_async_new_with_options(
                    SOUP_SESSION_LOCAL_ADDRESS, local_address,
                    NULL);
                g_object_unref(local_address);
            } else {
164
                GERR("Failed to query IP address of %s: %s",
165 166 167 168 169 170
                    conn->netif, strerror(errno));
            }
            close(fd);
        }
#endif /* _WIN32 */
    } else {
171
        GWARN("MMS interface is unknown");
172 173 174 175 176 177 178 179 180 181
    }

    if (!session) {
        /* No local address so bind to any interface */
        session = soup_session_async_new();
    }

    if (conn->mmsproxy && conn->mmsproxy[0]) {
        SoupURI* proxy_uri = mms_http_uri_parse(conn->mmsproxy);
        if (proxy_uri) {
182
            GDEBUG("MMS proxy %s", conn->mmsproxy);
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
            if (proxy_uri->host[0] == '0' || strstr(proxy_uri->host, ".0")) {
                /*
                 * Some operators provide IP address of the MMS proxy
                 * prepending zeros to each number shorter then 3 digits,
                 * e.g. "192.168.094.023" instead of "192.168.94.23".
                 * That may look nicer but it's actually wrong because
                 * the numbers starting with zeros are interpreted as
                 * octal numbers. In the example above 023 actually means
                 * 16 and 094 is not a valid number at all.
                 *
                 * In addition to publishing these broken settings on their
                 * web sites, some of the operators send them over the air,
                 * in which case we can't even blame the user for entering
                 * an invalid IP address. We better be prepared to deal with
                 * those.
                 *
                 * Since nobody in the world seems to be actually using the
                 * octal notation to write an IP address, let's remove the
                 * leading zeros if we find them in the host part of the MMS
                 * proxy URL.
                 */
                char* host;
                char** parts = g_strsplit(proxy_uri->host, ".", -1);
                guint count = g_strv_length(parts);
                if (count == 4) {
                    char** ptr = parts;
                    while (*ptr) {
                        char* part = *ptr;
                        while (part[0] == '0' && isdigit(part[1])) {
                            memmove(part, part+1, strlen(part));
                        }
                        *ptr++ = part;
                    }
                    host = g_strjoinv(".", parts);
                    GDEBUG("MMS proxy host %s => %s", proxy_uri->host, host);
                    soup_uri_set_host(proxy_uri, host);
                    g_free(host);
                }
                g_strfreev(parts);
            }
223 224 225 226 227
            g_object_set(session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
            soup_uri_free(proxy_uri);
        }
    }

228
    if (cfg && cfg->user_agent) {
229 230 231 232 233 234
        g_object_set(session, SOUP_SESSION_USER_AGENT, cfg->user_agent, NULL);
    }

    return session;
}

235
static
236 237
MMSHttpTransfer*
mms_http_transfer_new(
238
    const MMSSettingsSimData* cfg,
239 240 241 242 243 244 245 246
    MMSConnection* connection,
    const char* method,
    const char* uri,
    int receive_fd,
    int send_fd)
{
    SoupURI* soup_uri = mms_http_uri_parse(uri);
    if (soup_uri) {
247
        MMSHttpTransfer* tx = g_new0(MMSHttpTransfer, 1);
248
        tx->session = mms_http_create_session(cfg, connection);
249 250 251 252 253 254 255 256
        tx->message = soup_message_new_from_uri(method, soup_uri);
        tx->connection = mms_connection_ref(connection);
        tx->receive_fd = receive_fd;
        tx->send_fd = send_fd;
        soup_uri_free(soup_uri);
        soup_message_set_flags(tx->message,
            SOUP_MESSAGE_NO_REDIRECT |
            SOUP_MESSAGE_NEW_CONNECTION);
257 258
        soup_message_headers_append(tx->message->request_headers,
            "Connection", "close");
259 260
        if (cfg->uaprof && cfg->uaprof[0]) {
            const char* uaprof_header = "x-wap-profile";
261
            GVERBOSE("%s %s", uaprof_header, cfg->uaprof);
262 263 264
            soup_message_headers_append(tx->message->request_headers,
                uaprof_header, cfg->uaprof);
        }
265 266 267 268 269
        return tx;
    }
    return NULL;
}

270
static
271 272 273 274 275
void
mms_http_transfer_free(
    MMSHttpTransfer* tx)
{
    if (tx) {
276 277
        gutil_disconnect_handlers(tx->message, tx->msg_signal_id,
            G_N_ELEMENTS(tx->msg_signal_id));
278 279 280 281 282 283 284 285 286 287
        soup_session_abort(tx->session);
        g_object_unref(tx->session);
        g_object_unref(tx->message);
        mms_connection_unref(tx->connection);
        if (tx->receive_fd >= 0) close(tx->receive_fd);
        if (tx->send_fd >= 0) close(tx->send_fd);
        g_free(tx);
    }
}

288 289 290 291 292 293 294 295
static
void
mms_task_http_send_progress(
    MMSTaskHttp* http)
{
    MMSTaskHttpPriv* priv = http->priv;
    MMSHttpTransfer* tx = priv->tx;
    if (tx) {
296
        GASSERT(tx->bytes_sent <= tx->bytes_to_send);
297 298 299 300 301 302 303 304 305 306 307 308 309 310
        mms_transfer_list_transfer_send_progress(http->transfers,
            http->task.id, priv->transfer_type, tx->bytes_sent,
            tx->bytes_to_send);
    }
}

static
void
mms_task_http_receive_progress(
    MMSTaskHttp* http)
{
    MMSTaskHttpPriv* priv = http->priv;
    MMSHttpTransfer* tx = priv->tx;
    if (tx) {
311 312 313
        /* Some operators don't provide Content-Length */
        GASSERT(!tx->bytes_to_receive ||
            tx->bytes_received <= tx->bytes_to_receive);
314 315 316 317 318 319
        mms_transfer_list_transfer_receive_progress(http->transfers,
            http->task.id, priv->transfer_type, tx->bytes_received,
            tx->bytes_to_receive);
    }
}

320 321 322 323 324 325 326
static
void
mms_task_http_set_state(
    MMSTaskHttp* http,
    MMS_HTTP_STATE new_state,
    SoupStatus ss)
{
Slava Monich's avatar
Slava Monich committed
327
    MMSTaskHttpPriv* priv = http->priv;
328 329 330
    if (priv->transaction_state != new_state &&
        priv->transaction_state != MMS_HTTP_DONE) {
        MMSTaskHttpClass* klass = MMS_TASK_HTTP_GET_CLASS(http);
331 332 333 334 335 336 337 338 339 340 341 342 343 344
        const gboolean is_active = (new_state == MMS_HTTP_ACTIVE);

        /* Notify interested parties about transfer state changes */
        if (is_active != (priv->transaction_state == MMS_HTTP_ACTIVE)) {
            if (is_active) {
                mms_transfer_list_transfer_started(http->transfers,
                    http->task.id, priv->transfer_type);
                mms_task_http_send_progress(http);
            } else {
                mms_transfer_list_transfer_finished(http->transfers,
                    http->task.id, priv->transfer_type);
            }
        }

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
        priv->transaction_state = new_state;
        switch (new_state) {
        case MMS_HTTP_ACTIVE:
            if (klass->fn_started) klass->fn_started(http);
            break;
        case MMS_HTTP_PAUSED:
            if (klass->fn_paused) klass->fn_paused(http);
            break;
        case MMS_HTTP_DONE:
            if (klass->fn_done) klass->fn_done(http, priv->receive_path, ss);
            break;
        case MMS_HTTP_READY:
            break;
        }
    }
}

static
void
mms_task_http_finish_transfer(
    MMSTaskHttp* http)
{
Slava Monich's avatar
Slava Monich committed
367
    MMSTaskHttpPriv* priv = http->priv;
368 369 370 371 372 373 374 375 376 377 378 379 380 381
    if (priv->tx) {
        mms_http_transfer_free(priv->tx);
        priv->tx = NULL;
    }
}

static
void
mms_task_http_finished(
    SoupSession* session,
    SoupMessage* msg,
    gpointer user_data)
{
    MMSTaskHttp* http = user_data;
Slava Monich's avatar
Slava Monich committed
382
    MMSTaskHttpPriv* priv = http->priv;
383 384 385 386
    if (priv->tx && priv->tx->session == session) {
        MMS_HTTP_STATE next_http_state;
        MMSTask* task = &http->task;
        SoupStatus http_status = msg->status_code;
387

388 389
#if GUTIL_LOG_DEBUG
        MMSHttpTransfer* tx = priv->tx;
390
        if (tx->bytes_received) {
391
            GDEBUG("HTTP status %u [%s] %u byte(s)", msg->status_code,
392
                soup_message_headers_get_content_type(msg->response_headers,
393
                NULL), tx->bytes_received);
394
        } else {
395
            GDEBUG("HTTP status %u (%s)", msg->status_code,
396
                soup_status_get_phrase(msg->status_code));
397
        }
398
#endif /* GUTIL_LOG_DEBUG */
399

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
        if (SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
            next_http_state = MMS_HTTP_DONE;
            mms_task_set_state(task, MMS_TASK_STATE_DONE);
        } else {
            /* Will retry if this was an I/O error, otherwise we consider
             * it a permanent failure */
            if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) {
                if (mms_task_retry(task)) {
                    next_http_state = MMS_HTTP_PAUSED;
                } else {
                    next_http_state = MMS_HTTP_DONE;
                    http_status = SOUP_STATUS_CANCELLED;
                }
            } else {
                next_http_state = MMS_HTTP_DONE;
415
                GWARN("HTTP error %u (%s)", msg->status_code,
416
                    soup_status_get_phrase(msg->status_code));
417 418 419 420 421
                mms_task_set_state(task, MMS_TASK_STATE_DONE);
            }
        }
        mms_task_http_set_state(http, next_http_state, http_status);
    } else {
422
        GVERBOSE_("ignoring stale completion message");
423 424 425 426 427 428 429 430 431
    }
}

static
void
mms_task_http_write_next_chunk(
    SoupMessage* msg,
    MMSTaskHttp* http)
{
Slava Monich's avatar
Slava Monich committed
432
    MMSTaskHttpPriv* priv = http->priv;
433 434
    MMSHttpTransfer* tx = priv->tx;
#if MMS_LOG_VERBOSE
435
    if (tx->bytes_sent) {
436
        GVERBOSE("%u bytes sent", tx->bytes_sent);
437
    }
438
#endif
439
    GASSERT(tx && tx->message == msg);
440 441 442 443
    if (tx && tx->message == msg) {
        void* chunk = g_malloc(MMS_HTTP_MAX_CHUNK);
        int nbytes = read(tx->send_fd, chunk, MMS_HTTP_MAX_CHUNK);
        if (nbytes > 0) {
444
            tx->bytes_sent += nbytes;
445
            soup_message_body_append_take(msg->request_body, chunk, nbytes);
446
            mms_task_http_send_progress(http);
447 448 449 450 451 452 453
            return;
        }
        g_free(chunk);
    }
    soup_message_body_complete(msg->request_body);
}

454 455 456 457 458 459 460 461
static
void
mms_task_http_got_headers(
    SoupMessage* msg,
    MMSTaskHttp* http)
{
    MMSTaskHttpPriv* priv = http->priv;
    MMSHttpTransfer* tx = priv->tx;
462
    GASSERT(tx && tx->message == msg);
463
    if (tx && tx->message == msg) {
464
        GASSERT(!tx->bytes_received);
465 466 467 468
        tx->bytes_to_receive = (guint)soup_message_headers_get_content_length(
            msg->response_headers);
#if MMS_LOG_VERBOSE
        if (tx->bytes_to_receive) {
469
            GVERBOSE("Receiving %u bytes", tx->bytes_to_receive);
470 471 472 473 474 475
        }
#endif
        mms_task_http_receive_progress(http);
    }
}

476 477 478 479 480 481 482
static
void
mms_task_http_got_chunk(
    SoupMessage* msg,
    SoupBuffer* buf,
    MMSTaskHttp* http)
{
Slava Monich's avatar
Slava Monich committed
483
    MMSTaskHttpPriv* priv = http->priv;
484
    MMSHttpTransfer* tx = priv->tx;
485
    GASSERT(tx && tx->message == msg);
486
    if (tx && tx->message == msg) {
487
        tx->bytes_received += buf->length;
488
        GVERBOSE("%u bytes received", tx->bytes_received);
489 490 491
        if (write(tx->receive_fd, buf->data, buf->length) == (int)buf->length) {
            mms_task_http_receive_progress(http);
        } else {
492
            GERR("Write error: %s", strerror(errno));
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
            mms_task_http_finish_transfer(http);
            mms_task_http_set_state(http, MMS_HTTP_PAUSED, 0);
            mms_task_set_state(&http->task, MMS_TASK_STATE_SLEEP);
        }
    }
}

static
gboolean
mms_task_http_start(
    MMSTaskHttp* http,
    MMSConnection* connection)
{
    int send_fd = -1;
    int receive_fd = -1;
    guint bytes_to_send = 0;
Slava Monich's avatar
Slava Monich committed
509
    MMSTaskHttpPriv* priv = http->priv;
510
    GASSERT(mms_connection_is_open(connection));
511 512 513 514
    mms_task_http_finish_transfer(http);

    /* Open the files */
    if (priv->send_path) {
515
        send_fd = open(priv->send_path, O_RDONLY | O_BINARY);
516 517 518 519 520 521
        if (send_fd >= 0) {
            struct stat st;
            int err = fstat(send_fd, &st);
            if (!err) {
                bytes_to_send = st.st_size;
            } else {
522
                GERR("Can't stat %s: %s", priv->send_path, strerror(errno));
523 524 525 526
                close(send_fd);
                send_fd = -1;
            }
        } else {
527
            GERR("Can't open %s: %s", priv->send_path, strerror(errno));
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
        }
    }

    if (priv->receive_file) {
        char* dir = mms_task_dir(&http->task);
        if (priv->receive_path) {
            unlink(priv->receive_path);
            g_free(priv->receive_path);
            priv->receive_path = NULL;
        }
        receive_fd = mms_create_file(dir, priv->receive_file,
            &priv->receive_path, NULL);
        g_free(dir);
    }

    if ((!priv->send_path || send_fd >= 0) &&
544 545
        (!priv->receive_path || receive_fd >= 0) &&
        (send_fd >= 0 || receive_fd >= 0)) {
546 547 548

        /* Set up the transfer */
        const char* uri = priv->uri ? priv->uri : connection->mmsc;
549 550 551
        priv->tx = mms_http_transfer_new(mms_task_sim_settings(&http->task),
            connection, priv->send_path ? SOUP_METHOD_POST : SOUP_METHOD_GET,
            uri, receive_fd, send_fd);
552
        if (priv->tx) {
553 554
            MMSHttpTransfer* tx = priv->tx;
            SoupMessage* msg = tx->message;
555 556 557 558
            soup_message_body_set_accumulate(msg->response_body, FALSE);

            /* If we have data to send */
            if (priv->send_path) {
559
                tx->bytes_to_send = bytes_to_send;
560 561 562 563 564 565 566 567
                soup_message_headers_set_content_type(
                    msg->request_headers,
                    MMS_CONTENT_TYPE, NULL);
                soup_message_headers_set_content_length(
                    msg->request_headers,
                    bytes_to_send);

                /* Connect the signals */
568
                tx->msg_signal_id[MMS_SOUP_MESSAGE_SIGNAL_WROTE_HEADERS] =
569 570
                    g_signal_connect(msg, "wrote_headers",
                    G_CALLBACK(mms_task_http_write_next_chunk), http);
571
                tx->msg_signal_id[MMS_SOUP_MESSAGE_SIGNAL_WROTE_CHUNK] =
572 573 574 575 576 577
                    g_signal_connect(msg, "wrote_chunk",
                    G_CALLBACK(mms_task_http_write_next_chunk), http);
            }

            /* If we expect to receive data */
            if (priv->receive_path) {
578 579 580 581 582
                tx->msg_signal_id[MMS_SOUP_MESSAGE_SIGNAL_GOT_HEADERS] =
                    g_signal_connect(msg, "got_headers",
                    G_CALLBACK(mms_task_http_got_headers), http);
                tx->msg_signal_id[MMS_SOUP_MESSAGE_SIGNAL_GOT_CHUNK] =
                    g_signal_connect(msg, "got_chunk",
583 584 585 586
                    G_CALLBACK(mms_task_http_got_chunk), http);
            }

            /* Start the transfer */
587
#if GUTIL_LOG_DEBUG
588 589
            if (priv->send_path) {
                if (priv->receive_path) {
590
                    GDEBUG("%s (%u bytes) -> %s -> %s", priv->send_path,
591 592
                        bytes_to_send, uri, priv->receive_path);
                } else {
593
                    GDEBUG("%s (%u bytes) -> %s", priv->send_path,
594 595 596
                        bytes_to_send, uri);
                }
            } else {
597
                GDEBUG("%s -> %s", uri, priv->receive_path);
598 599

            }
600
#endif /* GUTIL_LOG_DEBUG */
601 602

            mms_task_http_set_state(http, MMS_HTTP_ACTIVE, 0);
603 604 605 606 607

            /* Soup message queue will unref the message when it's finished
             * with it, so we need to add one more reference if we need to
             * keep the message pointer too. */
            g_object_ref(msg);
608 609 610 611 612
            soup_session_queue_message(priv->tx->session, msg,
                mms_task_http_finished, http);
            return TRUE;
        }
    }
613 614
    if (receive_fd >= 0) close(receive_fd);
    if (send_fd >= 0) close(send_fd);
615 616 617 618 619 620 621 622 623 624
    return FALSE;
}

static
void
mms_task_http_transmit(
    MMSTask* task,
    MMSConnection* conn)
{
    if (task->state != MMS_TASK_STATE_TRANSMITTING) {
625 626 627 628 629 630 631 632 633 634
        MMSTaskHttp* http = MMS_TASK_HTTP(task);
        if (mms_task_http_start(http, conn)) {
            mms_task_set_state(task, MMS_TASK_STATE_TRANSMITTING);
        } else {
            MMSTaskHttpClass* klass = MMS_TASK_HTTP_GET_CLASS(task);
            if (klass->fn_done) {
                klass->fn_done(http, NULL, SOUP_STATUS_IO_ERROR);
            }
            mms_task_set_state(task, MMS_TASK_STATE_DONE);
        }
635 636 637 638 639 640 641 642
    }
}

static
void
mms_task_http_run(
    MMSTask* task)
{
643 644 645 646
    MMSTaskHttp* http = MMS_TASK_HTTP(task);
    mms_task_set_state(task,
        (http->priv->connection_type == MMS_CONNECTION_TYPE_USER) ?
        MMS_TASK_STATE_NEED_USER_CONNECTION : MMS_TASK_STATE_NEED_CONNECTION);
647 648 649 650 651
}

static
void
mms_task_http_network_unavailable(
652 653
    MMSTask* task,
    gboolean can_retry)
654
{
655 656 657 658 659 660
    if (can_retry) {
        mms_task_http_finish_transfer(MMS_TASK_HTTP(task));
        mms_task_set_state(task, MMS_TASK_STATE_SLEEP);
    } else {
        mms_task_cancel(task);
    }
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
}

static
void
mms_task_http_cancel(
    MMSTask* task)
{
    MMSTaskHttp* http = MMS_TASK_HTTP(task);
    mms_task_http_finish_transfer(http);
    mms_task_http_set_state(http, MMS_HTTP_DONE, SOUP_STATUS_CANCELLED);
    MMS_TASK_CLASS(mms_task_http_parent_class)->fn_cancel(task);
}

/**
 * First stage of deinitialization (release all references).
 * May be called more than once in the lifetime of the object.
 */
static
void
mms_task_http_dispose(
    GObject* object)
{
    MMSTaskHttp* http = MMS_TASK_HTTP(object);
    mms_task_http_finish_transfer(http);
    G_OBJECT_CLASS(mms_task_http_parent_class)->dispose(object);
}

/**
 * Final stage of deinitialization
 */
static
void
mms_task_http_finalize(
    GObject* object)
{
    MMSTaskHttp* http = MMS_TASK_HTTP(object);
697
    MMSTaskHttpPriv* priv = http->priv;
698
    GASSERT(!priv->tx);
699
    if (!task_config(&http->task)->keep_temp_files) {
700 701
        mms_remove_file_and_dir(priv->send_path);
        mms_remove_file_and_dir(priv->receive_path);
702
    }
703 704 705 706 707 708
    g_free(priv->uri);
    g_free(priv->transfer_type);
    g_free(priv->send_path);
    g_free(priv->receive_path);
    g_free(priv->receive_file);
    mms_transfer_list_unref(http->transfers);
709 710 711 712 713 714
    G_OBJECT_CLASS(mms_task_http_parent_class)->finalize(object);
}

/**
 * Per class initializer
 */
Slava Monich's avatar
Slava Monich committed
715
static
716
void
Slava Monich's avatar
Slava Monich committed
717
mms_task_http_class_init(
718 719 720 721
    MMSTaskHttpClass* klass)
{
    GObjectClass* object_class = G_OBJECT_CLASS(klass);
    MMSTaskClass* task_class = &klass->task;
Slava Monich's avatar
Slava Monich committed
722
    g_type_class_add_private(klass, sizeof(MMSTaskHttpPriv));
723 724 725 726 727 728 729 730 731 732 733 734 735 736
    task_class->fn_run = mms_task_http_run;
    task_class->fn_transmit = mms_task_http_transmit;
    task_class->fn_network_unavailable = mms_task_http_network_unavailable;
    task_class->fn_cancel = mms_task_http_cancel;
    object_class->dispose = mms_task_http_dispose;
    object_class->finalize = mms_task_http_finalize;
}

/**
 * Per instance initializer
 */
static
void
mms_task_http_init(
Slava Monich's avatar
Slava Monich committed
737
    MMSTaskHttp*  http)
738
{
Slava Monich's avatar
Slava Monich committed
739 740
    http->priv = G_TYPE_INSTANCE_GET_PRIVATE(http, MMS_TYPE_TASK_HTTP,
        MMSTaskHttpPriv);
741 742 743 744 745 746 747 748
}

/**
 * Create MMS http task
 */
void*
mms_task_http_alloc(
    GType type,                 /* Zero for MMS_TYPE_TASK_HTTP       */
749
    MMSSettings* settings,      /* Settings                          */
750
    MMSHandler* handler,        /* MMS handler                       */
751 752
    MMSTransferList* transfers, /* Transfer list                     */
    const char* name,           /* Task name (and transfer type)     */
753 754 755 756
    const char* id,             /* Database message id               */
    const char* imsi,           /* IMSI associated with the message  */
    const char* uri,            /* NULL to use MMSC URL              */
    const char* receive_file,   /* File to write data to (optional)  */
757 758
    const char* send_file,      /* File to read data from (optional) */
    MMS_CONNECTION_TYPE ct)
759 760
{
    MMSTaskHttp* http = mms_task_alloc(type ? type : MMS_TYPE_TASK_HTTP,
761
        settings, handler, name, id, imsi);
Slava Monich's avatar
Slava Monich committed
762
    MMSTaskHttpPriv* priv = http->priv;
763
    http->transfers = mms_transfer_list_ref(transfers);
764
    priv->uri = g_strdup(uri);
765 766
    priv->receive_file = g_strdup(receive_file);
    priv->transfer_type = g_strdup(name);
767
    priv->connection_type = ct;
768 769
    if (send_file) {
        priv->send_path = mms_task_file(&http->task, send_file);
770
        GASSERT(g_file_test(priv->send_path, G_FILE_TEST_IS_REGULAR));
771 772 773 774
    }
    return http;
}

775 776 777 778
void*
mms_task_http_alloc_with_parent(
    GType type,                 /* Zero for MMS_TYPE_TASK_HTTP       */
    MMSTask* parent,            /* Parent task                       */
779
    MMSTransferList* transfers, /* Transfer list                     */
780 781 782 783 784 785
    const char* name,           /* Task name                         */
    const char* uri,            /* NULL to use MMSC URL              */
    const char* receive_file,   /* File to write data to (optional)  */
    const char* send_file)      /* File to read data from (optional) */
{
    return mms_task_http_alloc(type, parent->settings, parent->handler,
786 787
        transfers, name, parent->id, parent->imsi, uri, receive_file,
        send_file, MMS_CONNECTION_TYPE_AUTO);
788 789
}

790 791 792 793 794 795 796
/*
 * Local Variables:
 * mode: C
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 */