Phone
/* $Id$ */
/* Copyright (c) 2011-2020 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Phone */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include <stdlib.h>
#ifdef DEBUG
# include <stdio.h>
#endif
#include <string.h>
#include <gtk/gtk.h>
#include <System.h>
#include "Phone.h"
#include "../../config.h"
#ifndef PREFIX
# define PREFIX "/usr/local"
#endif
#ifndef SYSCONFDIR
# define SYSCONFDIR PREFIX "/etc"
#endif
/* USSD */
/* private */
/* types */
typedef struct _PhonePlugin
{
PhonePluginHelper * helper;
Config * config;
char * _operator;
/* widgets */
GtkWidget * window;
GtkWidget * operators;
GtkWidget * codes;
} USSD;
typedef enum _USSDOperator
{
UO_OPERATOR = 0,
UO_DISPLAY
} USSDOperator;
#define UO_LAST UO_DISPLAY
#define UO_COUNT (UO_LAST + 1)
typedef enum _USSDCode
{
UC_CODE = 0,
UC_DISPLAY
} USSDCode;
#define UC_LAST UC_DISPLAY
#define UC_COUNT (UC_LAST + 1)
/* prototypes */
/* plugins */
static USSD * _ussd_init(PhonePluginHelper * helper);
static void _ussd_destroy(USSD * ussd);
static int _ussd_event(USSD * ussd, PhoneEvent * event);
static void _ussd_settings(USSD * ussd);
/* useful */
static int _ussd_load_operator(USSD * ussd, char const * name);
/* callbacks */
static void _ussd_on_operators_changed(gpointer data);
static void _ussd_on_settings_close(gpointer data);
static void _ussd_on_settings_send(gpointer data);
/* public */
/* variables */
PhonePluginDefinition plugin =
{
"USSD",
"user-info",
NULL,
_ussd_init,
_ussd_destroy,
_ussd_event,
_ussd_settings
};
/* private */
/* functions */
/* ussd_init */
static USSD * _ussd_init(PhonePluginHelper * helper)
{
USSD * ussd;
if((ussd = object_new(sizeof(*ussd))) == NULL)
return NULL;
ussd->config = config_new();
ussd->_operator = NULL;
ussd->helper = helper;
ussd->window = NULL;
ussd->operators = NULL;
ussd->codes = NULL;
/* check for errors */
if(ussd->config == NULL || config_load(ussd->config,
SYSCONFDIR "/" PACKAGE "/ussd.conf") != 0)
helper->error(helper->phone, error_get(NULL), 1);
return ussd;
}
/* ussd_destroy */
static void _ussd_destroy(USSD * ussd)
{
if(ussd->window != NULL)
gtk_widget_destroy(ussd->window);
free(ussd->_operator);
if(ussd->config != NULL)
config_delete(ussd->config);
object_delete(ussd);
}
/* ussd_event */
static int _event_modem(USSD * ussd, ModemEvent * event);
static int _ussd_event(USSD * ussd, PhoneEvent * event)
{
switch(event->type)
{
case PHONE_EVENT_TYPE_MODEM_EVENT:
return _event_modem(ussd, event->modem_event.event);
default:
/* not relevant */
return 0;
}
}
static int _event_modem(USSD * ussd, ModemEvent * event)
{
switch(event->type)
{
case MODEM_EVENT_TYPE_REGISTRATION:
_ussd_load_operator(ussd,
event->registration._operator);
/* XXX ignore errors */
return 0;
default:
/* not relevant */
return 0;
}
}
/* ussd_settings */
static void _settings_window(USSD * ussd);
static void _settings_window_operators(String const * section, void * data);
static void _ussd_settings(USSD * ussd)
{
if(ussd->window == NULL)
_settings_window(ussd);
gtk_window_present(GTK_WINDOW(ussd->window));
}
static void _settings_window(USSD * ussd)
{
GtkSizeGroup * group;
GtkListStore * model;
GtkWidget * vbox;
GtkWidget * hbox;
GtkWidget * image;
GtkWidget * widget;
GtkCellRenderer * renderer;
group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
ussd->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(ussd->window), 4);
gtk_window_set_default_size(GTK_WINDOW(ussd->window), 200, 300);
#if GTK_CHECK_VERSION(2, 6, 0)
gtk_window_set_icon_name(GTK_WINDOW(ussd->window), "gnome-settings");
#endif
gtk_window_set_title(GTK_WINDOW(ussd->window), "USSD");
g_signal_connect(ussd->window, "delete-event", G_CALLBACK(
gtk_widget_hide), NULL);
#if GTK_CHECK_VERSION(3, 0, 0)
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
#else
vbox = gtk_vbox_new(FALSE, 4);
#endif
/* operators */
#if GTK_CHECK_VERSION(3, 0, 0)
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
#else
hbox = gtk_hbox_new(FALSE, 4);
#endif
widget = gtk_label_new("Operator:");
#if GTK_CHECK_VERSION(3, 0, 0)
g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
#else
gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
#endif
gtk_size_group_add_widget(group, widget);
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
model = gtk_list_store_new(UO_COUNT, G_TYPE_STRING, G_TYPE_STRING);
ussd->operators = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ussd->operators), renderer,
TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ussd->operators),
renderer, "text", UO_DISPLAY, NULL);
g_signal_connect_swapped(ussd->operators, "changed", G_CALLBACK(
_ussd_on_operators_changed), ussd);
gtk_box_pack_start(GTK_BOX(hbox), ussd->operators, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
/* codes */
#if GTK_CHECK_VERSION(3, 0, 0)
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
#else
hbox = gtk_hbox_new(FALSE, 4);
#endif
widget = gtk_label_new("Code:");
#if GTK_CHECK_VERSION(3, 0, 0)
g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
#else
gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
#endif
gtk_size_group_add_widget(group, widget);
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
model = gtk_list_store_new(UC_COUNT, G_TYPE_STRING, G_TYPE_STRING);
ussd->codes = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model));
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ussd->codes), renderer,
TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ussd->codes), renderer,
"text", UC_DISPLAY, NULL);
gtk_box_pack_start(GTK_BOX(hbox), ussd->codes, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
/* send */
#if GTK_CHECK_VERSION(3, 0, 0)
hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
#else
hbox = gtk_hbox_new(FALSE, 4);
#endif
widget = gtk_label_new(NULL);
#if GTK_CHECK_VERSION(3, 0, 0)
g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
#else
gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
#endif
gtk_size_group_add_widget(group, widget);
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
widget = gtk_button_new_with_label("Send request");
image = gtk_image_new_from_icon_name("mail-send", GTK_ICON_SIZE_BUTTON);
gtk_button_set_image(GTK_BUTTON(widget), image);
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_ussd_on_settings_send), ussd);
gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
/* button box */
#if GTK_CHECK_VERSION(3, 0, 0)
hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
#else
hbox = gtk_hbutton_box_new();
#endif
gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
gtk_box_set_spacing(GTK_BOX(hbox), 4);
widget = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_ussd_on_settings_close), ussd);
gtk_container_add(GTK_CONTAINER(hbox), widget);
gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(ussd->window), vbox);
if(ussd->config != NULL)
config_foreach(ussd->config, _settings_window_operators, ussd);
gtk_widget_show_all(vbox);
}
static void _settings_window_operators(String const * section, void * data)
{
USSD * ussd = data;
GtkTreeModel * model;
GtkTreeIter iter;
String const * name;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(ussd->operators));
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
name = config_get(ussd->config, section, "name");
gtk_list_store_set(GTK_LIST_STORE(model), &iter, UO_OPERATOR, section,
UO_DISPLAY, (name != NULL) ? name : section, -1);
if(ussd->_operator != NULL && strcmp(ussd->_operator, section) == 0)
gtk_combo_box_set_active_iter(GTK_COMBO_BOX(ussd->operators),
&iter);
}
/* useful */
/* ussd_load_operator */
static int _ussd_load_operator(USSD * ussd, char const * name)
{
GtkTreeModel * model;
GtkTreeIter iter;
gboolean valid;
gchar * _operator;
if(name == NULL)
return -1;
if(ussd->_operator != NULL && strcmp(ussd->_operator, name) == 0)
return 0;
free(ussd->_operator);
if((ussd->_operator = strdup(name)) == NULL)
return -1;
if(ussd->operators == NULL)
return 0;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(ussd->operators));
for(valid = gtk_tree_model_get_iter_first(model, &iter); valid;
valid = gtk_tree_model_iter_next(model, &iter))
{
gtk_tree_model_get(model, &iter, UO_OPERATOR, &_operator, -1);
valid = (strcmp(name, _operator) == 0) ? TRUE : FALSE;
g_free(_operator);
if(valid)
{
gtk_combo_box_set_active_iter(
GTK_COMBO_BOX(ussd->operators), &iter);
break;
}
}
return 0;
}
/* callbacks */
/* ussd_on_operators_changed */
static void _ussd_on_operators_changed_operator(char const * variable,
char const * value, void * data);
static void _ussd_on_operators_changed(gpointer data)
{
USSD * ussd = data;
GtkTreeModel * model;
GtkTreeIter iter;
gchar * _operator;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(ussd->codes));
gtk_list_store_clear(GTK_LIST_STORE(model));
if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ussd->operators), &iter)
!= TRUE)
return;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(ussd->operators));
gtk_tree_model_get(model, &iter, UO_OPERATOR, &_operator, -1);
if(ussd->config != NULL)
config_foreach_section(ussd->config, _operator,
_ussd_on_operators_changed_operator, ussd);
g_free(_operator);
gtk_combo_box_set_active(GTK_COMBO_BOX(ussd->codes), 0);
}
static void _ussd_on_operators_changed_operator(char const * variable,
char const * value, void * data)
{
USSD * ussd = data;
GtkTreeModel * model;
GtkTreeIter iter;
if(strcmp(variable, "name") == 0)
return;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(ussd->codes));
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, UC_CODE, variable,
UC_DISPLAY, value, -1);
}
/* ussd_on_settings_close */
static void _ussd_on_settings_close(gpointer data)
{
USSD * ussd = data;
gtk_widget_hide(ussd->window);
}
/* ussd_on_settings_send */
static void _ussd_on_settings_send(gpointer data)
{
USSD * ussd = data;
PhonePluginHelper * helper = ussd->helper;
GtkTreeModel * model;
GtkTreeIter iter;
gchar * code;
ModemRequest request;
model = gtk_combo_box_get_model(GTK_COMBO_BOX(ussd->codes));
if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ussd->codes), &iter)
!= TRUE)
return;
gtk_tree_model_get(model, &iter, UC_CODE, &code, -1);
memset(&request, 0, sizeof(request));
request.type = MODEM_REQUEST_CALL;
request.call.call_type = MODEM_CALL_TYPE_VOICE;
request.call.number = code;
helper->request(helper->phone, &request);
g_free(code);
}