callable_obj.c 12 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *  Author: Julien Bonjean <julien.bonjean@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 19 20 21 22 23 24 25 26 27 28
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
29 30 31
 */

#include <callable_obj.h>
32
#include <codeclist.h>
33 34
#include <sflphone_const.h>
#include <time.h>
Julien Bonjean's avatar
Julien Bonjean committed
35
#include "contacts/calltree.h"
36 37
#include <unistd.h>
#include <assert.h>
38 39


40
gint get_state_callstruct (gconstpointer a, gconstpointer b)
41
{
42
    callable_obj_t * c = (callable_obj_t*) a;
43
    call_state_t state = *((call_state_t*)b);
44

45
    return c->_state == state ? 0 : 1;
46 47
}

48
gchar* call_get_peer_name (const gchar *format)
49
{
50 51
    const gchar *end = g_strrstr (format, "<");
    return g_strndup (format, end ? end - format : 0);
52 53
}

54
gchar* call_get_peer_number (const gchar *format)
55
{
56 57
    gchar *number = g_strrstr (format, "<") + 1;
    gchar *end = g_strrstr (format, ">");
58

59
    if (end && number)
60
        return g_strndup (number, end - number);
61
    else
62
        return g_strdup (format);
63 64
}

65 66
gchar* call_get_audio_codec (callable_obj_t *obj)
{
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    gchar *ret = NULL;
    gchar *audio_codec = NULL;
    if (!obj)
        goto out;

    audio_codec = dbus_get_current_audio_codec_name (obj);
    if (!audio_codec)
        goto out;

    account_t *acc = account_list_get_by_id(obj->_accountID);
    if (!acc)
        goto out;

    const codec_t *const codec = codec_list_get_by_name (audio_codec, acc->codecs);
    if (!codec)
        goto out;

    ret = g_strdup_printf("%s/%i", audio_codec, codec->sample_rate);

out:
    g_free(audio_codec);
    if (ret == NULL)
        return g_strdup("");
    return ret;
91
}
92

93
void call_add_error (callable_obj_t * call, gpointer dialog)
94 95 96 97
{
    g_ptr_array_add (call->_error_dialogs, dialog);
}

98
void call_remove_error (callable_obj_t * call, gpointer dialog)
99 100 101 102
{
    g_ptr_array_remove (call->_error_dialogs, dialog);
}

103
void call_remove_all_errors (callable_obj_t * call)
104 105 106 107
{
    g_ptr_array_foreach (call->_error_dialogs, (GFunc) gtk_widget_destroy, NULL);
}

108
static void threaded_clock_incrementer (void *pc)
Julien Bonjean's avatar
Julien Bonjean committed
109 110
{
    callable_obj_t *call = (callable_obj_t *) pc;
111

112
    for(;;) {
Julien Bonjean's avatar
Julien Bonjean committed
113
        gdk_threads_enter ();
114

115
        int duration = difftime (time(NULL), call->_time_start);
Julien Bonjean's avatar
Julien Bonjean committed
116

117
        g_snprintf (call->_timestr, 20, "%.2d:%.2d", duration % 60, duration / 60);
118

Julien Bonjean's avatar
Julien Bonjean committed
119 120 121 122 123 124 125 126 127
        // Update clock only if call is active (current, hold, recording transfer)
        if ( (call->_state != CALL_STATE_INVALID) &&
                (call->_state != CALL_STATE_INCOMING) &&
                (call->_state != CALL_STATE_RINGING) &&
                (call->_state != CALL_STATE_DIALING) &&
                (call->_state != CALL_STATE_FAILURE) &&
                (call->_state != CALL_STATE_BUSY)) {
            calltree_update_clock();
        }
128

Julien Bonjean's avatar
Julien Bonjean committed
129 130
        gdk_threads_leave ();

131 132 133 134 135 136 137
        GTimeVal abs_time;
        g_get_current_time(&abs_time);
        g_time_val_add(&abs_time, 1000000);
        g_mutex_lock(call->mutex);
        g_cond_timed_wait(call->cond, call->mutex, &abs_time);
        gboolean ret = call->exitClockThread;
        g_mutex_unlock(call->mutex);
Julien Bonjean's avatar
Julien Bonjean committed
138

139 140
        if (ret == TRUE)
            break;
Julien Bonjean's avatar
Julien Bonjean committed
141
    }
142

143 144 145 146 147
    g_thread_exit (NULL);
}

void stop_call_clock (callable_obj_t *c)
{
148 149 150 151 152 153 154 155 156 157 158 159
    assert(c->_type == CALL);

    g_mutex_lock(c->mutex);
    c->exitClockThread = TRUE;
    g_cond_signal(c->cond);
    g_mutex_unlock(c->mutex);

    g_thread_join(c->clock_thread);
    g_mutex_free(c->mutex);
    g_cond_free(c->cond);

    c->clock_thread = NULL;
160 161
}

162
callable_obj_t *create_new_call (callable_type_t type, call_state_t state,
163 164 165
                      const gchar* const callID,
                      const gchar* const accountID,
                      const gchar* const peer_name,
166
                      const gchar* const peer_number)
167
{
Rafaël Carré's avatar
Rafaël Carré committed
168
    DEBUG ("CallableObj: Create new call (Account: %s)", accountID);
169

Rafaël Carré's avatar
Rafaël Carré committed
170
    callable_obj_t *obj = g_new0 (callable_obj_t, 1);
171

172
    obj->_error_dialogs = g_ptr_array_new();
173 174
    obj->_type = type;
    obj->_state = state;
175
    obj->_callID = *callID ? g_strdup (callID) : g_strdup_printf("%d", rand());
176 177
    obj->_accountID = g_strdup (accountID);

178 179
    time (&obj->_time_start);
    time (&obj->_time_stop);
180 181 182 183

    obj->_peer_name = g_strdup (peer_name);
    obj->_peer_number = g_strdup (peer_number);
    obj->_peer_info = get_peer_info (peer_name, peer_number);
184

Julien Bonjean's avatar
Julien Bonjean committed
185
    if (obj->_type == CALL) {
186 187 188 189
        obj->mutex = g_mutex_new();
        obj->cond = g_cond_new();
        obj->exitClockThread = FALSE;
        obj->clock_thread = g_thread_create((GThreadFunc) threaded_clock_incrementer, (void *) obj, TRUE, NULL);
190
    }
191

192
    return obj;
193 194
}

195
callable_obj_t *create_new_call_from_details (const gchar *call_id, GHashTable *details)
196 197 198
{
    call_state_t state;

199 200 201 202
    const gchar * const accountID = g_hash_table_lookup (details, "ACCOUNTID");
    const gchar * const peer_number = g_hash_table_lookup (details, "PEER_NUMBER");
    const gchar * const peer_name = g_hash_table_lookup (details, "DISPLAY_NAME");
    const gchar * const state_str = g_hash_table_lookup (details, "CALL_STATE");
203

204 205
    if (g_strcasecmp (state_str, "CURRENT") == 0)
        state = CALL_STATE_CURRENT;
206 207 208 209
    else if (g_strcasecmp (state_str, "RINGING") == 0)
        state = CALL_STATE_RINGING;
    else if (g_strcasecmp (state_str, "INCOMING") == 0)
        state = CALL_STATE_INCOMING;
210 211 212 213 214 215 216
    else if (g_strcasecmp (state_str, "HOLD") == 0)
        state = CALL_STATE_HOLD;
    else if (g_strcasecmp (state_str, "BUSY") == 0)
        state = CALL_STATE_BUSY;
    else
        state = CALL_STATE_FAILURE;

217 218 219 220
    gchar *number = call_get_peer_number (peer_number);
    callable_obj_t *c = create_new_call (CALL, state, call_id, accountID, peer_name, number);
    g_free(number);
    return c;
221 222
}

223
callable_obj_t *create_history_entry_from_serialized_form (const gchar *entry)
224
{
225 226 227 228 229 230 231 232 233
    const gchar *peer_name = "";
    const gchar *peer_number = "";
    const gchar *callID = "";
    const gchar *accountID = "";
    const gchar *time_start = "";
    const gchar *time_stop = "";
    const gchar *recordfile = "";
    const gchar *confID = "";
    const gchar *time_added = "";
234
    history_state_t history_state = MISSED;
235 236 237 238 239

    gchar **ptr_orig = g_strsplit(entry, "|", 10);
    gchar **ptr;
    gint token;
    for (ptr = ptr_orig, token = 0; ptr && token < 10; token++, ptr++)
240
        switch (token) {
241 242 243 244 245 246 247 248 249 250 251
            case 0:     history_state = get_history_state_from_id (*ptr); break;
            case 1:     peer_number = *ptr;     break;
            case 2:     peer_name = *ptr;       break;
            case 3:     time_start = *ptr;      break;
	    case 4:     time_stop = *ptr;       break;
	    case 5:     callID = *ptr;          break;
            case 6:     accountID = *ptr;       break;
            case 7:     recordfile = *ptr;      break;
	    case 8:     confID = *ptr;          break;
	    case 9:     time_added = *ptr;      break;
            default:                            break;
252
        }
253

254
    if (g_strcasecmp (peer_name, "empty") == 0)
Alexandre Savard's avatar
Alexandre Savard committed
255
        peer_name = "";
256

257
    callable_obj_t *new_call = create_new_call (HISTORY_ENTRY, CALL_STATE_DIALING, callID, accountID, peer_name, peer_number);
258
    new_call->_history_state = history_state;
259 260
    new_call->_time_start = atoi(time_start);
    new_call->_time_stop = atoi(time_stop);
261
    new_call->_recordfile = g_strdup(recordfile);
Alexandre Savard's avatar
Alexandre Savard committed
262
    new_call->_confID = g_strdup(confID);
263
    new_call->_historyConfID = g_strdup(confID);
264
    new_call->_time_added = atoi(time_start);
265
    new_call->_record_is_playing = FALSE;
266

267
    g_strfreev(ptr_orig);
268
    return new_call;
269 270 271 272 273
}

void free_callable_obj_t (callable_obj_t *c)
{
    g_free (c->_callID);
274 275
    g_free (c->_confID);
    g_free (c->_historyConfID);
276
    g_free (c->_accountID);
277 278
    g_free (c->_srtp_cipher);
    g_free (c->_sas);
279 280
    g_free (c->_peer_name);
    g_free (c->_peer_number);
281
    g_free (c->_trsft_to);
282
    g_free (c->_peer_info);
283
    g_free (c->_recordfile);
284

285
    g_free (c);
286

287
    calltree_update_clock();
288 289
}

290
gchar* get_peer_info (const gchar* const number, const gchar* const name)
291
{
292
    return g_strconcat ("\"", name, "\" <", number, ">", NULL);
293
}
294

295 296
history_state_t get_history_state_from_id (gchar *indice)
{
297
    history_state_t state = atoi(indice);
298

299
    if (state > LAST)
300
        state = MISSED;
301

302 303
    return state;
}
304

305
gchar* get_call_duration (callable_obj_t *obj)
306
{
307 308 309 310
    long duration = difftime (obj->_time_stop, obj->_time_start);
    if (duration < 0)
        duration = 0;
    return g_strdup_printf("<small>Duration:</small> %.2ld:%.2ld" , duration/60 , duration%60);
311 312
}

313 314 315 316 317 318 319 320
static const gchar* get_history_id_from_state (history_state_t state)
{
    static const gchar *tab[LAST] = { "0", "1", "2" };
    if (state >= LAST)
        return "";
    return tab[state];
}

321
gchar* serialize_history_call_entry (callable_obj_t *entry)
322
{
323
    // "0|514-276-5468|Savoir-faire Linux|144562458" for instance
324
    gchar *peer_number, *peer_name, *account_id;
325
    static const gchar * const separator = "|";
326 327 328
    gchar *time_start, *time_stop ;
    gchar *record_file;
    gchar *confID , *time_added;
Alexandre Savard's avatar
Alexandre Savard committed
329

330
    // Need the string form for the history state
331
    const gchar *history_state = get_history_id_from_state (entry->_history_state);
332
    // and the timestamps
333 334 335
    time_start = g_strdup_printf ("%i", (int) entry->_time_start);
    time_stop = g_strdup_printf ("%i", (int) entry->_time_stop);
    time_added = g_strdup_printf ("%i", (int) entry->_time_added);
336

337 338 339
    peer_number = entry->_peer_number ? entry->_peer_number : "";
    peer_name = (entry->_peer_name && *entry->_peer_name) ? entry->_peer_name : "empty";
    account_id = (entry->_accountID && *entry->_accountID) ? entry->_accountID : "empty";
340

341 342
    confID = entry->_historyConfID ? entry->_historyConfID : "";
    record_file = entry->_recordfile ? entry->_recordfile : "";
343

344
    gchar *result = g_strconcat (history_state, separator,
345
                          entry->_peer_number, separator,
346
                          peer_name, separator,
347 348
                          time_start, separator,
			  time_stop, separator,
349
			  entry->_callID, separator,
350
                          account_id, separator,
Alexandre Savard's avatar
Alexandre Savard committed
351 352
			  record_file, separator,
			  confID, separator,
353
			  time_added, NULL);
354 355 356
    g_free(time_start);
    g_free(time_stop);
    g_free(time_added);
357 358 359
    return result;
}

360
gchar *get_formatted_start_timestamp (time_t start)
361
{
362 363 364 365 366 367 368 369 370 371 372 373
    time_t now = time (NULL);
    struct tm start_tm;

    localtime_r (&start, &start_tm);
    time_t diff = now - start;
    if (diff < 0)
        diff = 0;
    const char *fmt;

    if (diff < 60 * 60 * 24 * 7) { // less than 1 week
        if (diff < 60 * 60 * 24) { // less than 1 day
            fmt = N_("today at %R");
374
        } else {
375 376 377 378
            if (diff < 60 * 60 * 24 * 2) { // less than 2 days
                fmt = N_("yesterday at %R");
            } else { // between 2 days and 1 week
                fmt = N_("%A at %R");
379 380
            }
        }
381 382
    } else { // more than 1 week
        fmt = N_("%x at %R");
383
    }
384

385 386 387
    char str[100];
    strftime(str, sizeof str, fmt, &start_tm);
    return g_markup_printf_escaped ("%s\n", str);
388
}