isdn4k-utils/ant-phone/src/callerid.c

927 lines
28 KiB
C

/*
* caller id related functions
*
* This file is part of ANT (Ant is Not a Telephone)
*
* Copyright 2002, 2003 Roland Stigge
*
* ANT 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 2 of the License, or
* (at your option) any later version.
*
* ANT 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 ANT; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "config.h"
/* regular GNU system includes */
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <unistd.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <glob.h>
#include <fcntl.h>
/* GTK */
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
/* own header files */
#include "globals.h"
#include "callerid.h"
#include "session.h"
#include "util.h"
#include "isdn.h"
/* graphical symbols */
#include "in.xpm"
#include "out.xpm"
#include "record.xpm"
/*
* returns a new data structure (for association with a row)
* filled with default values
*/
static gpointer cid_row_new(void) {
struct cid_row_t *rowdata =
(struct cid_row_t *) malloc(sizeof(struct cid_row_t));
rowdata->marked_unanswered = 0;
return (gpointer) rowdata;
}
/*
* callback to be called for additional row data on destruction of row
*/
static void cid_row_destroy(gpointer rowdata) {
if (debug > 1)
fprintf(stderr, "debug: destroying rowdata at %p.\n", rowdata);
free(rowdata);
}
/* called when caller id monitor state check button is toggled */
void cid_toggle_cb(GtkWidget *widget _U_, gpointer data, guint action _U_) {
session_t *session = (session_t *) data;
if (GTK_CHECK_MENU_ITEM (session->cid_check_menu_item)->active) {
gtk_widget_show(session->cid);
session->option_show_callerid = 1;
} else {
gtk_widget_hide(session->cid);
session->option_show_callerid = 0;
gtk_window_resize(GTK_WINDOW(session->main_window), 1, 1);
}
}
/*
* Callback: called when a button was clicked in row delete dialog
*/
static void cid_delete_response_cb(GtkWidget* widget, gint response_id,
gpointer data)
{
session_t* session = (session_t*) data;
guint row = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "row"));
char* filename = (char*) g_object_get_data(G_OBJECT(widget), "filename");
GtkWidget* checkbutton =
(GtkWidget*) g_object_get_data(G_OBJECT(widget), "checkbutton");
if (response_id == GTK_RESPONSE_OK) {
if (filename &&
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton)))
{
unlink(filename);
}
cid_mark_row(session, row, FALSE); /* to count unanswered calls */
session->cid_num--;
/* decrease before removal: "select-row" signal submission -> other cb */
gtk_clist_remove(GTK_CLIST(session->cid_list), row);
}
if (filename) free(filename);
gtk_widget_destroy(widget);
}
/*
* called on cid delete request
*
* -> deletes specified row without confirmation
*/
static void cid_request_delete(GtkWidget *widget _U_, gpointer data,
guint row) {
session_t* session = (session_t *) data;
GtkWidget* vbox;
GtkWidget* label;
GtkWidget* checkbutton;
char *filename = cid_get_record_filename(session, row);
GtkWidget* dialog = gtk_dialog_new_with_buttons(_("Delete Entry"),
GTK_WINDOW(session->main_window),
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
vbox = gtk_vbox_new(FALSE, 16);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 16);
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox);
gtk_widget_show(vbox);
label = gtk_label_new(_("Are you sure you want to\ndelete this entry?"));
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start_defaults(GTK_BOX(vbox), label);
gtk_widget_show(label);
checkbutton = gtk_check_button_new_with_label(_("Delete recording"));
if (filename) { /* default: checked */
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
} else { /* if file doesn't exist, grey out */
gtk_widget_set_sensitive(checkbutton, FALSE);
}
gtk_box_pack_start_defaults(GTK_BOX(vbox), checkbutton);
gtk_widget_show(checkbutton);
g_object_set_data(G_OBJECT(dialog), "row", GUINT_TO_POINTER(row));
g_object_set_data(G_OBJECT(dialog), "filename", (gpointer) filename);
g_object_set_data(G_OBJECT(dialog), "checkbutton", (gpointer) checkbutton);
g_signal_connect(dialog, "response", G_CALLBACK(cid_delete_response_cb),
session);
gtk_widget_show(dialog);
}
/*
* called on key pressed in cid list
*/
static gint cid_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) {
switch (event->keyval) {
case GDK_Delete: /* Delete dialog */
case GDK_KP_Delete:
/* portability: GtkCList.selection is private! */
if (GTK_CLIST(((session_t *) data)->cid_list)->selection) {
cid_request_delete(widget, data,
GPOINTER_TO_INT(GTK_CLIST(((session_t *) data)->cid_list)->selection->data));
}
return TRUE; /* event handled */
default:
return FALSE;
}
}
/*
* Callback: called on playback request
*/
static void cid_playback(GtkWidget *widget _U_, gpointer data, guint row) {
session_t *session = (session_t *) data;
session->effect_filename = cid_get_record_filename(session, row);
session->effect_playback_start_time = time(NULL);
session_set_state(session, STATE_PLAYBACK);
}
/*
* Callback: called on "OK" click in "Save as..." file selection
*/
static void cid_save_as_filename_cb(GtkWidget *fs) {
char* sourcename = g_object_get_data(G_OBJECT(fs), "sourcename");
const char* destbase =
gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs));
if (*destbase && *basename(destbase)) {
/* copy if something was entered */
char* destination;
char* extension = filename_extension(sourcename);
if (0 <= asprintf(&destination, "%s.%s", destbase, extension)) {
/* actually copy if source and destination known */
int buffer_size = 65536;
char* buffer = (char*) malloc(buffer_size);
if (buffer) {
int fd_in = open(sourcename, O_RDONLY);
if (fd_in != -1) {
int fd_out = open(destination, O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
if (fd_out != -1) {
int just_read;
do {
int to_write = just_read = read(fd_in, buffer, buffer_size);
int written = 1;
char* buffer_index = buffer;
while (to_write && written) {
written = write(fd_out, buffer_index, to_write);
to_write -= written;
buffer_index += written;
}
} while (just_read);
if (close(fd_out)) {
fprintf(stderr, "Error on closing destination file.\n");
}
} else {
fprintf(stderr,
"Error on destination file (%s) open().\n", destination);
}
if (close(fd_in)) {
fprintf(stderr, "Error on source file.\n");
}
} else {
fprintf(stderr,
"Error on source file (%s) open().\n", sourcename);
}
free(buffer);
} else {
fprintf(stderr, "Error on malloc().\n");
}
free(destination);
} else {
fprintf(stderr, "Error on asprintf().\n");
}
}
free(sourcename);
gtk_widget_destroy(GTK_WIDGET(fs));
}
/*
* Callback: called on "Save as..." request
*/
static void cid_save_as(GtkWidget *widget _U_, gpointer data, guint row) {
session_t* session = (session_t*) data;
char* sourcename = cid_get_record_filename(session, row);
char* extension = filename_extension(sourcename);
if (extension) {
GtkWidget* fs;
char* title;
if (0 > asprintf(&title, _("Enter the base filename for %s file"),
extension))
{
fprintf(stderr, "Error on asprintf().\n");
return;
}
fs = gtk_file_selection_new(title);
free(title);
g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button),
"clicked", G_CALLBACK(gtk_widget_destroy),
(gpointer) fs);
g_object_set_data(G_OBJECT(fs), "sourcename", sourcename);
g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
"clicked", G_CALLBACK(cid_save_as_filename_cb), fs);
gtk_widget_show(fs);
} else {
fprintf(stderr, "Error: no filename extension found.\n");
}
}
/*
* Callback: called if something happened in the delete recording dialog
*/
static void cid_delete_rec_response_cb(GtkWidget* widget, gint response_id,
gpointer data)
{
session_t *session = (session_t *) data;
if (response_id == GTK_RESPONSE_OK) {
char* temp;
guint row = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "row"));
unlink(temp = cid_get_record_filename(session, row));
free(temp);
cid_row_mark_record(session, row);
}
gtk_widget_destroy(widget);
}
/*
* Callback: called on delete recording request
*/
static void cid_delete_rec(GtkWidget *widget _U_, gpointer data, guint row) {
session_t *session = (session_t *) data;
GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(session->main_window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK_CANCEL, _("Do you really want to delete this recording?"));
g_signal_connect(dialog, "response", G_CALLBACK(cid_delete_rec_response_cb),
session);
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
g_object_set_data(G_OBJECT(dialog), "row", GINT_TO_POINTER(row));
gtk_widget_show(dialog);
}
/*
* called on mouse button pressed in cid list
*/
static gint cid_mouse_cb(GtkWidget *widget _U_,
GdkEventButton *event, gpointer data) {
session_t *session = (session_t *) data;
if (event->button == 3) { /* popup menu */
gint row;
gint column;
if (gtk_clist_get_selection_info(GTK_CLIST(session->cid_list),
event->x, event->y, &row, &column)) {
GtkItemFactoryEntry menu_items[] = {
/*path accel. callb. cb param. kind extra */
{_("/_Playback"), NULL, cid_playback, row, "", NULL},
{_("/_Save as..."), NULL, cid_save_as, row, "", NULL},
{_("/Delete _Recording"),NULL, cid_delete_rec,row, "", NULL},
{ "/Sep", NULL, NULL, 0, "<Separator>", NULL},
{_("/_Delete Row"), NULL, cid_request_delete, row, "", NULL}
};
GtkMenu *menu = GTK_MENU(gtk_menu_new());
GtkItemFactory *item_factory;
GtkAccelGroup *accel_group;
gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
GtkWidget *playback_item;
GtkWidget *save_as_item;
GtkWidget *delete_record_item;
GtkWidget *delete_item;
char* fn;
char* temp;
accel_group = gtk_accel_group_new();
item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<popup>",
accel_group);
gtk_item_factory_create_items_ac(item_factory, nmenu_items, menu_items,
session, 2);
if (!(playback_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Playback"), '_'))))
fprintf(stderr, "Error getting playback_item.\n");
free(temp);
if (!(save_as_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Save as..."), '_'))))
fprintf(stderr, "Error getting save_as_item.\n");
free(temp);
if (!(delete_record_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/Delete _Recording"), '_'))))
fprintf(stderr, "Error getting delete_record_item_item.\n");
free(temp);
if (!(delete_item = gtk_item_factory_get_item(item_factory,
temp = stripchr(_("/_Delete Row"), '_'))))
fprintf(stderr, "Error getting delete_item.\n");
free(temp);
if (!(session->state == STATE_READY &&
(fn = cid_get_record_filename(session, row)))) {
gtk_widget_set_sensitive(playback_item, FALSE);
gtk_widget_set_sensitive(save_as_item, FALSE);
gtk_widget_set_sensitive(delete_record_item, FALSE);
} else {
free(fn);
}
menu = GTK_MENU(gtk_item_factory_get_widget(item_factory, "<popup>"));
gtk_menu_set_accel_group(menu, accel_group);
gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
}
return TRUE; /* event handled */
} else {
return FALSE;
}
}
/*
* called on row select (to unmark lines)
*/
static void cid_select_row_cb(GtkCList *list _U_, gint row, gint column _U_,
GdkEventButton *button _U_, gpointer data) {
session_t *session = (session_t *) data;
if (row < session->cid_num)
cid_mark_row(session, row, FALSE);
}
/*
* Returns the Caller ID section as GtkWidget,
* sets CID related members in session
* NOTE: caller has to gtk_widget_show() this widget
*/
GtkWidget *cid_new(session_t *session) {
GtkWidget *frame; /* frame with comment */
GtkWidget *scrolled_window; /* the home for the clist */
GtkWidget *clist; /* the list itself */
GtkStyle *style; /* needed for preparing symbols */
GtkWidget *pixmapwidget; /* record symbol */
gchar *titles[CID_COL_NUMBER];
style = gtk_widget_get_style(session->main_window);
frame = gtk_frame_new(_("Caller ID"));
titles[CID_COL_FLAGS ] = "";
titles[CID_COL_TIME ] = _("Date/Time");
titles[CID_COL_TYPE ] = _("Type");
titles[CID_COL_FROM ] = _("From");
titles[CID_COL_TO ] = _("To");
titles[CID_COL_DURATION] = _("Duration");
gtk_container_set_border_width(GTK_CONTAINER(frame), 8);
/* the scrolled window: no special adjustments */
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
/* always vertical scroll bar, horizontal... well... automatic means no ;) */
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_container_add(GTK_CONTAINER(frame), scrolled_window);
gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 8);
gtk_widget_show(scrolled_window);
/* the list widget */
clist = gtk_clist_new_with_titles(CID_COL_NUMBER, titles);
session->symbol_record_pixmap = gdk_pixmap_create_from_xpm_d(
session->main_window->window,
&session->symbol_record_bitmap,
&style->bg[GTK_STATE_NORMAL],
(gchar **) record_xpm);
pixmapwidget = gtk_pixmap_new(session->symbol_record_pixmap,
session->symbol_record_bitmap);
gtk_clist_set_column_widget(GTK_CLIST(clist), CID_COL_FLAGS, pixmapwidget);
/* default: GTK_SELECTION_MULTIPLE: */
gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);
gtk_clist_set_shadow_type(GTK_CLIST(clist), GTK_SHADOW_IN);
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_FLAGS, 14);
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TIME, 128);
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TYPE, 30);
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_FROM, 100);
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TO, 100);
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_DURATION, 80);
/*gtk_clist_set_column_width(GTK_CLIST(clist), 5, 80);*/
gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
gtk_clist_column_titles_show(GTK_CLIST(clist));
gtk_widget_set_size_request(clist, 500, 128);
gtk_signal_connect(GTK_OBJECT(clist), "key-press-event",
GTK_SIGNAL_FUNC(cid_key_cb), session);
gtk_signal_connect(GTK_OBJECT(clist), "button-press-event",
GTK_SIGNAL_FUNC(cid_mouse_cb), session);
gtk_signal_connect(GTK_OBJECT(clist), "select-row",
GTK_SIGNAL_FUNC(cid_select_row_cb), session);
gtk_widget_show(clist);
/* set up IN/OUT symbols */
session->symbol_in_pixmap = gdk_pixmap_create_from_xpm_d(
session->main_window->window,
&session->symbol_in_bitmap,
&style->bg[GTK_STATE_NORMAL],
(gchar **) in_xpm);
session->symbol_out_pixmap = gdk_pixmap_create_from_xpm_d(
session->main_window->window,
&session->symbol_out_bitmap,
&style->bg[GTK_STATE_NORMAL],
(gchar **) out_xpm);
session->cid = frame;
session->cid_list = clist;
session->cid_scrolled_window = scrolled_window;
return frame;
}
/*
* select last item in cid list and adjust view to end of list
*/
void cid_jump_to_end(session_t *session) {
GtkAdjustment *adj;
gtk_clist_select_row(GTK_CLIST(session->cid_list),
session->cid_num - 1, 0);
adj = gtk_scrolled_window_get_vadjustment(
GTK_SCROLLED_WINDOW(session->cid_scrolled_window));
gtk_adjustment_set_value(adj, fmax(adj->lower, adj->upper - adj->page_size));
}
/*
* remove unneeded entries from caller id list (shorten log)
*/
void cid_normalize(session_t *session) {
while (session->cid_num_max && session->cid_num > session->cid_num_max) {
gtk_clist_remove(GTK_CLIST(session->cid_list), 0);
session->cid_num--;
}
}
/*
* returns a dynamically allocated string in cid conformant time format
* caller has got to take care of freeing this memory after usage
*/
static char *cid_timestring(time_t t) {
char *date = (char *) malloc(20);
int len;
date[0] = '\1';
len = strftime(date, 20, "%Y-%m-%d %H:%M:%S", localtime(&t));
if (len == 0 && date[0] != '\0') {
fprintf(stderr, "cid: Error calculating time with strftime.\n");
return NULL;
}
return date;
}
/*
* insert new entry at end of list
*
* from == NULL, if still unknown
*/
void cid_add_line(session_t *session,
enum call_type_t ct, gchar *from, gchar *to) {
gchar *line[CID_COL_NUMBER];
char *typestring;
GdkPixmap *pixmap;
GdkBitmap *bitmap;
/* First: date */
line[CID_COL_TIME] = cid_timestring(session->ring_time);
line[CID_COL_FLAGS] = "";
line[CID_COL_TYPE] = ""; /* call type */
if (from == NULL) /* the from field */
line[CID_COL_FROM] = "???";
else
line[CID_COL_FROM] = from;
line[CID_COL_TO] = to;
line[CID_COL_DURATION] = "???"; /* duration */
/* create line */
session->cid_num = gtk_clist_append(GTK_CLIST(session->cid_list), line) + 1;
gtk_clist_set_row_data_full(GTK_CLIST(session->cid_list),
session->cid_num - 1,
cid_row_new(), cid_row_destroy);
cid_normalize(session);
/* actually set pixmap */
if (ct == CALL_IN) {
pixmap = session->symbol_in_pixmap;
bitmap = session->symbol_in_bitmap;
typestring = "IN";
} else {
pixmap = session->symbol_out_pixmap;
bitmap = session->symbol_out_bitmap;
typestring = "OUT";
}
gtk_clist_set_pixtext(GTK_CLIST(session->cid_list), session->cid_num - 1 ,
CID_COL_TYPE, typestring, 50, pixmap, bitmap);
cid_jump_to_end(session);
/* clean up */
free(line[CID_COL_TIME]);
}
/*
* set "date" field in last line
*/
void cid_set_date(session_t *session, time_t date) {
if (date) {
char *temp;
gtk_clist_set_text(GTK_CLIST(session->cid_list),
session->cid_num - 1, CID_COL_TIME,
temp = cid_timestring(date));
free(temp);
}
}
/*
* set "from" field in last line
*/
void cid_set_from(session_t *session, gchar *from) {
if (from)
gtk_clist_set_text(GTK_CLIST(session->cid_list),
session->cid_num - 1, CID_COL_FROM, from);
}
/*
* complete last line with "duration"
*
* if message == NULL, current time will be used to calculate a duration
* if message != NULL, this string will be displayed in the Duration field (#4)
*/
void cid_set_duration(session_t *session, gchar *message) {
char *buf = timediff_str(time(NULL), session->vcon_time);
if (message)
gtk_clist_set_text(GTK_CLIST(session->cid_list), session->cid_num - 1,
CID_COL_DURATION, message);
else
gtk_clist_set_text(GTK_CLIST(session->cid_list), session->cid_num - 1,
CID_COL_DURATION, buf);
free(buf);
}
/*
* Add a line to clist (e.g. from saved caller id file)
*/
void cid_add_saved_line(session_t *session, char *date, char *type,
char *from, char *to, char *duration) {
if (debug > 1)
fprintf(stderr, "Caller ID add:\n"
"Date: |%s|, Type: |%s|, From: |%s|, To: |%s|, Dur: |%s|\n",
date, type, from, to, duration);
cid_add_line(session,
!strncmp(type, "IN", 2) ? CALL_IN : CALL_OUT, from, to);
gtk_clist_set_text(GTK_CLIST(session->cid_list),
session->cid_num - 1, CID_COL_TIME, date);
gtk_clist_set_text(GTK_CLIST(session->cid_list),
session->cid_num - 1, CID_COL_DURATION, duration);
cid_row_mark_record(session, session->cid_num - 1);
}
/*
* Merges isdnlog data into existing cid list
*/
void cid_calls_merge(session_t *session) {
int low, high, mid; /* bounds and helper index */
time_t start = time(NULL)
- 60 * 60 * 24 * session->option_calls_merge_max_days;
int callstime; /* time of calls file line */
char *cidstr; /* time of cid_list entry */
size_t linelength = 100; /* buffer size */
char *line = (char *) malloc (linelength); /* initial buffer for getline() */
FILE *f = NULL;
char from[41]; /* data to read from calls file */
char to[41];
int duration;
int date;
char type[4];
char *temp;
int rel;
int cidline; /* index into cid list */
gchar *linearr[CID_COL_NUMBER]; /* line to insert to cid_list */
GdkPixmap *pixmap;
GdkBitmap *bitmap;
char *cache; /* tracks successive dates */
char* calls_filename;
/* try to find isdnlog data file */
calls_filename = isdn_get_calls_filename();
if (calls_filename && (f = fopen(calls_filename, "r"))) {
if (debug) {
fprintf(stderr, "Using %s as source for isdnlog data.\n", calls_filename);
}
if (session->option_calls_merge_max_days) {
/* binary search on the file for the desired starting time if needed */
if (debug >= 3) {
fprintf(stderr, "Binary search in calls file...\n");
}
low = 0;
fseek(f, 0, SEEK_END);
high = ftell(f);
while (high - low > 200) {
if (debug >= 3) {
fprintf(stderr, "low = %d, high = %d\n", low, high);
}
mid = (low + high) / 2;
fseek(f, mid, SEEK_SET);
getline(&line, &linelength, f); /* discard potentially partial line */
if (-1 != getline(&line, &linelength, f)) {
if (!sscanf(line, "%*40[^|]|%*40[^|]|%*40[^|]|%*40[^|]|%*40[^|]|%d",
&callstime))
break;
if (callstime < start)
low = mid;
else
high = mid;
} else
break;
}
fseek(f, low, SEEK_SET);
getline(&line, &linelength, f);
}
/* read in all remaining lines: merge */
cidline = 0;
cache = strdup("");
while (!feof(f)) {
/* get new calls line */
if (-1 == getline(&line, &linelength, f)) break;
if (sscanf(line,
"%*40[^|]|%40[^|]|%40[^|]|%d|%*40[^|]|%d|%*40[^|]|%1c",
from, to, &duration, &date, type) != 5) {
fprintf(stderr, "Warning: Incomplete data input from calls file.\n");
break;
}
if ((temp = strchr(from, ' '))) *temp = '\0';
if ((temp = strchr(to, ' '))) *temp = '\0';
if (type[0] == 'I') {
type[1] = 'N';
type[2] = '\0';
pixmap = session->symbol_in_pixmap;
bitmap = session->symbol_in_bitmap;
} else {
type[1] = 'U';
type[2] = 'T';
type[3] = '\0';
pixmap = session->symbol_out_pixmap;
bitmap = session->symbol_out_bitmap;
}
/* prepare (text) line to merge */
linearr[CID_COL_TIME] = cid_timestring((time_t)date);
linearr[CID_COL_TYPE] = type;
if (*from)
linearr[CID_COL_FROM] = from;
else
linearr[CID_COL_FROM] = "???";
if (*to)
linearr[CID_COL_TO] = to;
else
linearr[CID_COL_TO] = "???";
if (duration < 0) {
linearr[CID_COL_DURATION] = strdup(_("(UNKNOWN)"));
} else {
linearr[CID_COL_DURATION] = timediff_str(duration, (time_t) 0);
}
/* where to merge it in? */
rel = 1; /* init to merge by default */
while (cidline < session->cid_num &&
(gtk_clist_get_text(GTK_CLIST(session->cid_list),
cidline, CID_COL_TIME, &cidstr),
(rel = strcmp(cidstr, linearr[CID_COL_TIME])) < 0)) {
cidline++;
}
if ((rel > 0 && strcmp(linearr[CID_COL_TIME], cache) > 0)
|| cidline == session->cid_num) {
/* only merge new and successive entries */
gtk_clist_insert(GTK_CLIST(session->cid_list), cidline, linearr);
gtk_clist_set_row_data_full(GTK_CLIST(session->cid_list), cidline,
cid_row_new(), cid_row_destroy);
gtk_clist_set_pixtext(GTK_CLIST(session->cid_list),
cidline, CID_COL_TYPE, type, 50, pixmap, bitmap);
session->cid_num++;
}
free(cache);
cache = linearr[CID_COL_TIME];
free(linearr[CID_COL_DURATION]);
}
free(cache);
cid_normalize(session);
if (fclose(f))
fprintf(stderr, "Error closing %s.\n", calls_filename);
} else { /* error on fopen() */
fprintf(stderr,
"Warning: Couldn't open isdnlog calls logfile. Proceeding without it.\n");
}
free(line);
}
/*
* - marks caller id row as unanswered
* - sets window title to reflect number of unanswered calls
*
* input: session
* row number of specified row
* state 1 for mark, 0 for unmark
*/
void cid_mark_row(session_t *session, int row, int state) {
struct cid_row_t *rowdata = (struct cid_row_t *) gtk_clist_get_row_data(
GTK_CLIST(session->cid_list), row);
GdkColor color;
char *title;
if (rowdata->marked_unanswered != state) {
if (state) {
gdk_color_parse("#FF8888", &color);
session->unanswered++;
rowdata->marked_unanswered = 1;
} else {
gdk_color_parse("#FFFFFF", &color);
session->unanswered--;
rowdata->marked_unanswered = 0;
}
gtk_clist_set_background(GTK_CLIST(session->cid_list), row, &color);
if (session->unanswered
&& 0 < asprintf(&title, _("ANT: %d unanswered"), session->unanswered)) {
gtk_window_set_title(GTK_WINDOW(session->main_window), title);
free(title);
} else {
gtk_window_set_title(GTK_WINDOW(session->main_window), "ANT " VERSION);
}
}
}
/*
* returns timestring with just digits
* - caller has got to free() the result
*/
char* cid_purify_timestring(char *s) {
char* t;
int length = 0;
char* result;
char* result_index;
for (t=s; *t; t++) {
if (isdigit (*t))
length++;
}
result_index = result = (char*) malloc (length + 1);
for (t=s; *t; t++) {
if (isdigit (*t)) {
*(result_index++) = *t;
}
}
*(result_index++) = '\0';
return result;
}
/*
* returns name of sound filename, if it exists; NULL otherwise
* - caller has go to free() result
*/
char* cid_get_record_filename(session_t* session, int row) {
char* timestr;
char* homedir;
char* pattern;
glob_t g;
char* result; /* found something */
gtk_clist_get_text(GTK_CLIST(session->cid_list), row, CID_COL_TIME, &timestr);
timestr = cid_purify_timestring(timestr);
if (!(homedir = get_homedir())) {
fprintf(stderr, "Warning: Couldn't get home dir.\n");
return NULL;
}
if (asprintf(&pattern, "%s/." PACKAGE "/recordings/%s.*",
homedir, timestr) < 0) {
fprintf(stderr, "Warning: "
"Couldn't allocate memory for filename globbing pattern.\n");
return NULL;
}
switch (glob(pattern, 0, NULL, &g)) {
case 0:
result = strdup(*(g.gl_pathv));
break;
case GLOB_NOMATCH:
result = NULL;
break;
default:
fprintf(stderr, "Warning: "
"globbing error while looking up recorded conversation.\n");
return NULL;
}
globfree(&g);
free(pattern);
free(timestr);
return result;
}
/*
* marks row with record sign if recording available
* - deletes mark if recording was removed before
*/
void cid_row_mark_record(session_t* session, int row) {
char* fn;
if ((fn = cid_get_record_filename(session, row))) {
gtk_clist_set_pixmap(GTK_CLIST(session->cid_list), row, CID_COL_FLAGS,
session->symbol_record_pixmap, session->symbol_record_bitmap);
free(fn);
} else {
gtk_clist_set_text(GTK_CLIST(session->cid_list), row, CID_COL_FLAGS, "");
}
}