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);
							}
							