~ubuntu-branches/ubuntu/trusty/isdnutils/trusty

« back to all changes in this revision

Viewing changes to ant-phone/src/callerid.c

  • Committer: Package Import Robot
  • Author(s): Steve Langasek
  • Date: 2013-11-15 00:02:58 UTC
  • mfrom: (1.1.8) (31.1.1 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20131115000258-tt9v3gasgrdml07k
Tags: 1:3.25+dfsg1-3.3ubuntu1
* Merge from Debian unstable:
  - resolves licensing issues with package contents.  LP: #511988.
  - includes proper upstream fix for ipppd on ARM.  LP: #453159.
  - resolves isdnutils-base removal failures.  LP: #813771.
  - fixes capiutils init script to not try to mount obsolete capifs.
    LP: #1064347.
* Remaining changes:
  - Switch libreadline5-dev to libreadline-gplv2-dev since this package
    appears to be GPLv2
  - debian/patches/no-imake.patch: Don't build xisdnload/xmonisdn using
    xmkmf/imake.  This patch was dropped in Debian without explanation;
    it still applies and seems to still be a good idea for eventual
    upstreaming, since imake is quite obsolete.
  - capi.conf: Fix typo for fcdsl2 firmware. LP: #189132.
  - Remove dependencies on /etc/inittab.
    - Disable the installation code to modify /etc/inittab.
    - isdnutils-base: Add ttyI0 example script, which needs to be installed
      in /etc/event.d/ttyI0.
    - isdnvboxserver: Add ttyI1 example script, which needs to be installed
      in /etc/event.d/ttyI1.
    - The two upstart scripts need to be edited.
    - Further improvements and documentation welcome.
* Changes included in Debian:
  - replace calls to ./MAKEDEV with /sbin/MAKEDEV
  - Build-depend on ppp-dev.
  - Switch to newer tcl -dev.
  - update to newer automake
  - debian/rules: use autoreconf to update the autotools in the capi20
    directory
  - debian/{compat,rules,*.files,.dirs}: Convert to Multi-arch.
  - debian/libcapi20-dev.install: Remove .la files (no builds use them).
* Changes included upstream:
  - fix for ARM FTBFS.
  - fix bashisms in vboxplay.
  - debian/patches/{config_libdir,toplevel-make}.patch: add CONFIG_LIBDIR
    override to upstream build system to support Multi-arch.
* Dropped changes:
  - kick dpatch to the curb.
* Handle migrating the blacklist file from
  /etc/modprobe.d/blacklist-capiutils.conf to the path used in Debian,
  /etc/modprobe.d/capiutils.conf.
* Handle rename of /etc/ppp/ip-down.d/99-ipppd and /etc/ppp/ip-up.d/00-ipppd
  to /etc/ppp/ip-down.d/ipppd and /etc/ppp/ip-up.d/ipppd
* Handle rename of /etc/init.d/isdnutils to /etc/init.d/isdnutils-base
* Restore standard.tcl to /usr/share/isdnvboxserver/default; maintainer
  scripts must not depend on contents of /usr/share/doc.
* Apply patches that were preserved in the 3.0 (quilt) migration, but
  were inadvertently not applied:
  - debian/patches/capifax.additional_error_codes.patch
  - debian/patches/capifax.3_1kHz_audio.patch
* Drop debian/isdnutils-base.cron.d, which isn't a cronjob example at all
  but an inittab example gone astray.
* debian/dotconfig*: don't use embedded quotes for paths; this confuses
  vbox's Makefiles something fierce, and causes files to be missed from
  debian/tmp'/usr/share/man/' at install time.
* Fix isdnlog and ipppd to not ship files used in the postinst under
  /usr/share/doc.
* Modernize the upstart examples.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * caller id related functions
3
 
 *
4
 
 * This file is part of ANT (Ant is Not a Telephone)
5
 
 *
6
 
 * Copyright 2002, 2003 Roland Stigge
7
 
 *
8
 
 * ANT is free software; you can redistribute it and/or modify
9
 
 * it under the terms of the GNU General Public License as published by
10
 
 * the Free Software Foundation; either version 2 of the License, or
11
 
 * (at your option) any later version.
12
 
 *
13
 
 * ANT is distributed in the hope that it will be useful,
14
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 
 * GNU General Public License for more details.
17
 
 *
18
 
 * You should have received a copy of the GNU General Public License
19
 
 * along with ANT; if not, write to the Free Software
20
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
 
 *
22
 
 */
23
 
 
24
 
#include "config.h"
25
 
 
26
 
/* regular GNU system includes */
27
 
#include <stdio.h>
28
 
#ifdef HAVE_STDLIB_H
29
 
  #include <stdlib.h>
30
 
#endif
31
 
#include <unistd.h>
32
 
#include <math.h>
33
 
#include <time.h>
34
 
#include <string.h>
35
 
#include <ctype.h>
36
 
#include <glob.h>
37
 
#include <fcntl.h>
38
 
 
39
 
/* GTK */
40
 
#include <gtk/gtk.h>
41
 
#include <gdk/gdkkeysyms.h>
42
 
 
43
 
/* own header files */
44
 
#include "globals.h"
45
 
#include "callerid.h"
46
 
#include "session.h"
47
 
#include "util.h"
48
 
#include "isdn.h"
49
 
 
50
 
/* graphical symbols */
51
 
#include "in.xpm"
52
 
#include "out.xpm"
53
 
#include "record.xpm"
54
 
 
55
 
/*
56
 
 * returns a new data structure (for association with a row)
57
 
 * filled with default values
58
 
 */
59
 
static gpointer cid_row_new(void) {
60
 
  struct cid_row_t *rowdata =
61
 
    (struct cid_row_t *) malloc(sizeof(struct cid_row_t));
62
 
  
63
 
  rowdata->marked_unanswered = 0;
64
 
  return (gpointer) rowdata;
65
 
}
66
 
 
67
 
/*
68
 
 * callback to be called for additional row data on destruction of row
69
 
 */
70
 
static void cid_row_destroy(gpointer rowdata) {
71
 
  if (debug > 1)
72
 
    fprintf(stderr, "debug: destroying rowdata at %p.\n", rowdata);
73
 
  free(rowdata);
74
 
}
75
 
 
76
 
/* called when caller id monitor state check button is toggled */
77
 
void cid_toggle_cb(GtkWidget *widget _U_, gpointer data, guint action _U_) {
78
 
  session_t *session = (session_t *) data;
79
 
 
80
 
  if (GTK_CHECK_MENU_ITEM (session->cid_check_menu_item)->active) {
81
 
    gtk_widget_show(session->cid);
82
 
    session->option_show_callerid = 1;
83
 
  } else {
84
 
    gtk_widget_hide(session->cid);
85
 
    session->option_show_callerid = 0;
86
 
    gtk_window_resize(GTK_WINDOW(session->main_window), 1, 1);
87
 
  }
88
 
}
89
 
 
90
 
/*
91
 
 * Callback: called when a button was clicked in row delete dialog
92
 
 */
93
 
static void cid_delete_response_cb(GtkWidget* widget, gint response_id,
94
 
                                   gpointer data)
95
 
{
96
 
  session_t* session = (session_t*) data;
97
 
  guint row = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "row"));
98
 
  char* filename = (char*) g_object_get_data(G_OBJECT(widget), "filename");
99
 
  GtkWidget* checkbutton =
100
 
    (GtkWidget*) g_object_get_data(G_OBJECT(widget), "checkbutton");
101
 
 
102
 
  if (response_id == GTK_RESPONSE_OK) {
103
 
    if (filename &&
104
 
        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton)))
105
 
    {
106
 
      unlink(filename);
107
 
    }
108
 
    cid_mark_row(session, row, FALSE); /* to count unanswered calls */
109
 
    session->cid_num--;
110
 
    /* decrease before removal: "select-row" signal submission -> other cb */
111
 
    gtk_clist_remove(GTK_CLIST(session->cid_list), row);
112
 
  }
113
 
  
114
 
  if (filename) free(filename);
115
 
  gtk_widget_destroy(widget);
116
 
}
117
 
 
118
 
/*
119
 
 * called on cid delete request
120
 
 *
121
 
 * -> deletes specified row without confirmation
122
 
 */
123
 
static void cid_request_delete(GtkWidget *widget _U_, gpointer data,
124
 
                               guint row) {
125
 
  session_t* session = (session_t *) data;
126
 
  GtkWidget* vbox;
127
 
  GtkWidget* label;
128
 
  GtkWidget* checkbutton;
129
 
  char *filename = cid_get_record_filename(session, row);
130
 
  
131
 
  GtkWidget* dialog = gtk_dialog_new_with_buttons(_("Delete Entry"),
132
 
                         GTK_WINDOW(session->main_window),
133
 
                         GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
134
 
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
135
 
                         GTK_STOCK_OK, GTK_RESPONSE_OK,
136
 
                         NULL);
137
 
 
138
 
  vbox = gtk_vbox_new(FALSE, 16);
139
 
  gtk_container_set_border_width(GTK_CONTAINER(vbox), 16);
140
 
  gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox);
141
 
  gtk_widget_show(vbox);
142
 
  
143
 
  label = gtk_label_new(_("Are you sure you want to\ndelete this entry?"));
144
 
  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
145
 
  gtk_box_pack_start_defaults(GTK_BOX(vbox), label);
146
 
  gtk_widget_show(label);
147
 
 
148
 
  checkbutton = gtk_check_button_new_with_label(_("Delete recording"));
149
 
  if (filename) { /* default: checked */
150
 
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
151
 
  } else { /* if file doesn't exist, grey out */
152
 
    gtk_widget_set_sensitive(checkbutton, FALSE);
153
 
  }
154
 
  gtk_box_pack_start_defaults(GTK_BOX(vbox), checkbutton);
155
 
  gtk_widget_show(checkbutton);
156
 
 
157
 
  g_object_set_data(G_OBJECT(dialog), "row", GUINT_TO_POINTER(row));
158
 
  g_object_set_data(G_OBJECT(dialog), "filename", (gpointer) filename);
159
 
  g_object_set_data(G_OBJECT(dialog), "checkbutton", (gpointer) checkbutton);
160
 
  g_signal_connect(dialog, "response", G_CALLBACK(cid_delete_response_cb),
161
 
                   session);
162
 
  gtk_widget_show(dialog);
163
 
}
164
 
 
165
 
/*
166
 
 * called on key pressed in cid list
167
 
 */
168
 
static gint cid_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) {
169
 
  
170
 
  switch (event->keyval) {
171
 
    case GDK_Delete:     /* Delete dialog */
172
 
    case GDK_KP_Delete:
173
 
      /* portability: GtkCList.selection is private! */
174
 
      if (GTK_CLIST(((session_t *) data)->cid_list)->selection) {
175
 
        cid_request_delete(widget, data,
176
 
            GPOINTER_TO_INT(GTK_CLIST(((session_t *) data)->cid_list)->selection->data));
177
 
      }
178
 
      return TRUE; /* event handled */
179
 
    default:
180
 
      return FALSE;
181
 
  }
182
 
}
183
 
 
184
 
/*
185
 
 * Callback: called on playback request
186
 
 */
187
 
static void cid_playback(GtkWidget *widget _U_, gpointer data, guint row) {
188
 
  session_t *session = (session_t *) data;
189
 
 
190
 
  session->effect_filename = cid_get_record_filename(session, row);
191
 
  session->effect_playback_start_time = time(NULL);
192
 
  session_set_state(session, STATE_PLAYBACK);
193
 
}
194
 
 
195
 
/*
196
 
 * Callback: called on "OK" click in "Save as..." file selection
197
 
 */
198
 
static void cid_save_as_filename_cb(GtkWidget *fs) {
199
 
  char* sourcename = g_object_get_data(G_OBJECT(fs), "sourcename");
200
 
  const char* destbase =
201
 
    gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs));
202
 
  if (*destbase && *basename(destbase)) {
203
 
    /* copy if something was entered */
204
 
    char* destination;
205
 
    char* extension = filename_extension(sourcename);
206
 
    if (0 <= asprintf(&destination, "%s.%s", destbase, extension)) {
207
 
      /* actually copy if source and destination known */
208
 
      int buffer_size = 65536;
209
 
      char* buffer = (char*) malloc(buffer_size);
210
 
      if (buffer) {
211
 
        int fd_in = open(sourcename, O_RDONLY);
212
 
        if (fd_in != -1) {
213
 
          int fd_out = open(destination, O_WRONLY | O_CREAT | O_TRUNC,
214
 
                            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
215
 
          if (fd_out != -1) {
216
 
            int just_read;
217
 
            do {
218
 
              int to_write = just_read = read(fd_in, buffer, buffer_size);
219
 
              int written = 1;
220
 
              char* buffer_index = buffer;
221
 
              while (to_write && written) {
222
 
                written = write(fd_out, buffer_index, to_write);
223
 
                to_write -= written;
224
 
                buffer_index += written;
225
 
              }
226
 
            } while (just_read);
227
 
            if (close(fd_out)) {
228
 
              fprintf(stderr, "Error on closing destination file.\n");
229
 
            }
230
 
          } else {
231
 
            fprintf(stderr,
232
 
                    "Error on destination file (%s) open().\n", destination);
233
 
          }
234
 
          if (close(fd_in)) {
235
 
            fprintf(stderr, "Error on source file.\n");
236
 
          }
237
 
        } else {
238
 
          fprintf(stderr,
239
 
                  "Error on source file (%s) open().\n", sourcename);
240
 
        }
241
 
        free(buffer);
242
 
      } else {
243
 
        fprintf(stderr, "Error on malloc().\n");
244
 
      }
245
 
      free(destination);
246
 
    } else {
247
 
      fprintf(stderr, "Error on asprintf().\n");
248
 
    }
249
 
  }
250
 
  free(sourcename);
251
 
  gtk_widget_destroy(GTK_WIDGET(fs));
252
 
}
253
 
 
254
 
/*
255
 
 * Callback: called on "Save as..." request
256
 
 */
257
 
static void cid_save_as(GtkWidget *widget _U_, gpointer data, guint row) {
258
 
  session_t* session = (session_t*) data;
259
 
  char* sourcename = cid_get_record_filename(session, row);
260
 
  char* extension = filename_extension(sourcename);
261
 
 
262
 
  if (extension) {
263
 
    GtkWidget* fs;
264
 
    char* title;
265
 
    
266
 
    if (0 > asprintf(&title, _("Enter the base filename for %s file"),
267
 
                     extension))
268
 
    {
269
 
      fprintf(stderr, "Error on asprintf().\n");
270
 
      return;
271
 
    }
272
 
    fs = gtk_file_selection_new(title);
273
 
    free(title);
274
 
    g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button),
275
 
                             "clicked", G_CALLBACK(gtk_widget_destroy),
276
 
                             (gpointer) fs);
277
 
    g_object_set_data(G_OBJECT(fs), "sourcename", sourcename);
278
 
    g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
279
 
                           "clicked", G_CALLBACK(cid_save_as_filename_cb), fs);
280
 
    gtk_widget_show(fs);
281
 
  } else {
282
 
    fprintf(stderr, "Error: no filename extension found.\n");
283
 
  }
284
 
}
285
 
 
286
 
/*
287
 
 * Callback: called if something happened in the delete recording dialog
288
 
 */
289
 
static void cid_delete_rec_response_cb(GtkWidget* widget, gint response_id,
290
 
                                       gpointer data)
291
 
{
292
 
  session_t *session = (session_t *) data;
293
 
 
294
 
  if (response_id == GTK_RESPONSE_OK) {
295
 
    char* temp;
296
 
    guint row = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "row"));
297
 
    unlink(temp = cid_get_record_filename(session, row));
298
 
    free(temp);
299
 
    cid_row_mark_record(session, row);
300
 
  }
301
 
  gtk_widget_destroy(widget);
302
 
}
303
 
 
304
 
/*
305
 
 * Callback: called on delete recording request
306
 
 */
307
 
static void cid_delete_rec(GtkWidget *widget _U_, gpointer data, guint row) {
308
 
  session_t *session = (session_t *) data;
309
 
 
310
 
  GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(session->main_window),
311
 
      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING,
312
 
      GTK_BUTTONS_OK_CANCEL, _("Do you really want to delete this recording?"));
313
 
  g_signal_connect(dialog, "response", G_CALLBACK(cid_delete_rec_response_cb),
314
 
                   session);
315
 
  gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
316
 
  g_object_set_data(G_OBJECT(dialog), "row", GINT_TO_POINTER(row));
317
 
  gtk_widget_show(dialog);
318
 
}
319
 
 
320
 
/*
321
 
 * called on mouse button pressed in cid list
322
 
 */
323
 
static gint cid_mouse_cb(GtkWidget *widget _U_,
324
 
                         GdkEventButton *event, gpointer data) {
325
 
  session_t *session = (session_t *) data;
326
 
  
327
 
  if (event->button == 3) { /* popup menu */
328
 
    gint row;
329
 
    gint column;
330
 
 
331
 
    if (gtk_clist_get_selection_info(GTK_CLIST(session->cid_list),
332
 
                                     event->x, event->y, &row, &column)) {
333
 
 
334
 
      GtkItemFactoryEntry menu_items[] = {
335
 
/*path                   accel. callb.         cb param. kind           extra */
336
 
{_("/_Playback"),        NULL,  cid_playback,  row,      "",            NULL},
337
 
{_("/_Save as..."),      NULL,  cid_save_as,   row,      "",            NULL},
338
 
{_("/Delete _Recording"),NULL,  cid_delete_rec,row,      "",            NULL},
339
 
{  "/Sep",               NULL,  NULL,          0,        "<Separator>", NULL},
340
 
{_("/_Delete Row"),      NULL,  cid_request_delete, row, "",            NULL}
341
 
      };
342
 
      GtkMenu *menu = GTK_MENU(gtk_menu_new());
343
 
      GtkItemFactory *item_factory;
344
 
      GtkAccelGroup *accel_group;
345
 
 
346
 
      gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
347
 
 
348
 
      GtkWidget *playback_item;
349
 
      GtkWidget *save_as_item;
350
 
      GtkWidget *delete_record_item;
351
 
      GtkWidget *delete_item;
352
 
      char* fn;
353
 
      char* temp;
354
 
  
355
 
      accel_group = gtk_accel_group_new();
356
 
      item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<popup>",
357
 
                                          accel_group);
358
 
      gtk_item_factory_create_items_ac(item_factory, nmenu_items, menu_items,
359
 
                                   session, 2);
360
 
      
361
 
      if (!(playback_item = gtk_item_factory_get_item(item_factory,
362
 
          temp = stripchr(_("/_Playback"), '_'))))
363
 
        fprintf(stderr, "Error getting playback_item.\n");
364
 
      free(temp);
365
 
      if (!(save_as_item = gtk_item_factory_get_item(item_factory,
366
 
          temp = stripchr(_("/_Save as..."), '_'))))
367
 
        fprintf(stderr, "Error getting save_as_item.\n");
368
 
      free(temp);
369
 
      if (!(delete_record_item = gtk_item_factory_get_item(item_factory,
370
 
            temp = stripchr(_("/Delete _Recording"), '_'))))
371
 
        fprintf(stderr, "Error getting delete_record_item_item.\n");
372
 
      free(temp);
373
 
      if (!(delete_item = gtk_item_factory_get_item(item_factory,
374
 
          temp = stripchr(_("/_Delete Row"), '_'))))
375
 
        fprintf(stderr, "Error getting delete_item.\n");
376
 
      free(temp);
377
 
 
378
 
      if (!(session->state == STATE_READY &&
379
 
            (fn = cid_get_record_filename(session, row)))) {
380
 
        gtk_widget_set_sensitive(playback_item, FALSE);
381
 
        gtk_widget_set_sensitive(save_as_item, FALSE);
382
 
        gtk_widget_set_sensitive(delete_record_item, FALSE);
383
 
      } else {
384
 
        free(fn);
385
 
      }
386
 
  
387
 
      menu = GTK_MENU(gtk_item_factory_get_widget(item_factory, "<popup>"));
388
 
      gtk_menu_set_accel_group(menu, accel_group);
389
 
 
390
 
      gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
391
 
    }
392
 
    return TRUE; /* event handled */
393
 
  } else {
394
 
    return FALSE;
395
 
  }
396
 
}
397
 
 
398
 
/*
399
 
 * called on row select (to unmark lines)
400
 
 */
401
 
static void cid_select_row_cb(GtkCList *list _U_, gint row, gint column _U_,
402
 
                              GdkEventButton *button _U_, gpointer data) {
403
 
  session_t *session = (session_t *) data;
404
 
 
405
 
  if (row < session->cid_num)
406
 
    cid_mark_row(session, row, FALSE);
407
 
}
408
 
 
409
 
/*
410
 
 * Returns the Caller ID section as GtkWidget,
411
 
 *   sets CID related members in session
412
 
 * NOTE: caller has to gtk_widget_show() this widget
413
 
 */
414
 
GtkWidget *cid_new(session_t *session) {
415
 
  GtkWidget *frame;           /* frame with comment */
416
 
  GtkWidget *scrolled_window; /* the home for the clist */
417
 
  GtkWidget *clist;           /* the list itself */
418
 
  GtkStyle *style;            /* needed for preparing symbols */
419
 
  GtkWidget *pixmapwidget;    /* record symbol */
420
 
  gchar *titles[CID_COL_NUMBER];
421
 
 
422
 
  style = gtk_widget_get_style(session->main_window);
423
 
  frame = gtk_frame_new(_("Caller ID"));
424
 
 
425
 
  titles[CID_COL_FLAGS   ] = "";
426
 
  titles[CID_COL_TIME    ] = _("Date/Time");
427
 
  titles[CID_COL_TYPE    ] = _("Type");
428
 
  titles[CID_COL_FROM    ] = _("From");
429
 
  titles[CID_COL_TO      ] = _("To");
430
 
  titles[CID_COL_DURATION] = _("Duration");
431
 
  
432
 
  gtk_container_set_border_width(GTK_CONTAINER(frame), 8);
433
 
 
434
 
  /* the scrolled window: no special adjustments */
435
 
  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
436
 
  /* always vertical scroll bar, horizontal... well... automatic means no ;) */
437
 
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
438
 
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
439
 
  gtk_container_add(GTK_CONTAINER(frame), scrolled_window);
440
 
  gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 8);
441
 
  gtk_widget_show(scrolled_window);
442
 
 
443
 
  /* the list widget */
444
 
  clist = gtk_clist_new_with_titles(CID_COL_NUMBER, titles);
445
 
  session->symbol_record_pixmap = gdk_pixmap_create_from_xpm_d(
446
 
                                         session->main_window->window,
447
 
                                         &session->symbol_record_bitmap,
448
 
                                         &style->bg[GTK_STATE_NORMAL],
449
 
                                         (gchar **) record_xpm);
450
 
  pixmapwidget = gtk_pixmap_new(session->symbol_record_pixmap,
451
 
                                session->symbol_record_bitmap);
452
 
  gtk_clist_set_column_widget(GTK_CLIST(clist), CID_COL_FLAGS, pixmapwidget);
453
 
  
454
 
  /* default: GTK_SELECTION_MULTIPLE: */
455
 
  gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);
456
 
 
457
 
  gtk_clist_set_shadow_type(GTK_CLIST(clist), GTK_SHADOW_IN);
458
 
  gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_FLAGS, 14);
459
 
  gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TIME, 128);
460
 
  gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TYPE, 30);
461
 
  gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_FROM, 100);
462
 
  gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TO, 100);
463
 
  gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_DURATION, 80);
464
 
  /*gtk_clist_set_column_width(GTK_CLIST(clist), 5, 80);*/
465
 
  gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
466
 
  gtk_clist_column_titles_show(GTK_CLIST(clist));
467
 
  gtk_widget_set_size_request(clist, 500, 128);
468
 
 
469
 
  gtk_signal_connect(GTK_OBJECT(clist), "key-press-event",
470
 
                     GTK_SIGNAL_FUNC(cid_key_cb), session);
471
 
  gtk_signal_connect(GTK_OBJECT(clist), "button-press-event",
472
 
                     GTK_SIGNAL_FUNC(cid_mouse_cb), session);
473
 
  gtk_signal_connect(GTK_OBJECT(clist), "select-row",
474
 
                     GTK_SIGNAL_FUNC(cid_select_row_cb), session);
475
 
 
476
 
  gtk_widget_show(clist);
477
 
 
478
 
  /* set up IN/OUT symbols */
479
 
  session->symbol_in_pixmap = gdk_pixmap_create_from_xpm_d(
480
 
                                 session->main_window->window,
481
 
                                 &session->symbol_in_bitmap,
482
 
                                 &style->bg[GTK_STATE_NORMAL],
483
 
                                 (gchar **) in_xpm);
484
 
  session->symbol_out_pixmap = gdk_pixmap_create_from_xpm_d(
485
 
                                 session->main_window->window,
486
 
                                 &session->symbol_out_bitmap,
487
 
                                 &style->bg[GTK_STATE_NORMAL],
488
 
                                 (gchar **) out_xpm);
489
 
  session->cid = frame;
490
 
  session->cid_list = clist;
491
 
  session->cid_scrolled_window = scrolled_window;
492
 
 
493
 
  return frame;
494
 
}
495
 
 
496
 
/*
497
 
 * select last item in cid list and adjust view to end of list
498
 
 */
499
 
void cid_jump_to_end(session_t *session) {
500
 
  GtkAdjustment *adj;
501
 
 
502
 
  gtk_clist_select_row(GTK_CLIST(session->cid_list),
503
 
                       session->cid_num - 1, 0);
504
 
 
505
 
  adj = gtk_scrolled_window_get_vadjustment(
506
 
          GTK_SCROLLED_WINDOW(session->cid_scrolled_window));
507
 
  gtk_adjustment_set_value(adj, fmax(adj->lower, adj->upper - adj->page_size));
508
 
}
509
 
 
510
 
/*
511
 
 * remove unneeded entries from caller id list (shorten log)
512
 
 */
513
 
void cid_normalize(session_t *session) {
514
 
  while (session->cid_num_max && session->cid_num > session->cid_num_max) {
515
 
    gtk_clist_remove(GTK_CLIST(session->cid_list), 0);
516
 
    session->cid_num--;
517
 
  }
518
 
}
519
 
 
520
 
/*
521
 
 * returns a dynamically allocated string in cid conformant time format
522
 
 * caller has got to take care of freeing this memory after usage
523
 
 */
524
 
static char *cid_timestring(time_t t) {
525
 
  char *date = (char *) malloc(20);
526
 
  int len;
527
 
  
528
 
  date[0] = '\1';
529
 
  len = strftime(date, 20, "%Y-%m-%d %H:%M:%S", localtime(&t));
530
 
  if (len == 0 && date[0] != '\0') {
531
 
    fprintf(stderr, "cid: Error calculating time with strftime.\n");
532
 
    return NULL;
533
 
  }
534
 
 
535
 
  return date;
536
 
}
537
 
 
538
 
/*
539
 
 * insert new entry at end of list
540
 
 *
541
 
 * from == NULL, if still unknown
542
 
 */
543
 
void cid_add_line(session_t *session,
544
 
                  enum call_type_t ct, gchar *from, gchar *to) {
545
 
  gchar *line[CID_COL_NUMBER];
546
 
  char *typestring;
547
 
 
548
 
  GdkPixmap *pixmap;
549
 
  GdkBitmap *bitmap;
550
 
 
551
 
  /* First: date */
552
 
  line[CID_COL_TIME] = cid_timestring(session->ring_time);
553
 
 
554
 
  line[CID_COL_FLAGS] = "";
555
 
  line[CID_COL_TYPE] = ""; /* call type */
556
 
 
557
 
  if (from == NULL) /* the from field */
558
 
    line[CID_COL_FROM] = "???";
559
 
  else
560
 
    line[CID_COL_FROM] = from;
561
 
  line[CID_COL_TO] = to;
562
 
 
563
 
  line[CID_COL_DURATION] = "???"; /* duration */
564
 
 
565
 
  /* create line */
566
 
  session->cid_num = gtk_clist_append(GTK_CLIST(session->cid_list), line) + 1;
567
 
  gtk_clist_set_row_data_full(GTK_CLIST(session->cid_list),
568
 
                              session->cid_num - 1,
569
 
                              cid_row_new(), cid_row_destroy);
570
 
 
571
 
  cid_normalize(session);
572
 
 
573
 
  /* actually set pixmap */
574
 
  if (ct == CALL_IN) {
575
 
    pixmap = session->symbol_in_pixmap;
576
 
    bitmap = session->symbol_in_bitmap;
577
 
    typestring = "IN";
578
 
  } else {
579
 
    pixmap = session->symbol_out_pixmap;
580
 
    bitmap = session->symbol_out_bitmap;
581
 
    typestring = "OUT";
582
 
  }
583
 
  gtk_clist_set_pixtext(GTK_CLIST(session->cid_list), session->cid_num - 1 ,
584
 
                        CID_COL_TYPE, typestring, 50, pixmap, bitmap);
585
 
 
586
 
  cid_jump_to_end(session);
587
 
 
588
 
  /* clean up */
589
 
  free(line[CID_COL_TIME]);
590
 
}
591
 
 
592
 
/*
593
 
 * set "date" field in last line
594
 
 */
595
 
void cid_set_date(session_t *session, time_t date) {
596
 
  if (date) {
597
 
    char *temp;
598
 
    gtk_clist_set_text(GTK_CLIST(session->cid_list),
599
 
                       session->cid_num - 1, CID_COL_TIME,
600
 
                       temp = cid_timestring(date));
601
 
    free(temp);
602
 
  }
603
 
}
604
 
 
605
 
/*
606
 
 * set "from" field in last line
607
 
 */
608
 
void cid_set_from(session_t *session, gchar *from) {
609
 
  if (from)
610
 
    gtk_clist_set_text(GTK_CLIST(session->cid_list),
611
 
                       session->cid_num - 1, CID_COL_FROM, from);
612
 
}
613
 
 
614
 
/*
615
 
 * complete last line with "duration"
616
 
 *
617
 
 * if message == NULL, current time will be used to calculate a duration
618
 
 * if message != NULL, this string will be displayed in the Duration field (#4)
619
 
 */
620
 
void cid_set_duration(session_t *session, gchar *message) {
621
 
  char *buf = timediff_str(time(NULL), session->vcon_time);
622
 
  
623
 
  if (message)
624
 
    gtk_clist_set_text(GTK_CLIST(session->cid_list), session->cid_num - 1,
625
 
                       CID_COL_DURATION, message);
626
 
  else
627
 
    gtk_clist_set_text(GTK_CLIST(session->cid_list), session->cid_num - 1,
628
 
                       CID_COL_DURATION, buf);
629
 
  free(buf);
630
 
}
631
 
 
632
 
/*
633
 
 * Add a line to clist (e.g. from saved caller id file)
634
 
 */
635
 
void cid_add_saved_line(session_t *session, char *date, char *type,
636
 
                        char *from, char *to, char *duration) {
637
 
  if (debug > 1)
638
 
    fprintf(stderr, "Caller ID add:\n"
639
 
            "Date: |%s|, Type: |%s|, From: |%s|, To: |%s|, Dur: |%s|\n",
640
 
            date, type, from, to, duration);
641
 
  
642
 
  cid_add_line(session,
643
 
               !strncmp(type, "IN", 2) ? CALL_IN : CALL_OUT, from, to);
644
 
  gtk_clist_set_text(GTK_CLIST(session->cid_list),
645
 
                     session->cid_num - 1, CID_COL_TIME, date);
646
 
  gtk_clist_set_text(GTK_CLIST(session->cid_list),
647
 
                     session->cid_num - 1, CID_COL_DURATION, duration);
648
 
 
649
 
  cid_row_mark_record(session, session->cid_num - 1);
650
 
  
651
 
}
652
 
 
653
 
/*
654
 
 * Merges isdnlog data into existing cid list
655
 
 */
656
 
void cid_calls_merge(session_t *session) {
657
 
  int low, high, mid; /* bounds and helper index */
658
 
  time_t start = time(NULL)
659
 
                        - 60 * 60 * 24 * session->option_calls_merge_max_days;
660
 
  int callstime; /* time of calls file line */
661
 
  char *cidstr; /* time of cid_list entry */
662
 
  size_t linelength = 100; /* buffer size */ 
663
 
  char *line = (char *) malloc (linelength); /* initial buffer for getline() */
664
 
  FILE *f = NULL;
665
 
 
666
 
  char from[41]; /* data to read from calls file */
667
 
  char to[41];
668
 
  int duration;
669
 
  int date;
670
 
  char type[4];
671
 
  
672
 
  char *temp;
673
 
  int rel;
674
 
  int cidline; /* index into cid list */
675
 
  gchar *linearr[CID_COL_NUMBER]; /* line to insert to cid_list */
676
 
  GdkPixmap *pixmap;
677
 
  GdkBitmap *bitmap;
678
 
 
679
 
  char *cache; /* tracks successive dates */
680
 
 
681
 
  char* calls_filename;
682
 
  
683
 
  /* try to find isdnlog data file */
684
 
  calls_filename = isdn_get_calls_filename();
685
 
  if (calls_filename && (f = fopen(calls_filename, "r"))) {
686
 
    if (debug) {
687
 
      fprintf(stderr, "Using %s as source for isdnlog data.\n", calls_filename);
688
 
    }
689
 
    if (session->option_calls_merge_max_days) {
690
 
      /* binary search on the file for the desired starting time if needed */
691
 
      if (debug >= 3) {
692
 
        fprintf(stderr, "Binary search in calls file...\n");
693
 
      }
694
 
      low = 0;
695
 
      fseek(f, 0, SEEK_END);
696
 
      high = ftell(f);
697
 
      
698
 
      while (high - low > 200) {
699
 
        if (debug >= 3) {
700
 
          fprintf(stderr, "low = %d, high = %d\n", low, high);
701
 
        }
702
 
        mid = (low + high) / 2;
703
 
        fseek(f, mid, SEEK_SET);
704
 
        getline(&line, &linelength, f); /* discard potentially partial line */
705
 
        if (-1 != getline(&line, &linelength, f)) {
706
 
          if (!sscanf(line, "%*40[^|]|%*40[^|]|%*40[^|]|%*40[^|]|%*40[^|]|%d",
707
 
                      &callstime))
708
 
            break;
709
 
          if (callstime < start)
710
 
            low = mid;
711
 
          else
712
 
            high = mid;
713
 
        } else
714
 
          break;
715
 
      }
716
 
      fseek(f, low, SEEK_SET);
717
 
      getline(&line, &linelength, f);
718
 
    }
719
 
    
720
 
    /* read in all remaining lines: merge */
721
 
    cidline = 0;
722
 
    cache = strdup("");
723
 
    while (!feof(f)) {
724
 
      /* get new calls line */
725
 
      if (-1 == getline(&line, &linelength, f)) break;
726
 
      if (sscanf(line,
727
 
                 "%*40[^|]|%40[^|]|%40[^|]|%d|%*40[^|]|%d|%*40[^|]|%1c",
728
 
                 from, to, &duration, &date, type) != 5) {
729
 
        fprintf(stderr, "Warning: Incomplete data input from calls file.\n");
730
 
        break;
731
 
      }
732
 
      if ((temp = strchr(from, ' '))) *temp = '\0';
733
 
      if ((temp = strchr(to, ' '))) *temp = '\0';
734
 
      if (type[0] == 'I') {
735
 
        type[1] = 'N';
736
 
        type[2] = '\0';
737
 
        pixmap = session->symbol_in_pixmap;
738
 
        bitmap = session->symbol_in_bitmap;
739
 
      } else {
740
 
        type[1] = 'U';
741
 
        type[2] = 'T';
742
 
        type[3] = '\0';
743
 
        pixmap = session->symbol_out_pixmap;
744
 
        bitmap = session->symbol_out_bitmap;
745
 
      }
746
 
      
747
 
      /* prepare (text) line to merge */
748
 
      linearr[CID_COL_TIME] = cid_timestring((time_t)date);
749
 
      linearr[CID_COL_TYPE] = type;
750
 
      if (*from)
751
 
        linearr[CID_COL_FROM] = from;
752
 
      else
753
 
        linearr[CID_COL_FROM] = "???";
754
 
      if (*to)
755
 
        linearr[CID_COL_TO] = to;
756
 
      else
757
 
        linearr[CID_COL_TO] = "???";
758
 
      if (duration < 0) {
759
 
        linearr[CID_COL_DURATION] = strdup(_("(UNKNOWN)"));
760
 
      } else {
761
 
        linearr[CID_COL_DURATION] = timediff_str(duration, (time_t) 0);
762
 
      }
763
 
 
764
 
      /* where to merge it in? */
765
 
      rel = 1; /* init to merge by default */
766
 
      while (cidline < session->cid_num &&
767
 
        (gtk_clist_get_text(GTK_CLIST(session->cid_list),
768
 
                            cidline, CID_COL_TIME, &cidstr),
769
 
         (rel = strcmp(cidstr, linearr[CID_COL_TIME])) < 0)) {
770
 
        
771
 
        cidline++;
772
 
      }
773
 
 
774
 
      if ((rel > 0 && strcmp(linearr[CID_COL_TIME], cache) > 0)
775
 
          || cidline == session->cid_num) {
776
 
        /* only merge new and successive entries */
777
 
        
778
 
        gtk_clist_insert(GTK_CLIST(session->cid_list), cidline, linearr);
779
 
        gtk_clist_set_row_data_full(GTK_CLIST(session->cid_list), cidline,
780
 
                                    cid_row_new(), cid_row_destroy);
781
 
        gtk_clist_set_pixtext(GTK_CLIST(session->cid_list),
782
 
            cidline, CID_COL_TYPE, type, 50, pixmap, bitmap);
783
 
        session->cid_num++;
784
 
      }
785
 
      free(cache);
786
 
      cache = linearr[CID_COL_TIME];
787
 
      free(linearr[CID_COL_DURATION]);
788
 
    }
789
 
    free(cache);
790
 
 
791
 
    cid_normalize(session);
792
 
  
793
 
    if (fclose(f))
794
 
      fprintf(stderr, "Error closing %s.\n", calls_filename);
795
 
  } else { /* error on fopen() */
796
 
    fprintf(stderr,
797
 
      "Warning: Couldn't open isdnlog calls logfile. Proceeding without it.\n");
798
 
  }
799
 
  free(line);
800
 
}
801
 
 
802
 
/*
803
 
 * - marks caller id row as unanswered
804
 
 * - sets window title to reflect number of unanswered calls
805
 
 *
806
 
 * input: session
807
 
 *        row     number of specified row
808
 
 *        state   1 for mark, 0 for unmark
809
 
 */
810
 
void cid_mark_row(session_t *session, int row, int state) {
811
 
  struct cid_row_t *rowdata = (struct cid_row_t *) gtk_clist_get_row_data(
812
 
                                            GTK_CLIST(session->cid_list), row);
813
 
  GdkColor color;
814
 
  char *title;
815
 
 
816
 
  if (rowdata->marked_unanswered != state) {
817
 
    if (state) {
818
 
      gdk_color_parse("#FF8888", &color);
819
 
      session->unanswered++;
820
 
      rowdata->marked_unanswered = 1;
821
 
    } else {
822
 
      gdk_color_parse("#FFFFFF", &color);
823
 
      session->unanswered--;
824
 
      rowdata->marked_unanswered = 0;
825
 
    }
826
 
    gtk_clist_set_background(GTK_CLIST(session->cid_list), row, &color);
827
 
    if (session->unanswered
828
 
        && 0 < asprintf(&title, _("ANT: %d unanswered"), session->unanswered)) {
829
 
      gtk_window_set_title(GTK_WINDOW(session->main_window), title);
830
 
      free(title);
831
 
    } else {
832
 
      gtk_window_set_title(GTK_WINDOW(session->main_window), "ANT " VERSION);
833
 
    }
834
 
  }
835
 
}
836
 
 
837
 
/*
838
 
 * returns timestring with just digits
839
 
 * - caller has got to free() the result
840
 
 */
841
 
char* cid_purify_timestring(char *s) {
842
 
  char* t;
843
 
  int length = 0;
844
 
  char* result;
845
 
  char* result_index;
846
 
 
847
 
  for (t=s; *t; t++) {
848
 
    if (isdigit (*t))
849
 
      length++;
850
 
  }
851
 
  
852
 
  result_index = result = (char*) malloc (length + 1);
853
 
  
854
 
  for (t=s; *t; t++) {
855
 
    if (isdigit (*t)) {
856
 
      *(result_index++) = *t;
857
 
    }
858
 
  }
859
 
  *(result_index++) = '\0';
860
 
 
861
 
  return result;
862
 
}
863
 
 
864
 
/*
865
 
 * returns name of sound filename, if it exists; NULL otherwise
866
 
 * - caller has go to free() result
867
 
 */
868
 
char* cid_get_record_filename(session_t* session, int row) {
869
 
  char* timestr;
870
 
  char* homedir;
871
 
  char* pattern;
872
 
  glob_t g;
873
 
  char* result; /* found something */
874
 
  
875
 
  gtk_clist_get_text(GTK_CLIST(session->cid_list), row, CID_COL_TIME, &timestr);
876
 
  timestr = cid_purify_timestring(timestr);
877
 
 
878
 
  if (!(homedir = get_homedir())) {
879
 
    fprintf(stderr, "Warning: Couldn't get home dir.\n");
880
 
    return NULL;
881
 
  }
882
 
  
883
 
  if (asprintf(&pattern, "%s/." PACKAGE "/recordings/%s.*",
884
 
        homedir, timestr) < 0) {
885
 
    fprintf(stderr, "Warning: "
886
 
            "Couldn't allocate memory for filename globbing pattern.\n");
887
 
    return NULL;
888
 
  }
889
 
  
890
 
  switch (glob(pattern, 0, NULL, &g)) {
891
 
    case 0:
892
 
      result = strdup(*(g.gl_pathv));
893
 
      break;
894
 
    case GLOB_NOMATCH:
895
 
      result = NULL;
896
 
      break;
897
 
    default:
898
 
      fprintf(stderr, "Warning: "
899
 
          "globbing error while looking up recorded conversation.\n");
900
 
      return NULL;
901
 
  }
902
 
 
903
 
  globfree(&g);
904
 
  free(pattern);
905
 
  free(timestr);
906
 
 
907
 
  return result;
908
 
}
909
 
 
910
 
/*
911
 
 * marks row with record sign if recording available
912
 
 * - deletes mark if recording was removed before
913
 
 */
914
 
void cid_row_mark_record(session_t* session, int row) {
915
 
  char* fn;
916
 
  
917
 
  if ((fn = cid_get_record_filename(session, row))) {
918
 
    gtk_clist_set_pixmap(GTK_CLIST(session->cid_list), row, CID_COL_FLAGS,
919
 
        session->symbol_record_pixmap, session->symbol_record_bitmap);
920
 
    free(fn);
921
 
  } else {
922
 
    gtk_clist_set_text(GTK_CLIST(session->cid_list), row, CID_COL_FLAGS, "");
923
 
  }
924
 
  
925
 
}
926