diff --git a/configure.ac b/configure.ac index ff2f4c729ebe671fffa6935ea9a25d946ccefb5a..d01e698e3e50ce6c63f994e6fd1b533740316bbf 100644 --- a/configure.ac +++ b/configure.ac @@ -401,6 +401,11 @@ if test "$has_sighandler_t" = "yes" ; then AC_DEFINE( HAVE_SIGHANDLER_T, 1, [Define if sighandler_t available] ) fi +dnl check libsoup (needed for wizard) +PKG_CHECK_MODULES(LIBSOUP, [libsoup-2.4 >= 2.26]) +AC_SUBST(LIBSOUP_CFLAGS) +AC_SUBST(LIBSOUP_LIBS) + ################################################## # Stricter build options (after external packages) ################################################## diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index e9cfe715b70a83da0fbcc8ad972ff28934066d24..ced68ababb53b82682dc241c371af8f775c061b7 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -35,6 +35,7 @@ liblinphone_la_SOURCES=\ linphonecall.c \ sipsetup.c sipsetup.h \ siplogin.c \ + sipwizard.c \ lsd.c linphonecore_utils.h \ ec-calibrator.c \ conference.c @@ -45,7 +46,8 @@ liblinphone_la_LDFLAGS= -version-info $(LIBLINPHONE_SO_VERSION) -no-undefined liblinphone_la_LIBADD= \ $(EXOSIP_LIBS) \ $(MEDIASTREAMER_LIBS) \ - $(ORTP_LIBS) $(OPENSSL_LIBS) + $(ORTP_LIBS) $(OPENSSL_LIBS) \ + $(LIBSOUP_LIBS) if BUILD_WIN32 liblinphone_la_LIBADD+=$(top_builddir)/oRTP/src/libortp.la @@ -69,8 +71,9 @@ AM_CFLAGS=$(STRICT_OPTIONS) -DIN_LINPHONE \ $(OSIP_CFLAGS) \ $(MEDIASTREAMER_CFLAGS) \ $(EXOSIP_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ -DENABLE_TRACE \ -DLOG_DOMAIN=\"LinphoneCore\" \ $(IPV6_CFLAGS) \ -DORTP_INET6 \ - $(VIDEO_CFLAGS) + $(VIDEO_CFLAGS) diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 245bd899b18367965c1955d910bf1ebba337270c..0a22c5c5a4aef08786cc5038d4b86d311f67e176 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -404,6 +404,9 @@ typedef struct _LinphoneAccountCreator{ char *username; char *password; char *domain; + char *route; + char *email; + int suscribe; bool_t succeeded; }LinphoneAccountCreator; @@ -411,9 +414,13 @@ LinphoneAccountCreator *linphone_account_creator_new(struct _LinphoneCore *core, void linphone_account_creator_set_username(LinphoneAccountCreator *obj, const char *username); void linphone_account_creator_set_password(LinphoneAccountCreator *obj, const char *password); void linphone_account_creator_set_domain(LinphoneAccountCreator *obj, const char *domain); +void linphone_account_creator_set_route(LinphoneAccountCreator *obj, const char *route); +void linphone_account_creator_set_email(LinphoneAccountCreator *obj, const char *email); +void linphone_account_creator_set_suscribe(LinphoneAccountCreator *obj, int suscribre); const char * linphone_account_creator_get_username(LinphoneAccountCreator *obj); const char * linphone_account_creator_get_domain(LinphoneAccountCreator *obj); int linphone_account_creator_test_existence(LinphoneAccountCreator *obj); +int linphone_account_creator_test_validation(LinphoneAccountCreator *obj); LinphoneProxyConfig * linphone_account_creator_validate(LinphoneAccountCreator *obj); void linphone_account_creator_destroy(LinphoneAccountCreator *obj); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 011be670672f228ea7c4efb739946b20c694c808..5a8786587a4757035c7ce5d77962491c590d6198 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -780,6 +780,18 @@ void linphone_account_creator_set_domain(LinphoneAccountCreator *obj, const char set_string(&obj->domain,domain); } +void linphone_account_creator_set_route(LinphoneAccountCreator *obj, const char *route) { + set_string(&obj->route,route); +} + +void linphone_account_creator_set_email(LinphoneAccountCreator *obj, const char *email) { + set_string(&obj->email,email); +} + +void linphone_account_creator_set_suscribe(LinphoneAccountCreator *obj, int suscribe) { + obj->suscribe = suscribe; +} + const char * linphone_account_creator_get_username(LinphoneAccountCreator *obj){ return obj->username; } @@ -796,10 +808,16 @@ int linphone_account_creator_test_existence(LinphoneAccountCreator *obj){ return err; } +int linphone_account_creator_test_validation(LinphoneAccountCreator *obj) { + SipSetupContext *ssctx=obj->ssctx; + int err=sip_setup_context_account_validated(ssctx,obj->username); + return err; +} + LinphoneProxyConfig * linphone_account_creator_validate(LinphoneAccountCreator *obj){ SipSetupContext *ssctx=obj->ssctx; char *uri=ms_strdup_printf("%s@%s",obj->username,obj->domain); - int err=sip_setup_context_create_account(ssctx,uri,obj->password); + int err=sip_setup_context_create_account(ssctx, uri, obj->password, obj->email, obj->suscribe); ms_free(uri); if (err==0) { obj->succeeded=TRUE; diff --git a/coreapi/sipsetup.c b/coreapi/sipsetup.c index 270737df1a2cbb26fd0943870269f4564954dffc..a1eaa57893302d5cfeb9ca1d1267f2bbb5a15863 100644 --- a/coreapi/sipsetup.c +++ b/coreapi/sipsetup.c @@ -24,9 +24,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" extern SipSetup linphone_sip_login; +extern SipSetup linphone_sip_wizard; static SipSetup *all_sip_setups[]={ &linphone_sip_login, + &linphone_sip_wizard, NULL }; @@ -123,9 +125,9 @@ int sip_setup_context_get_capabilities(SipSetupContext *ctx){ return ctx->funcs->capabilities; } -int sip_setup_context_create_account(SipSetupContext * ctx, const char *uri, const char *passwd){ +int sip_setup_context_create_account(SipSetupContext * ctx, const char *uri, const char *passwd, const char *email, int suscribe){ if (ctx->funcs->create_account) - return ctx->funcs->create_account(ctx,uri, passwd); + return ctx->funcs->create_account(ctx, uri, passwd, email, suscribe); else return -1; } @@ -135,6 +137,12 @@ int sip_setup_context_account_exists(SipSetupContext *ctx, const char *uri){ return -1; } +int sip_setup_context_account_validated(SipSetupContext *ctx, const char *uri){ + if (ctx->funcs->account_validated) + return ctx->funcs->account_validated(ctx,uri); + return -1; +} + int sip_setup_context_login_account(SipSetupContext * ctx, const char *uri, const char *passwd){ LinphoneAddress *from=linphone_address_new(uri); if (from==NULL) { diff --git a/coreapi/sipsetup.h b/coreapi/sipsetup.h index 6776367c961352480264dc537891413306a31665..2aefbee5c6097ab6a0322c2cc1aa58576644ba4e 100644 --- a/coreapi/sipsetup.h +++ b/coreapi/sipsetup.h @@ -97,7 +97,7 @@ struct _SipSetup{ void (*init_instance)(SipSetupContext *ctx); void (*uninit_instance)(SipSetupContext *ctx); int (*account_exists)(SipSetupContext *ctx, const char *uri); - int (*create_account)(SipSetupContext *ctx, const char *uri, const char *passwd); + int (*create_account)(SipSetupContext *ctx, const char *uri, const char *passwd, const char *email, int suscribe); int (*login_account)(SipSetupContext *ctx, const char *uri, const char *passwd); int (*get_proxy)(SipSetupContext *ctx, const char *domain, char *proxy, size_t sz); int (*get_stun_servers)(SipSetupContext *ctx, char *stun1, char *stun2, size_t size); @@ -106,6 +106,7 @@ struct _SipSetup{ const char ** (*get_domains)(SipSetupContext *ctx); int (*logout_account)(SipSetupContext *ctx); BuddyLookupFuncs *buddy_lookup_funcs; + int (*account_validated)(SipSetupContext *ctx, const char *uri); }; typedef struct _SipSetup SipSetup; @@ -131,7 +132,8 @@ unsigned int sip_setup_get_capabilities(SipSetup *s); SipSetupContext * sip_setup_context_new(SipSetup *s, struct _LinphoneProxyConfig *cfg); int sip_setup_context_account_exists(SipSetupContext *ctx, const char *uri); -int sip_setup_context_create_account(SipSetupContext *ctx, const char *uri, const char *passwd); +int sip_setup_context_account_validated(SipSetupContext *ctx, const char *uri); +int sip_setup_context_create_account(SipSetupContext *ctx, const char *uri, const char *passwd, const char *email, int suscribe); int sip_setup_context_get_capabilities(SipSetupContext *ctx); int sip_setup_context_login_account(SipSetupContext * ctx, const char *uri, const char *passwd); int sip_setup_context_get_proxy(SipSetupContext *ctx, const char *domain, char *proxy, size_t sz); diff --git a/coreapi/sipwizard.c b/coreapi/sipwizard.c new file mode 100644 index 0000000000000000000000000000000000000000..0bd654471d8c93c78cfe1400437a1c69b9a2dae5 --- /dev/null +++ b/coreapi/sipwizard.c @@ -0,0 +1,313 @@ +/* +linphone +Copyright (C) 2011 Simon MORLAT (simon.morlat@linphone.org) + +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 2 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "linphonecore.h" +#include "private.h" +#include <ctype.h> +#include <libsoup/soup.h> + +typedef struct _BLReq{ + int status; + int result; + SoupMessage *msg; + SoupSession *session; + ortp_thread_t th; +}BLReq; + +const int XMLRPC_FAILED = -1; +const int XMLRPC_OK = 0; +const char *XMLRPC_URL = "https://www.linphone.org/wizard.php"; + +static void sip_wizard_init_instance(SipSetupContext *ctx){ + LinphoneProxyConfig *cfg=sip_setup_context_get_proxy_config(ctx); + /*disable registration until the user logs in*/ + linphone_proxy_config_enable_register(cfg,FALSE); +} + +const char ** sip_wizard_get_domains(SipSetupContext *ctx) { + LinphoneProxyConfig *cfg=sip_setup_context_get_proxy_config(ctx); + const char **domains = (const char**) &cfg->reg_proxy; + return domains; +} + +static SoupMessage * build_xmlrpc_check_account_request(const char *identity){ + SoupMessage * msg; + + msg=soup_xmlrpc_request_new(XMLRPC_URL, + "check_account", + G_TYPE_STRING, identity, + G_TYPE_INVALID); + if (!msg){ + ms_error("Fail to create SoupMessage !"); + }else{ + SoupBuffer *sb=soup_message_body_flatten(msg->request_body); + ms_message("This is the XML-RPC request we are going to send:\n%s\n",sb->data); + soup_buffer_free(sb); + } + return msg; +} + +static SoupMessage * build_xmlrpc_check_account_validated(const char *identity){ + SoupMessage * msg; + + msg=soup_xmlrpc_request_new(XMLRPC_URL, + "check_account_validated", + G_TYPE_STRING, identity, + G_TYPE_INVALID); + if (!msg){ + ms_error("Fail to create SoupMessage !"); + }else{ + SoupBuffer *sb=soup_message_body_flatten(msg->request_body); + ms_message("This is the XML-RPC request we are going to send:\n%s\n",sb->data); + soup_buffer_free(sb); + } + return msg; +} + +static SoupMessage * build_xmlrpc_create_account_request(const char *identity, const char *passwd, const char *email, int suscribe){ + SoupMessage * msg; + + msg=soup_xmlrpc_request_new(XMLRPC_URL, + "create_account", + G_TYPE_STRING, identity, + G_TYPE_STRING, passwd, + G_TYPE_STRING, email, + G_TYPE_INT, suscribe, + G_TYPE_INVALID); + if (!msg){ + ms_error("Fail to create SoupMessage !"); + }else{ + SoupBuffer *sb=soup_message_body_flatten(msg->request_body); + ms_message("This is the XML-RPC request we are going to send:\n%s\n",sb->data); + soup_buffer_free(sb); + } + return msg; +} + +static int xml_rpc_parse_response(BLReq *blreq, SoupMessage *sm){ + SoupBuffer *sb; + GValue retval; + GError *error=NULL; + sb=soup_message_body_flatten(sm->response_body); + ms_message("This the xml-rpc response:\n%s\n",sb->data); + if (soup_xmlrpc_parse_method_response(sb->data,sb->length,&retval,&error)==FALSE){ + if (error!=NULL){ + ms_error("xmlrpc fault: %s",error->message); + g_error_free(error); + }else{ + ms_error("Could not parse xml-rpc response !"); + } + blreq->status=XMLRPC_FAILED; + }else{ + ms_message("Extracting values from return type..."); + blreq->result = g_value_get_int(&retval); + g_value_unset(&retval); + blreq->status=XMLRPC_OK; + } + soup_buffer_free(sb); + return blreq->status; +} + +static void got_headers(BLReq *blreq, SoupMessage*msg){ + ms_message("Got headers !"); + blreq->status=XMLRPC_OK; +} + +#if SERIALIZE_HTTPS +/*on windows libsoup support for threads with gnutls is not yet functionnal (only in git) +This will come in next release of libsoup, probably. +In the meantime, we are forced to serialize all soup https processing with a big +ugly global mutex...*/ + +static GStaticMutex big_mutex = G_STATIC_MUTEX_INIT; +#endif + +static void * process_xml_rpc_request(void *up){ + BLReq *blreq=(BLReq*)up; + SoupMessage *sm=blreq->msg; + int code; + g_signal_connect_swapped(G_OBJECT(sm),"got-headers",(GCallback)got_headers,blreq); + blreq->status=XMLRPC_OK; +#if SERIALIZE_HTTPS + g_static_mutex_lock(&big_mutex); +#endif + code=soup_session_send_message(blreq->session,sm); + if (code==200){ + ms_message("Got a response from server, yeah !"); + xml_rpc_parse_response(blreq,sm); + }else{ + ms_error("request failed, error-code=%i (%s)",code,soup_status_get_phrase(code)); + blreq->status=XMLRPC_FAILED; + } +#if SERIALIZE_HTTPS + g_static_mutex_unlock(&big_mutex); +#endif + return NULL; +} + +int sip_wizard_account_exists(SipSetupContext *ctx, const char *uri) { + /* + * Return 1 if account already exists + * 0 if account doesn't exists + * -1 if information isn't available + */ + SoupMessage *sm; + BLReq *req=ms_new0(BLReq, 1); + req->session=soup_session_sync_new(); + sm=build_xmlrpc_check_account_request(uri); + req->msg=sm; + process_xml_rpc_request(req); + + if (req->status == XMLRPC_OK) { + return req->result; + } else { + return -1; + } +} + +int sip_wizard_account_validated(SipSetupContext *ctx, const char *uri) { + /* + * Return 1 if account already exists + * 0 if account doesn't exists + * -1 if information isn't available + */ + SoupMessage *sm; + BLReq *req=ms_new0(BLReq, 1); + req->session=soup_session_sync_new(); + sm=build_xmlrpc_check_account_validated(uri); + req->msg=sm; + process_xml_rpc_request(req); + + if (req->status == XMLRPC_OK) { + return req->result; + } else { + return -1; + } +} + +int sip_wizard_create_account(SipSetupContext *ctx, const char *uri, const char *passwd, const char *email, int suscribe) { + /* + * Return 0 if account successfully created + * Else return -1 + */ + SoupMessage *sm; + BLReq *req=ms_new0(BLReq, 1); + req->session=soup_session_sync_new(); + sm=build_xmlrpc_create_account_request(uri, passwd, email, suscribe); + req->msg=sm; + process_xml_rpc_request(req); + + if (req->status == XMLRPC_OK) { + return req->result; + } else { + return -1; + } +} + +static void guess_display_name(LinphoneAddress *from){ + char *dn=(char*)ms_malloc(strlen(linphone_address_get_username(from))+3); + const char *it; + char *wptr=dn; + bool_t begin=TRUE; + bool_t surname=0; + for(it=linphone_address_get_username(from);*it!='\0';++it){ + if (begin){ + *wptr=toupper(*it); + begin=FALSE; + }else if (*it=='.'){ + if (surname) break; + *wptr=' '; + begin=TRUE; + surname=TRUE; + }else *wptr=*it; + wptr++; + } + linphone_address_set_display_name(from,dn); + ms_free(dn); +} + +static int sip_wizard_do_login(SipSetupContext * ctx, const char *uri, const char *passwd){ + LinphoneProxyConfig *cfg=sip_setup_context_get_proxy_config(ctx); + LinphoneCore *lc=linphone_proxy_config_get_core(cfg); + LinphoneAuthInfo *auth; + LinphoneAddress *parsed_uri; + char *tmp; + + parsed_uri=linphone_address_new(uri); + if (parsed_uri==NULL){ + return -1; + } + if (linphone_address_get_display_name(parsed_uri)!=NULL){ + guess_display_name(parsed_uri); + } + tmp=linphone_address_as_string(parsed_uri); + linphone_proxy_config_set_identity(cfg,tmp); + if (passwd) { + auth=linphone_auth_info_new(linphone_address_get_username(parsed_uri),NULL,passwd,NULL,NULL); + linphone_core_add_auth_info(lc,auth); + } + linphone_proxy_config_enable_register(cfg,TRUE); + linphone_proxy_config_done(cfg); + ms_free(tmp); + linphone_address_destroy(parsed_uri); + return 0; +} + +/* a simple SipSetup built-in plugin to allow creating accounts at runtime*/ + +#ifndef _MSC_VER + +SipSetup linphone_sip_wizard={ + .name="SipWizard", + .capabilities=SIP_SETUP_CAP_ACCOUNT_MANAGER, + .init_instance=sip_wizard_init_instance, + .account_exists=sip_wizard_account_exists, + .create_account=sip_wizard_create_account, + .login_account=sip_wizard_do_login, + .get_domains=sip_wizard_get_domains, + .account_validated=sip_wizard_account_validated +}; + +#else +SipSetup linphone_sip_wizard={ + "SipWizard", + SIP_SETUP_CAP_ACCOUNT_MANAGER, + 0, + NULL, + NULL, + sip_wizard_init_instance, + NULL, + sip_wizard_account_exists, + sip_wizard_create_account, + sip_wizard_do_login, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + sip_wizard_get_domains, + NULL, + NULL, + sip_wizard_account_validated +}; + + + +#endif diff --git a/gtk/linphone.h b/gtk/linphone.h index 63564c72ff7661261f913fcb86531e07339f6838..c941e8c77eec101766003b8f9691bd724aec3dad 100644 --- a/gtk/linphone.h +++ b/gtk/linphone.h @@ -56,6 +56,7 @@ GdkPixbuf *_gdk_pixbuf_new_from_memory_at_scale(const void *data, gint len, gint GtkWidget *linphone_gtk_create_window(const char *window_name); GtkWidget *linphone_gtk_get_widget(GtkWidget *window, const char *name); GtkWidget *linphone_gtk_create_widget(const char *filename, const char *widget_name); +GtkWidget * linphone_gtk_create_assistant(void); LinphoneCore *linphone_gtk_get_core(void); GtkWidget *linphone_gtk_get_main_window(); diff --git a/gtk/main.c b/gtk/main.c index 1eec9173ad03b4b4a6ed8d596957a1e51207383b..3f61a3df4c99f4756ec7f874c7f79da0838e45a2 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -50,6 +50,7 @@ const char *this_program_ident_string="linphone_ident_string=" LINPHONE_VERSION; static LinphoneCore *the_core=NULL; static GtkWidget *the_ui=NULL; +GtkWidget *the_wizard=NULL; static void linphone_gtk_registration_state_changed(LinphoneCore *lc, LinphoneProxyConfig *cfg, LinphoneRegistrationState rs, const char *msg); static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid); @@ -1416,8 +1417,9 @@ static void linphone_gtk_configure_main_window(){ g_object_unref(G_OBJECT(pbuf)); } } - if (linphone_gtk_can_manage_accounts()) + if (linphone_gtk_can_manage_accounts()) { gtk_widget_show(linphone_gtk_get_widget(w,"assistant_item")); + } if (update_check_menu){ gtk_widget_show(linphone_gtk_get_widget(w,"versioncheck_item")); } @@ -1561,6 +1563,13 @@ static void linphone_gtk_check_soundcards(){ } } +// Display the account wizard +void linphone_gtk_display_wizard() { + if (the_wizard == NULL || !gtk_widget_get_visible(the_wizard)) { // Only one instance of the wizard at the same time + the_wizard = linphone_gtk_create_assistant(); + } +} + static void linphone_gtk_quit(void){ linphone_gtk_uninit_instance(); linphone_gtk_destroy_log_window(); @@ -1699,6 +1708,12 @@ int main(int argc, char *argv[]){ gtk_timeout_add(30,(GtkFunction)linphone_gtk_iterate,(gpointer)linphone_gtk_get_core()); gtk_timeout_add(30,(GtkFunction)linphone_gtk_check_logs,(gpointer)NULL); linphone_gtk_init_main_window(); + + // Veryfing if at least one sip account is configured. If not, show wizard + if (linphone_core_get_proxy_config_list(linphone_gtk_get_core()) == NULL) { + linphone_gtk_display_wizard(); + } + #ifndef HAVE_GTK_OSX linphone_gtk_init_status_icon(); #endif diff --git a/gtk/main.ui b/gtk/main.ui index 9c393b54c76405c7bc29aeae2bd73f31610a3e59..2fe36aa5dfd1932510774c53a39a9e2d143a5c89 100644 --- a/gtk/main.ui +++ b/gtk/main.ui @@ -333,6 +333,11 @@ <property name="can_focus">False</property> <property name="stock">gtk-clear</property> </object> + <object class="GtkImage" id="image12"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="stock">gtk-connect</property> + </object> <object class="GtkImage" id="image2"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -504,6 +509,16 @@ <signal name="activate" handler="linphone_gtk_check_for_new_version" swapped="no"/> </object> </child> + <child> + <object class="GtkImageMenuItem" id="assistant_item"> + <property name="label" translatable="yes">Account assistant</property> + <property name="can_focus">False</property> + <property name="use_action_appearance">False</property> + <property name="image">image12</property> + <property name="use_stock">False</property> + <signal name="activate" handler="linphone_gtk_display_wizard" swapped="no"/> + </object> + </child> </object> </child> </object> diff --git a/gtk/parameters.ui b/gtk/parameters.ui index 3bec2e63ed2fb1be6e368bac06afe80c868f0282..004e49edd4d293eb29120fc526a2de0ec92e681f 100644 --- a/gtk/parameters.ui +++ b/gtk/parameters.ui @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <interface> <requires lib="gtk+" version="2.16"/> + <!-- interface-naming-policy toplevel-contextual --> <object class="GtkAdjustment" id="adjustment1"> <property name="lower">500</property> <property name="upper">3001</property> @@ -213,6 +214,8 @@ <property name="can_focus">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <property name="adjustment">adjustment1</property> <signal name="value-changed" handler="linphone_gtk_mtu_changed" swapped="no"/> </object> @@ -330,6 +333,8 @@ <property name="invisible_char_set">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <property name="adjustment">adjustment7</property> </object> <packing> @@ -346,6 +351,8 @@ <property name="invisible_char_set">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <property name="adjustment">adjustment2</property> <signal name="value-changed" handler="linphone_gtk_video_port_changed" swapped="no"/> </object> @@ -365,6 +372,8 @@ <property name="invisible_char_set">True</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <property name="adjustment">adjustment3</property> <signal name="value-changed" handler="linphone_gtk_audio_port_changed" swapped="no"/> </object> @@ -504,6 +513,8 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <signal name="changed" handler="linphone_gtk_nat_address_changed" swapped="no"/> </object> <packing> @@ -574,6 +585,8 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <signal name="changed" handler="linphone_gtk_stun_server_changed" swapped="no"/> </object> <packing> @@ -741,6 +754,8 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <signal name="editing-done" handler="linphone_gtk_alsa_special_device_changed" swapped="no"/> </object> <packing> @@ -1091,6 +1106,8 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <signal name="changed" handler="linphone_gtk_update_my_contact" swapped="no"/> </object> <packing> @@ -1129,6 +1146,8 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <signal name="changed" handler="linphone_gtk_update_my_contact" swapped="no"/> </object> <packing> @@ -1146,6 +1165,8 @@ <property name="editable">False</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> </object> <packing> <property name="left_attach">1</property> @@ -1216,6 +1237,53 @@ <property name="visible">True</property> <property name="can_focus">False</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <object class="GtkButton" id="wizard"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_action_appearance">False</property> + <signal name="clicked" handler="linphone_gtk_display_wizard" swapped="no"/> + <child> + <object class="GtkHBox" id="hbox5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <object class="GtkImage" id="image14"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="stock">gtk-add</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="wizard_label0"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="label" translatable="yes">Wizard</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> <child> <object class="GtkButton" id="add_proxy"> <property name="visible">True</property> @@ -1261,7 +1329,7 @@ <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> <child> @@ -1309,7 +1377,7 @@ <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> <child> @@ -1357,7 +1425,7 @@ <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">2</property> + <property name="position">3</property> </packing> </child> <child> @@ -1401,7 +1469,7 @@ virtual network !</property> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">3</property> + <property name="position">4</property> </packing> </child> </object> @@ -1826,6 +1894,8 @@ virtual network !</property> <property name="tooltip_text" translatable="yes">0 stands for "unlimited"</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <property name="adjustment">adjustment5</property> <signal name="value-changed" handler="linphone_gtk_upload_bw_changed" swapped="no"/> </object> @@ -1846,6 +1916,8 @@ virtual network !</property> <property name="tooltip_text" translatable="yes">0 stands for "unlimited"</property> <property name="primary_icon_activatable">False</property> <property name="secondary_icon_activatable">False</property> + <property name="primary_icon_sensitive">True</property> + <property name="secondary_icon_sensitive">True</property> <property name="adjustment">adjustment6</property> <signal name="value-changed" handler="linphone_gtk_download_bw_changed" swapped="no"/> </object> diff --git a/gtk/setupwizard.c b/gtk/setupwizard.c index e5f8702f28652055ae7746486d34a7a490d10566..7f22af3881300c75fadcb1df9ebd7e6a0ea5ced7 100644 --- a/gtk/setupwizard.c +++ b/gtk/setupwizard.c @@ -18,8 +18,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphone.h" +#include <glib.h> +#include <glib/gprintf.h> LinphoneAccountCreator *linphone_gtk_assistant_get_creator(GtkWidget*w); +const int PASSWORD_MIN_SIZE = 6; +const int LOGIN_MIN_SIZE = 4; +int is_username_available = 0; +int is_email_correct = 0; +int is_password_correct = 0; + +GdkPixbuf *ok; +GdkPixbuf *notok; + static GtkWidget *create_intro(){ GtkWidget *vbox=gtk_vbox_new(FALSE,2); GtkWidget *label=gtk_label_new(_("Welcome !\nThis assistant will help you to use a SIP account for your calls.")); @@ -31,83 +42,277 @@ static GtkWidget *create_intro(){ static GtkWidget *create_setup_signin_choice(){ GtkWidget *vbox=gtk_vbox_new(FALSE,2); - GtkWidget *t1=gtk_radio_button_new_with_label(NULL,_("Create an account by choosing a username")); - GtkWidget *t2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(t1),_("I have already an account and just want to use it")); + GtkWidget *t1=gtk_radio_button_new_with_label(NULL,_("Create an account on linphone.org")); + GtkWidget *t2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(t1),_("I have already a linphone.org account and I just want to use it")); + GtkWidget *t3=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(t1),_("I have already a sip account and I just want to use it")); gtk_box_pack_start (GTK_BOX (vbox), t1, TRUE, TRUE, 2); gtk_box_pack_start (GTK_BOX (vbox), t2, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (vbox), t3, TRUE, TRUE, 2); gtk_widget_show_all(vbox); g_object_set_data(G_OBJECT(vbox),"create_account",t1); - g_object_set_data(G_OBJECT(vbox),"setup_account",t2); + g_object_set_data(G_OBJECT(vbox),"setup_linphone_account",t2); + g_object_set_data(G_OBJECT(vbox),"setup_account",t3); return vbox; } -static void create_username_changed(GtkEntry *entry, GtkWidget *w){ +static int all_account_information_entered(GtkWidget *w) { + GtkEntry* username = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"username")); + GtkEntry* domain = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"domain")); + + if (gtk_entry_get_text_length(username) > 0 && + gtk_entry_get_text_length(domain) > 0 && + g_regex_match_simple("^(sip:)?[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\\.-][a-z0-9]+)*)+\\.[a-z]{2,}$", gtk_entry_get_text(username), 0, 0) && + g_regex_match_simple("^(sip:)?([a-z0-9]+([\\.-][a-z0-9]+)*)+\\.[a-z]{2,}$", gtk_entry_get_text(domain), 0, 0)) { + return 1; + } + return 0; +} + +static void account_informations_changed(GtkEntry *entry, GtkWidget *w) { GtkWidget *assistant=gtk_widget_get_toplevel(w); + GtkEntry* username = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"username")); + GtkEntry* domain = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"domain")); + + const gchar *needle = "@"; + if (entry == username && g_strrstr(gtk_entry_get_text(username), needle) != NULL) { + gtk_entry_set_text(domain, g_strrstr(gtk_entry_get_text(username), "@")+1); + } + gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant),w, - gtk_entry_get_text_length(entry)>=3); + all_account_information_entered(w)>0); } -static GtkWidget *create_username_chooser(){ - GtkWidget *vbox=gtk_vbox_new(FALSE,2); - GtkWidget *hbox=gtk_hbox_new(FALSE,2); - GtkWidget *label=gtk_label_new(_("Please choose a username:")); - GtkWidget *label2=gtk_label_new(_("Username:")); - GtkWidget *label3=gtk_label_new(NULL); - GtkWidget *entry=gtk_entry_new(); - gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (hbox), label2, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (vbox), label3, TRUE, TRUE, 2); +static void linphone_account_informations_changed(GtkEntry *entry, GtkWidget *w) { + GtkWidget *assistant=gtk_widget_get_toplevel(w); + GtkEntry* username = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"username")); + + gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant),w, + gtk_entry_get_text_length(username) >= LOGIN_MIN_SIZE); +} + +static GtkWidget *create_linphone_account_informations_page() { + GtkWidget *vbox=gtk_table_new(3, 2, TRUE); + GtkWidget *label=gtk_label_new(_("Enter your linphone.org's username")); + + GdkColor color; + gdk_color_parse ("red", &color); + GtkWidget *labelEmpty=gtk_label_new(NULL); + gtk_widget_modify_fg(labelEmpty, GTK_STATE_NORMAL, &color); + + GtkWidget *labelUsername=gtk_label_new(_("Username:")); + GtkWidget *entryUsername=gtk_entry_new(); + GtkWidget *labelPassword=gtk_label_new(_("Password:")); + GtkWidget *entryPassword=gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entryPassword), FALSE); + + gtk_table_attach_defaults(GTK_TABLE(vbox), label, 0, 2, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelUsername, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), entryUsername, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelPassword, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(vbox), entryPassword, 1, 2, 2, 3); + gtk_widget_show_all(vbox); - g_object_set_data(G_OBJECT(vbox),"username",entry); - g_object_set_data(G_OBJECT(vbox),"errorstring",label3); - g_signal_connect(G_OBJECT(entry),"changed",(GCallback)create_username_changed,vbox); + g_object_set_data(G_OBJECT(vbox),"username",entryUsername); + g_object_set_data(G_OBJECT(vbox),"password",entryPassword); + g_object_set_data(G_OBJECT(vbox),"errorstring",labelEmpty); + g_signal_connect(G_OBJECT(entryUsername),"changed",(GCallback)linphone_account_informations_changed,vbox); return vbox; } -static GtkWidget *create_username_checking_page(){ - GtkWidget *vbox=gtk_vbox_new(FALSE,2); - GtkWidget *label=gtk_label_new(NULL); - GtkWidget *progress=gtk_progress_bar_new(); - gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (vbox), progress, TRUE, TRUE, 2); - g_object_set_data(G_OBJECT(vbox),"label",label); - g_object_set_data(G_OBJECT(vbox),"progress",progress); +static GtkWidget *create_account_informations_page() { + GtkWidget *vbox=gtk_table_new(6, 2, FALSE); + GtkWidget *label=gtk_label_new(_("Enter your account informations")); + + GdkColor color; + gdk_color_parse ("red", &color); + GtkWidget *labelEmpty=gtk_label_new(NULL); + gtk_widget_modify_fg(labelEmpty, GTK_STATE_NORMAL, &color); + + GtkWidget *labelUsername=gtk_label_new(_("Identity:")); + GtkWidget *labelUsernameExemple=gtk_label_new(_("exemple: user@sip.linphone.org")); + GtkWidget *labelPassword=gtk_label_new(_("Password:")); + GtkWidget *entryPassword=gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entryPassword), FALSE); + GtkWidget *labelDomain=gtk_label_new(_("Proxy:")); + GtkWidget *labelDomainExemple=gtk_label_new(_("exemple: sip.linphone.org")); + GtkWidget *labelRoute=gtk_label_new(_("Route (optional):")); + GtkWidget *entryUsername=gtk_entry_new(); + GtkWidget *entryDomain=gtk_entry_new(); + GtkWidget *entryRoute=gtk_entry_new(); + + GtkWidget *vbox1=gtk_vbox_new(FALSE, 1); + gtk_box_pack_start (GTK_BOX (vbox1), entryUsername, TRUE, TRUE, 1); + gtk_box_pack_start (GTK_BOX (vbox1), labelUsernameExemple, TRUE, TRUE, 1); + GtkWidget *vbox2=gtk_vbox_new(FALSE, 1); + gtk_box_pack_start (GTK_BOX (vbox2), entryDomain, TRUE, TRUE, 1); + gtk_box_pack_start (GTK_BOX (vbox2), labelDomainExemple, TRUE, TRUE, 1); + gtk_table_set_row_spacing(GTK_TABLE(vbox), 1, 10); + gtk_table_set_row_spacing(GTK_TABLE(vbox), 3, 5); + + gtk_table_attach_defaults(GTK_TABLE(vbox), label, 0, 2, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelUsername, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), vbox1, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelPassword, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(vbox), entryPassword, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelDomain, 0, 1, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(vbox), vbox2, 1, 2, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelRoute, 0, 1, 4, 5); + gtk_table_attach_defaults(GTK_TABLE(vbox), entryRoute, 1, 2, 4, 5); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelEmpty, 0, 2, 5, 6); gtk_widget_show_all(vbox); + + g_object_set_data(G_OBJECT(vbox),"username",entryUsername); + g_object_set_data(G_OBJECT(vbox),"password",entryPassword); + g_object_set_data(G_OBJECT(vbox),"domain",entryDomain); + g_object_set_data(G_OBJECT(vbox),"route",entryRoute); + g_object_set_data(G_OBJECT(vbox),"errorstring",labelEmpty); + g_signal_connect(G_OBJECT(entryUsername),"changed",(GCallback)account_informations_changed,vbox); + g_signal_connect(G_OBJECT(entryDomain),"changed",(GCallback)account_informations_changed,vbox); + g_signal_connect(G_OBJECT(entryRoute),"changed",(GCallback)account_informations_changed,vbox); + return vbox; } -static void *progress_bar_update(LinphoneCore *lc, void *ctx, LinphoneWaitingState ws, const char *purpose, float progress){ - GtkWidget *pb=(GtkWidget*)ctx; - if (ws==LinphoneWaitingProgress) gtk_progress_bar_pulse(GTK_PROGRESS_BAR(pb)); - else if (ws==LinphoneWaitingFinished) gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pb),1); - return ctx; +static int create_account(GtkWidget *page) { + LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(gtk_widget_get_toplevel(page)); + LinphoneProxyConfig *res=linphone_account_creator_validate(creator); + if (res) { + if (!g_regex_match_simple("^sip:[a-zA-Z]+[a-zA-Z0-9.\\-_]{2,}@sip.linphone.org$",creator->username, 0, 0)) { + gchar identity[128]; + g_sprintf(identity, "sip:%s@sip.linphone.org", creator->username); + linphone_account_creator_set_username(creator, identity); + linphone_account_creator_set_domain(creator, "sip:sip.linphone.org"); + } + return 1; + } + return 0; +} + +static int is_account_information_correct(GtkWidget *w) { + if (is_username_available == 1 && is_email_correct == 1 && is_password_correct == 1) { + return 1; + } + return 0; } -static void check_username(GtkWidget *page){ - GtkWidget *progress=(GtkWidget*)g_object_get_data(G_OBJECT(page),"progress"); - GtkWidget *label=(GtkWidget*)g_object_get_data(G_OBJECT(page),"label"); - LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(gtk_widget_get_toplevel(page)); - gchar *text=g_strdup_printf(_("Checking if '%s' is available..."),linphone_account_creator_get_username(creator)); - LinphoneAccountCreator *c=linphone_gtk_assistant_get_creator(gtk_widget_get_toplevel(page)); - int res; - gtk_label_set_text(GTK_LABEL(label),text); - g_free(text); - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Please wait...")); - linphone_core_set_waiting_callback(linphone_gtk_get_core(),progress_bar_update,progress); - res=linphone_account_creator_test_existence(c); - if (res==1){ - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Sorry this username already exists. Please try a new one.")); - }else if (res==0){ - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Ok !")); - gtk_assistant_set_page_complete(GTK_ASSISTANT(gtk_widget_get_toplevel(page)),page,TRUE); - }else if (res==-1){ - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Communication problem, please try again later.")); +static void account_email_changed(GtkEntry *entry, GtkWidget *w) { + // Verifying if email entered is correct, and if form is correctly filled, let the user go next page + + GtkEntry* email = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"email")); + GtkImage* isEmailOk = GTK_IMAGE(g_object_get_data(G_OBJECT(w),"emailOk")); + GtkWidget *assistant=gtk_widget_get_toplevel(w); + + if (g_regex_match_simple("^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\\.-][a-z0-9]+)*)+\\.[a-z]{2,}$", gtk_entry_get_text(email), 0, 0)) { + is_email_correct = 1; + gtk_image_set_from_pixbuf(isEmailOk, ok); } - linphone_core_set_waiting_callback(linphone_gtk_get_core(),linphone_gtk_wait,NULL); + else { + is_email_correct = 0; + gtk_image_set_from_pixbuf(isEmailOk, notok); + } + gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant),w, + is_account_information_correct(w)>0); +} + +static void account_password_changed(GtkEntry *entry, GtkWidget *w) { + // Verifying if passwords entered match, and if form is correctly filled, let the user go next page + + GtkEntry* password = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"password")); + GtkImage* isPasswordOk = GTK_IMAGE(g_object_get_data(G_OBJECT(w),"passwordOk")); + GtkEntry* password_confirm = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"password_confirm")); + GtkWidget *assistant=gtk_widget_get_toplevel(w); + + if (gtk_entry_get_text_length(password) >= PASSWORD_MIN_SIZE && + g_ascii_strcasecmp(gtk_entry_get_text(password), gtk_entry_get_text(password_confirm)) == 0) { + is_password_correct = 1; + gtk_image_set_from_pixbuf(isPasswordOk, ok); + } + else { + is_password_correct = 0; + gtk_image_set_from_pixbuf(isPasswordOk, notok); + } + gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant),w, + is_account_information_correct(w)>0); } +static void account_username_changed(GtkEntry *entry, GtkWidget *w) { + // Verifying if username choosed is available, and if form is correctly filled, let the user go next page + + GtkWidget *assistant=gtk_widget_get_toplevel(w); + GtkEntry* username = GTK_ENTRY(g_object_get_data(G_OBJECT(w),"username")); + GtkImage* isUsernameOk = GTK_IMAGE(g_object_get_data(G_OBJECT(w),"usernameOk")); + + LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(assistant); + linphone_account_creator_set_username(creator, gtk_entry_get_text(username)); + if (g_regex_match_simple("^[a-zA-Z]+[a-zA-Z0-9.\\-_]{2,}$", gtk_entry_get_text(username), 0, 0) + && linphone_account_creator_test_existence(creator) == 0) { + is_username_available = 1; + gtk_image_set_from_pixbuf(isUsernameOk, ok); + } + else { + is_username_available = 0; + gtk_image_set_from_pixbuf(isUsernameOk, notok); + } + + gtk_assistant_set_page_complete(GTK_ASSISTANT(assistant),w, + is_account_information_correct(w)>0); +} + +static GtkWidget *create_account_information_page() { + GtkWidget *vbox=gtk_table_new(7, 3, FALSE); + + GtkWidget *label=gtk_label_new(_("(*) Required fields")); + GtkWidget *labelUsername=gtk_label_new(_("Username: (*)")); + GtkWidget *isUsernameOk=gtk_image_new_from_pixbuf(notok); + GtkWidget *labelPassword=gtk_label_new(_("Password: (*)")); + GtkWidget *isPasswordOk=gtk_image_new_from_pixbuf(notok); + GtkWidget *labelEmail=gtk_label_new(_("Email: (*)")); + GtkWidget *isEmailOk=gtk_image_new_from_pixbuf(notok); + GtkWidget *labelPassword2=gtk_label_new(_("Confirm your password: (*)")); + GtkWidget *entryUsername=gtk_entry_new(); + GtkWidget *entryPassword=gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entryPassword), FALSE); + GtkWidget *entryEmail=gtk_entry_new(); + GtkWidget *entryPassword2=gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entryPassword2), FALSE); + GtkWidget *checkNewsletter=gtk_check_button_new_with_label("Keep me informed with linphone updates"); + + GtkWidget *passwordVbox1=gtk_vbox_new(FALSE,2); + GtkWidget *passwordVbox2=gtk_vbox_new(FALSE,2); + gtk_box_pack_start (GTK_BOX (passwordVbox1), labelPassword, TRUE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (passwordVbox1), labelPassword2, TRUE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (passwordVbox2), entryPassword, TRUE, FALSE, 2); + gtk_box_pack_start (GTK_BOX (passwordVbox2), entryPassword2, TRUE, FALSE, 2); + + gtk_table_attach_defaults(GTK_TABLE(vbox), label, 0, 3, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelEmail, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), entryEmail, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), isEmailOk, 2, 3, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(vbox), labelUsername, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(vbox), entryUsername, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(vbox), isUsernameOk, 2, 3, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(vbox), passwordVbox1, 0, 1, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(vbox), passwordVbox2, 1, 2, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(vbox), isPasswordOk, 2, 3, 3, 4); + gtk_table_attach_defaults(GTK_TABLE(vbox), checkNewsletter, 0, 3, 5, 6); + + gtk_widget_show_all(vbox); + g_object_set_data(G_OBJECT(vbox),"username",entryUsername); + g_object_set_data(G_OBJECT(vbox),"password",entryPassword); + g_object_set_data(G_OBJECT(vbox),"email",entryEmail); + g_object_set_data(G_OBJECT(vbox),"usernameOk",isUsernameOk); + g_object_set_data(G_OBJECT(vbox),"passwordOk",isPasswordOk); + g_object_set_data(G_OBJECT(vbox),"emailOk",isEmailOk); + g_object_set_data(G_OBJECT(vbox),"password_confirm",entryPassword2); + g_object_set_data(G_OBJECT(vbox),"newsletter",checkNewsletter); + g_signal_connect(G_OBJECT(entryUsername),"changed",(GCallback)account_username_changed,vbox); + g_signal_connect(G_OBJECT(entryPassword),"changed",(GCallback)account_password_changed,vbox); + g_signal_connect(G_OBJECT(entryEmail),"changed",(GCallback)account_email_changed,vbox); + g_signal_connect(G_OBJECT(entryPassword2),"changed",(GCallback)account_password_changed,vbox); + return vbox; +} + +/* static GtkWidget *create_confirmation_page(){ GtkWidget *vbox=gtk_vbox_new(FALSE,2); GtkWidget *label=gtk_label_new(NULL); @@ -116,15 +321,15 @@ static GtkWidget *create_confirmation_page(){ gtk_widget_show_all(vbox); return vbox; } +*/ + +static GtkWidget *create_error_page(){ + GtkWidget *vbox=gtk_table_new(2, 1, FALSE); + GtkWidget *label=gtk_label_new(_("Error, account not validated, username already used or server unreachable.\nPlease go back and try again.")); + + gtk_table_attach(GTK_TABLE(vbox), label, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 100); -static GtkWidget *create_creation_page(){ - GtkWidget *vbox=gtk_vbox_new(FALSE,2); - GtkWidget *label=gtk_label_new(NULL); - GtkWidget *progress=gtk_progress_bar_new(); - gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (vbox), progress, TRUE, TRUE, 2); g_object_set_data(G_OBJECT(vbox),"label",label); - g_object_set_data(G_OBJECT(vbox),"progress",progress); gtk_widget_show_all(vbox); return vbox; } @@ -137,61 +342,130 @@ static GtkWidget *create_finish_page(){ return vbox; } +static GtkWidget *wait_for_activation() { + GtkWidget *vbox=gtk_table_new(2, 1, FALSE); + GtkWidget *label=gtk_label_new(_("Please validate your account by clicking on the link we just sent you by email.\n" + "Then come back here and press Next button.")); + + gtk_table_attach(GTK_TABLE(vbox), label, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 100); + + g_object_set_data(G_OBJECT(vbox),"label",label); + gtk_widget_show_all(vbox); + return vbox; +} + +int is_account_validated(GtkWidget *page) { + LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(gtk_widget_get_toplevel(page)); + return linphone_account_creator_test_validation(creator); +} + static void linphone_gtk_assistant_closed(GtkWidget *w){ gtk_widget_destroy(w); } +static void linphone_gtk_assistant_prepare(GtkWidget *assistant, GtkWidget *page){ + int pagenum=gtk_assistant_get_current_page(GTK_ASSISTANT(assistant)); + + if (pagenum == 5) { + gtk_assistant_commit(GTK_ASSISTANT(assistant)); + } else if (pagenum == gtk_assistant_get_n_pages(GTK_ASSISTANT(assistant)) - 1) { + // Saving the account and making it default + LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(assistant); + LinphoneProxyConfig *cfg=linphone_proxy_config_new(); + linphone_proxy_config_set_identity(cfg, creator->username); + linphone_proxy_config_set_server_addr(cfg, creator->domain); + linphone_proxy_config_set_route(cfg, creator->route); + linphone_proxy_config_expires(cfg, 3600); + linphone_proxy_config_enable_publish(cfg, FALSE); + linphone_proxy_config_enable_register(cfg, TRUE); + + gchar *username = creator->username + 4; + const gchar *needle = "@"; + username = g_strndup(username, (g_strrstr(username, needle) - username)); + gchar domain[128]; + g_sprintf(domain, "\"%s\"", creator->domain + 4); + LinphoneAuthInfo *info=linphone_auth_info_new(username, username, creator->password, NULL, domain); + linphone_core_add_auth_info(linphone_gtk_get_core(),info); + + if (linphone_core_add_proxy_config(linphone_gtk_get_core(),cfg)==-1) + return; + + linphone_core_set_default_proxy(linphone_gtk_get_core(),cfg); + linphone_gtk_load_identities(); + } +} + static int linphone_gtk_assistant_forward(int curpage, gpointer data){ GtkWidget *w=(GtkWidget*)data; GtkWidget *box=gtk_assistant_get_nth_page(GTK_ASSISTANT(w),curpage); if (curpage==1){ GtkWidget *create_button=(GtkWidget*)g_object_get_data(G_OBJECT(box),"create_account"); - if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(create_button))){ - g_error("Not implemented yet..."); + GtkWidget *setup_linphone_account=(GtkWidget*)g_object_get_data(G_OBJECT(box),"setup_linphone_account"); + GtkWidget *setup_account=(GtkWidget*)g_object_get_data(G_OBJECT(box),"setup_account"); + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(create_button))) { + curpage += 3; // Going to P33 + } + else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setup_linphone_account))) { + curpage += 2; // Going to P32 + } + else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(setup_account))) { + curpage += 1; // Going to P31 } - }else if (curpage==2){ - LinphoneAccountCreator *c=linphone_gtk_assistant_get_creator(w); - linphone_account_creator_set_username(c,gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"username")))); } - return curpage+1; -} + else if (curpage == 2) { // Account's informations entered + LinphoneAccountCreator *c=linphone_gtk_assistant_get_creator(w); + if (!g_regex_match_simple("^sip:[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\\.-][a-z0-9]+)*)+\\.[a-z]{2,}$", gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"username"))), 0, 0)) { + gchar identity[128]; + g_sprintf(identity, "sip:%s", gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"username")))); + linphone_account_creator_set_username(c, identity); + } else { + linphone_account_creator_set_username(c, gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"username")))); + } -static void linphone_gtk_assistant_apply(GtkWidget *w){ - LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(w); - GtkWidget *page=gtk_assistant_get_nth_page(GTK_ASSISTANT(w),gtk_assistant_get_current_page(GTK_ASSISTANT(w))); - GtkWidget *progress=(GtkWidget*)g_object_get_data(G_OBJECT(page),"progress"); - LinphoneProxyConfig *res; - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Please wait...")); - linphone_core_set_waiting_callback(linphone_gtk_get_core(),progress_bar_update,progress); - res=linphone_account_creator_validate(creator); - if (res){ - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Ok !")); - gtk_assistant_set_page_complete(GTK_ASSISTANT(w),page,TRUE); - }else{ - gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress),_("Communication problem, please try again later.")); + if (!g_regex_match_simple("^sip:([a-z0-9]+([\\.-][a-z0-9]+)*)+\\.[a-z]{2,}$", gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"domain"))), 0, 0)) { + gchar proxy[128]; + g_sprintf(proxy, "sip:%s", gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"domain")))); + linphone_account_creator_set_domain(c, proxy); + } else { + linphone_account_creator_set_domain(c, gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"domain")))); + } + linphone_account_creator_set_route(c, gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"route")))); + linphone_account_creator_set_password(c,gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"password")))); + curpage = gtk_assistant_get_n_pages(GTK_ASSISTANT(w)) - 1; // Going to the last page } - linphone_core_set_waiting_callback(linphone_gtk_get_core(),linphone_gtk_wait,NULL); - if (res) linphone_core_add_proxy_config(linphone_gtk_get_core(),res); - gtk_assistant_set_page_complete(GTK_ASSISTANT(w),page,TRUE); -} - -static void linphone_gtk_assistant_prepare(GtkWidget *assistant, GtkWidget *page){ - int pagenum=gtk_assistant_get_current_page(GTK_ASSISTANT(assistant)); - if (pagenum==3){ - check_username(page); - }else if (pagenum==4){ - GtkWidget *label=(GtkWidget*)g_object_get_data(G_OBJECT(page),"label"); - LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(assistant); - gchar *text=g_strdup_printf("You have choosen '%s' as username.\nDo you confirm the creation of the account ?",linphone_account_creator_get_username(creator)); - gtk_label_set_text(GTK_LABEL(label),text); - g_free(text); - }else if (pagenum==5){ - GtkWidget *label=(GtkWidget*)g_object_get_data(G_OBJECT(page),"label"); - LinphoneAccountCreator *creator=linphone_gtk_assistant_get_creator(assistant); - gchar *text=g_strdup_printf("Account creation in progress for '%s'",linphone_account_creator_get_username(creator)); - gtk_label_set_text(GTK_LABEL(label),text); - g_free(text); + else if (curpage == 3) { // Linphone Account's informations entered + LinphoneAccountCreator *c=linphone_gtk_assistant_get_creator(w); + gchar identity[128]; + g_sprintf(identity, "sip:%s@sip.linphone.org", gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"username")))); + linphone_account_creator_set_username(c, identity); + linphone_account_creator_set_domain(c, "sip:sip.linphone.org"); + linphone_account_creator_set_password(c,gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"password")))); + curpage = gtk_assistant_get_n_pages(GTK_ASSISTANT(w)) - 1; // Going to the last page + } + else if (curpage == 4) { // Password & Email entered + LinphoneAccountCreator *c=linphone_gtk_assistant_get_creator(w); + linphone_account_creator_set_username(c, gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"username")))); + linphone_account_creator_set_password(c,gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"password")))); + linphone_account_creator_set_email(c,gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(box),"email")))); + linphone_account_creator_set_suscribe(c,gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(box),"newsletter")))); + if (create_account(w) == 1) { + curpage += 1; + } else { // Error when attempting to create the account + curpage += 2; + } + } + else if (curpage == 5) { // Waiting for account validation + if (is_account_validated(w) == 1) { + curpage += 2; // Going to the last page + } else { + curpage += 1; + } + } + else { + curpage += 1; } + return curpage; } static LinphoneAccountCreator * linphone_gtk_assistant_init(GtkWidget *w){ @@ -214,12 +488,19 @@ LinphoneAccountCreator *linphone_gtk_assistant_get_creator(GtkWidget*w){ GtkWidget * linphone_gtk_create_assistant(void){ GtkWidget *w=gtk_assistant_new(); + gtk_window_set_resizable (GTK_WINDOW(w), FALSE); + + ok = create_pixbuf(linphone_gtk_get_ui_config("ok","ok.png")); + notok = create_pixbuf(linphone_gtk_get_ui_config("notok","notok.png")); + GtkWidget *p1=create_intro(); GtkWidget *p2=create_setup_signin_choice(); - GtkWidget *p3=create_username_chooser(); - GtkWidget *checking=create_username_checking_page(); - GtkWidget *confirm=create_confirmation_page(); - GtkWidget *creation=create_creation_page(); + GtkWidget *p31=create_account_informations_page(); + GtkWidget *p32=create_linphone_account_informations_page(); + GtkWidget *p33=create_account_information_page(); + //GtkWidget *confirm=create_confirmation_page(); + GtkWidget *validate=wait_for_activation(); + GtkWidget *error=create_error_page(); GtkWidget *end=create_finish_page(); linphone_gtk_assistant_init(w); @@ -227,38 +508,50 @@ GtkWidget * linphone_gtk_create_assistant(void){ gtk_assistant_set_page_type(GTK_ASSISTANT(w),p1,GTK_ASSISTANT_PAGE_INTRO); gtk_assistant_set_page_title(GTK_ASSISTANT(w),p1,_("Welcome to the account setup assistant")); gtk_assistant_set_page_complete(GTK_ASSISTANT(w),p1,TRUE); + gtk_assistant_append_page(GTK_ASSISTANT(w),p2); gtk_assistant_set_page_type(GTK_ASSISTANT(w),p2,GTK_ASSISTANT_PAGE_CONTENT); gtk_assistant_set_page_title(GTK_ASSISTANT(w),p2,_("Account setup assistant")); gtk_assistant_set_page_complete(GTK_ASSISTANT(w),p2,TRUE); - gtk_assistant_append_page(GTK_ASSISTANT(w),p3); - gtk_assistant_set_page_type(GTK_ASSISTANT(w),p3,GTK_ASSISTANT_PAGE_CONTENT); - gtk_assistant_set_page_title(GTK_ASSISTANT(w),p3,_("Choosing a username")); - - gtk_assistant_append_page(GTK_ASSISTANT(w),checking); - gtk_assistant_set_page_type(GTK_ASSISTANT(w),checking,GTK_ASSISTANT_PAGE_PROGRESS); - gtk_assistant_set_page_title(GTK_ASSISTANT(w),checking,_("Verifying")); - - gtk_assistant_append_page(GTK_ASSISTANT(w),confirm); + + gtk_assistant_append_page(GTK_ASSISTANT(w),p31); + gtk_assistant_set_page_type(GTK_ASSISTANT(w),p31,GTK_ASSISTANT_PAGE_CONFIRM); + gtk_assistant_set_page_complete(GTK_ASSISTANT(w),p31,FALSE); + gtk_assistant_set_page_title(GTK_ASSISTANT(w),p31,_("Configure your account (step 1/1)")); + + gtk_assistant_append_page(GTK_ASSISTANT(w),p32); + gtk_assistant_set_page_type(GTK_ASSISTANT(w),p32,GTK_ASSISTANT_PAGE_CONFIRM); + gtk_assistant_set_page_complete(GTK_ASSISTANT(w),p32,FALSE); + gtk_assistant_set_page_title(GTK_ASSISTANT(w),p32,_("Enter your sip username (step 1/1)")); + + gtk_assistant_append_page(GTK_ASSISTANT(w),p33); + gtk_assistant_set_page_type(GTK_ASSISTANT(w),p33,GTK_ASSISTANT_PAGE_CONFIRM); + gtk_assistant_set_page_title(GTK_ASSISTANT(w),p33,_("Enter account information (step 1/2)")); + + /*gtk_assistant_append_page(GTK_ASSISTANT(w),confirm); gtk_assistant_set_page_type(GTK_ASSISTANT(w),confirm,GTK_ASSISTANT_PAGE_CONFIRM); - gtk_assistant_set_page_title(GTK_ASSISTANT(w),confirm,_("Confirmation")); - gtk_assistant_set_page_complete(GTK_ASSISTANT(w),confirm,TRUE); + gtk_assistant_set_page_title(GTK_ASSISTANT(w),confirm,_("Confirmation (step 2/2)")); + gtk_assistant_set_page_complete(GTK_ASSISTANT(w),confirm,TRUE);*/ + + gtk_assistant_append_page(GTK_ASSISTANT(w),validate); + gtk_assistant_set_page_type(GTK_ASSISTANT(w),validate,GTK_ASSISTANT_PAGE_CONTENT); + gtk_assistant_set_page_title(GTK_ASSISTANT(w),validate,_("Validation (step 2/2)")); + gtk_assistant_set_page_complete(GTK_ASSISTANT(w),validate,TRUE); - gtk_assistant_append_page(GTK_ASSISTANT(w),creation); - gtk_assistant_set_page_type(GTK_ASSISTANT(w),creation,GTK_ASSISTANT_PAGE_PROGRESS); - gtk_assistant_set_page_title(GTK_ASSISTANT(w),creation,_("Creating your account")); + gtk_assistant_append_page(GTK_ASSISTANT(w),error); + gtk_assistant_set_page_type(GTK_ASSISTANT(w),error,GTK_ASSISTANT_PAGE_CONTENT); + gtk_assistant_set_page_title(GTK_ASSISTANT(w),error,_("Error")); gtk_assistant_append_page(GTK_ASSISTANT(w),end); gtk_assistant_set_page_type(GTK_ASSISTANT(w),end,GTK_ASSISTANT_PAGE_SUMMARY); - gtk_assistant_set_page_title(GTK_ASSISTANT(w),end,_("Now ready !")); - + gtk_assistant_set_page_title(GTK_ASSISTANT(w),end,_("Terminating")); + gtk_assistant_set_forward_page_func(GTK_ASSISTANT(w),linphone_gtk_assistant_forward,w,NULL); g_signal_connect(G_OBJECT(w),"close",(GCallback)linphone_gtk_assistant_closed,NULL); g_signal_connect(G_OBJECT(w),"cancel",(GCallback)linphone_gtk_assistant_closed,NULL); - g_signal_connect(G_OBJECT(w),"apply",(GCallback)linphone_gtk_assistant_apply,NULL); g_signal_connect(G_OBJECT(w),"prepare",(GCallback)linphone_gtk_assistant_prepare,NULL); + gtk_widget_show(w); - + return w; } - diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am index 60194fcd866add4b5fe5f3caf9c72a23b8c5b6f4..4e7c56ed6208c84b804bc9c75de7992ddb1ec462 100644 --- a/pixmaps/Makefile.am +++ b/pixmaps/Makefile.am @@ -14,6 +14,8 @@ status-offline.png \ contact-orange.png dialer-orange.png history-orange.png\ startcall-green.png stopcall-red.png addcall-green.png linphone.icns \ contact_starred.png contact_unstarred.png \ -speaker.png +speaker.png \ +ok.png \ +notok.png EXTRA_DIST=$(pixmap_DATA) diff --git a/pixmaps/notok.png b/pixmaps/notok.png new file mode 100644 index 0000000000000000000000000000000000000000..84813bc2d7452bdc7d4875a9489b8c71cb4c5913 Binary files /dev/null and b/pixmaps/notok.png differ diff --git a/pixmaps/ok.png b/pixmaps/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..769986fb79c67b15f7f58082ad0f170fc97c0275 Binary files /dev/null and b/pixmaps/ok.png differ