diff options
Diffstat (limited to 'lib/network/connection_manager.pm')
-rw-r--r-- | lib/network/connection_manager.pm | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/lib/network/connection_manager.pm b/lib/network/connection_manager.pm new file mode 100644 index 0000000..e6b2a83 --- /dev/null +++ b/lib/network/connection_manager.pm @@ -0,0 +1,506 @@ +package network::connection_manager; + +use strict; + +use common; +use run_program; +use detect_devices; +use interactive; +use mygtk2; +use ugtk2 qw(:create :helpers :wrappers); +use Gtk2::SimpleList; +use network::signal_strength; +use network::network; +use network::tools; +use network::connection; +use modules; +use locale; # for cmp + +sub create_pixbufs() { + { + state => { map { $_ => gtkcreate_pixbuf($_) } qw(connected disconnected refresh) }, + link_level => { map { + $_ => gtkcreate_pixbuf('wifi-' . sprintf('%03d', $_))->scale_simple(24, 24, 'hyper'); + } qw(20 40 60 80 100) }, + encryption => { map { + $_ => gtkcreate_pixbuf("encryption-$_-24"); + } qw(open weak strong) }, + }; +} + +sub new { + my ($class, $in, $net, $w, $pixbufs) = @_; + bless { + in => $in, net => $net, gui => { w => $w, pixbufs => $pixbufs }, + }, $class; +} + +sub set_connection { + my ($cmanager, $connection) = @_; + $cmanager->{connection} = $connection; + $cmanager->{wait_message_timeout} = 20*1000 if ref($connection) eq 'network::connection::wireless'; +} + +sub check_setup { + my ($cmanager) = @_; + $cmanager->{connection}{passed_setup} = + (!$cmanager->{connection}->can("check_device") || + $cmanager->{connection}->check_device) && + (!$cmanager->{connection}->can("check_hardware") || + !$cmanager->{connection}->check_hardware_is_slow && $cmanager->{connection}->check_hardware) + if !defined $cmanager->{connection}{passed_setup}; + $cmanager->{connection}{passed_setup}; +} + +sub setup_connection { + my ($cmanager) = @_; + + $cmanager->load_settings; + + my @packages = $cmanager->{connection}->can('get_packages') ? $cmanager->{connection}->get_packages : (); + if (@packages && !$cmanager->{in}->do_pkgs->install(@packages)) { + $cmanager->{in}->ask_warn(N("Error"), N("Could not install the packages (%s)!", join(', ', @packages))); + return; + } + $cmanager->{connection}->prepare_device; + $cmanager->{connection}->setup_thirdparty($cmanager->{in}) or return; + if ($cmanager->{connection}->can("check_device") && !$cmanager->{connection}->check_device) { + $cmanager->{in}->ask_warn(N("Error"), $cmanager->{connection}{device}{error}); + return; + } + my $device_ready = 1; + if ($cmanager->{connection}->can('check_hardware')) { + #- FIXME: change message to "Checking device..." in cooker + my $_wait = $cmanager->{in}->wait_message(N("Please wait"), N("Configuring device...")); + $device_ready = $cmanager->{connection}->check_hardware; + } + if ($cmanager->{connection}->can('get_hardware_settings') && !$device_ready) { + $cmanager->{in}->ask_from_({ + title => N("Network settings"), + messages => N("Please enter settings for network"), + auto_window_size => 1, + }, $cmanager->{connection}->get_hardware_settings) or return; + if ($cmanager->{connection}->can("check_hardware_settings") && !$cmanager->{connection}->check_hardware_settings) { + $cmanager->{in}->ask_warn(N("Error"), $cmanager->{connection}->{hardware}{error}); + return; + } + } + if ($cmanager->{connection}->can('configure_hardware') && !$device_ready) { + my $wait = $cmanager->{in}->wait_message(N("Please wait"), N("Configuring device...")); + if (!$cmanager->{connection}->configure_hardware) { + undef $wait; + $cmanager->{in}->ask_warn(N("Error"), $cmanager->{connection}{hardware}{error}) if $cmanager->{connection}{hardware}{error}; + return; + } + } + $cmanager->write_settings; + $cmanager->{connection}{passed_setup} = 1; +} + +sub load_settings { + my ($cmanager) = @_; + + $cmanager->{connection}->load_interface_settings; + $cmanager->{connection}->guess_hardware_settings if $cmanager->{connection}->can('guess_hardware_settings'); + $cmanager->{connection}->guess_network_access_settings if $cmanager->{connection}->can('guess_network_access_settings'); + if ($cmanager->{connection}->can('get_providers')) { + $cmanager->{connection}->guess_provider_settings; + $cmanager->{connection}->set_provider; + } + $cmanager->{connection}->guess_protocol($cmanager->{net}) if $cmanager->{connection}->can('guess_protocol'); + $cmanager->{connection}->guess_access_settings if $cmanager->{connection}->can('guess_access_settings'); + $cmanager->{connection}->guess_address_settings if $cmanager->{connection}->can('guess_address_settings'); + $cmanager->{connection}->guess_hostname_settings if $cmanager->{connection}->can('guess_hostname_settings'); + $cmanager->{connection}->guess_network_control_settings if $cmanager->{connection}->can('guess_network_control_settings'); + $cmanager->{connection}->guess_control_settings; +} + +sub write_settings { + my ($cmanager) = @_; + + my $modules_conf = modules::any_conf->read; + $cmanager->{connection}->write_settings($cmanager->{net}, $modules_conf); + $modules_conf->write; +} + +sub configure_connection { + my ($cmanager) = @_; + + if (!$cmanager->check_setup) { + $cmanager->setup_connection or return; + $cmanager->update_networks if $cmanager->{connection}->can('get_networks'); + $cmanager->update_on_status_change; + return; + } + + $cmanager->load_settings; + my $system_file = '/etc/sysconfig/drakx-net'; + my %global_settings = getVarsFromSh($system_file); + + my $error; + do { + undef $error; + $cmanager->{in}->ask_from_({ + title => N("Network settings"), + messages => N("Please enter settings for network"), + icon => $cmanager->{connection}->get_type_icon(48), + banner_title => $cmanager->{connection}->get_description, + }, + [ + $cmanager->{connection}->can('get_network_access_settings') ? ( + { label => $cmanager->{connection}->get_network_access_settings_label, title => 1, advanced => 1 }, + @{$cmanager->{connection}->get_network_access_settings}, + ) : (), + $cmanager->{connection}->can('get_providers') ? ( + @{$cmanager->{connection}->get_provider_settings($cmanager->{net})} + ) : (), + $cmanager->{connection}->can('get_protocols') ? ( + @{$cmanager->{connection}->get_protocol_settings}, + ) : (), + $cmanager->{connection}->can('get_access_settings') ? ( + { label => $cmanager->{connection}->get_access_settings_label, title => 1, advanced => 1 }, + @{$cmanager->{connection}->get_access_settings} + ) : (), + $cmanager->{connection}->can('get_address_settings') && !text2bool($global_settings{AUTOMATIC_ADDRESS}) ? ( + { label => $cmanager->{connection}->get_address_settings_label, title => 1, advanced => 1 }, + @{$cmanager->{connection}->get_address_settings('show_all')} + ) : (), + $cmanager->{connection}->can('get_network_control_settings') ? ( + @{$cmanager->{connection}->get_network_control_settings} + ) : (), + $cmanager->{connection}->can('get_control_settings') ? ( + @{$cmanager->{connection}->get_control_settings} + ) : (), + ], + ) or return; + if ($cmanager->{connection}->can('check_network_access_settings') && !$cmanager->{connection}->check_network_access_settings) { + $cmanager->{in}->ask_warn(N("Error"), $cmanager->{connection}{network_access}{error}{message}); + $error = 1; + } + if ($cmanager->{connection}->can('check_address_settings') && !$cmanager->{connection}->check_address_settings($cmanager->{net})) { + $cmanager->{in}->ask_warn(N("Error"), $cmanager->{connection}{address}{error}{message}); + $error = 1; + } + } while $error; + + $cmanager->{connection}->install_packages($cmanager->{in}) if $cmanager->{connection}->can('install_packages'); + $cmanager->{connection}->unload_connection if $cmanager->{connection}->can('unload_connection'); + + $cmanager->write_settings; + + 1; +} + +sub start_connection { + my ($cmanager) = @_; + + $cmanager->{connection} or return; + if ($cmanager->{connection}->can('get_networks')) { + $cmanager->{connection}{network} && + ($cmanager->{connection}->selected_network_is_configured || + $cmanager->configure_connection) + or return; + } + + gtkset_mousecursor_wait($cmanager->{gui}{w}{window}->window); + my $wait = $cmanager->{in}->wait_message(N("Please wait"), N("Connecting...")); + if ($cmanager->{connection}->can('apply_network_selection')) { + $cmanager->load_settings; + $cmanager->{connection}->apply_network_selection($cmanager); + } + $cmanager->{connection}->prepare_connection if $cmanager->{connection}->can('prepare_connection'); + $cmanager->{connection}->disconnect; + $cmanager->{connection}->connect($cmanager->{in}, $cmanager->{net}); + gtkset_mousecursor_normal($cmanager->{gui}{w}{window}->window); + + $cmanager->update_on_status_change; + if ($cmanager->{wait_message_timeout}) { + $cmanager->{wait_message} = $wait; + Glib::Timeout->add($cmanager->{wait_message_timeout}, + sub { + if ($cmanager->{wait_message}) { + undef $cmanager->{wait_message}; + $cmanager->{in}->ask_warn(N("Error"), N("Connection failed.")) + if !$cmanager->{connection}->get_status; + } + undef; + }); + }; +} + +sub stop_connection { + my ($cmanager) = @_; + + gtkset_mousecursor_wait($cmanager->{gui}{w}{window}->window); + my $_wait = $cmanager->{in}->wait_message(N("Please wait"), N("Disconnecting...")); + $cmanager->{connection}->disconnect; + gtkset_mousecursor_normal($cmanager->{gui}{w}{window}->window); + + $cmanager->update_on_status_change; +} + +sub monitor_connection { + my ($cmanager) = @_; + my $interface = $cmanager->{connection} && $cmanager->{connection}->get_interface or return; + run_program::raw({ detach => 1 }, '/usr/bin/net_monitor', '--defaultintf', $interface); +} + +sub toggle_would_disconnect { + my ($cmanager) = @_; + + my $network = $cmanager->{connection} && $cmanager->{connection}->get_selected_network; + $cmanager->{connection} && $cmanager->{connection}->get_status && + (!$network || keys(%{$cmanager->{connection}{networks}}) <= 1 || $network->{current}); +} + +sub toggle_connection { + my ($cmanager) = @_; + + if ($cmanager->toggle_would_disconnect) { + $cmanager->stop_connection; + } else { + $cmanager->start_connection; + } +} + +sub create_networks_list { + my ($cmanager) = @_; + + if ($cmanager->{gui}{show_unique_network}) { + $cmanager->{gui}{networks_list} = gtknew('HBox', spacing => 20); + return; + } + + $cmanager->{gui}{networks_list} = Gtk2::SimpleList->new( + "AP" => "hidden", + '' => "pixbuf", + N("SSID") => "text", + N("Signal strength") => "pixbuf", + N("Encryption") => "pixbuf", + N("Operating Mode") => "text", + ); + $cmanager->{gui}{networks_list}->get_selection->set_mode('single'); + $cmanager->{gui}{networks_list}->get_selection->signal_connect('changed' => sub { $cmanager->select_network }); + + $cmanager->{gui}{networks_list}->signal_connect('query-tooltip' => sub { + my ($widget, $x, $y, $kbd_tip, $tooltip) = @_; + (undef, undef, my $model, my $path, my $iter) = $widget->get_tooltip_context($x, $y, $kbd_tip) or return; + my $ap = $model->get($iter, 0); + my $network = $cmanager->{connection}{networks}{$ap}; + $tooltip->set_text(sprintf("%2.2f%% %s\n", $network->{signal_strength}, $network->{flags})); + $widget->set_tooltip_row($tooltip, $path); + 1; + }); + $cmanager->{gui}{networks_list}->set_has_tooltip(1); + $cmanager->{gui}{networks_list}->get_column(1)->set_sort_column_id(1); + $cmanager->{gui}{networks_list}->get_model->set_sort_func (1, sub { + my ($sortable, $iter_left, $iter_right) = @_; + my $s1 = $sortable->get($iter_left, 2); + my $s2 = $sortable->get($iter_right, 2); + return $s1 cmp $s2; + }); + $cmanager->{gui}{networks_list}->get_column(2)->set_sort_column_id(2); + $cmanager->{gui}{networks_list}->get_model->set_sort_func (2, sub { + my ($sortable, $iter_left, $iter_right) = @_; + my $s1 = $cmanager->{connection}{networks}{$sortable->get($iter_left, 0)}->{signal_strength}; + my $s2 = $cmanager->{connection}{networks}{$sortable->get($iter_right, 0)}->{signal_strength}; + return $s1 <=> $s2; + }); + $cmanager->{gui}{networks_list}->get_column(3)->set_sort_column_id(3); + $cmanager->{gui}{networks_list}->get_model->set_sort_func (3, sub { + my ($sortable, $iter_left, $iter_right) = @_; + my $s1 = $cmanager->{connection}{networks}{$sortable->get($iter_left, 0)}->{flags}; + my $s2 = $cmanager->{connection}{networks}{$sortable->get($iter_right, 0)}->{flags}; + #FIXME Should define an explicit order OPEN < WEP < WPA + return $s1 cmp $s2; + }); + $cmanager->{gui}{networks_list}->set_enable_search(1); + $cmanager->{gui}{networks_list}->set_search_column(1); + $cmanager->{gui}{networks_list}->set_search_equal_func(sub { + my ($model, $column, $key, $iter, $data) = @_; + return $model->get($iter, 2) !~ /^\Q$key/i; + }); + # Sort by signal level by default + $cmanager->{gui}{networks_list}->get_model->set_sort_column_id(2, 'descending'); +} + +sub select_network { + my ($cmanager) = @_; + + if ($cmanager->{connection}) { + my ($selected) = $cmanager->{gui}{networks_list}->get_selected_indices; + $cmanager->{connection}{network} = defined $selected && $cmanager->{gui}{networks_list}{data}[$selected][0]; + } + $cmanager->update_on_status_change; +} + +sub filter_networks { + my ($connection) = @_; + $_->{configured} = $connection->network_is_configured($_) foreach values %{$connection->{networks}}; + sort { + $b->{current} <=> $a->{current} || $b->{configured} <=> $a->{configured} || $b->{signal_strength} <=> $a->{signal_strength} || $a->{name} cmp $b->{name}; + } values %{$connection->{networks}}; +} + +sub update_networks { + my ($cmanager) = @_; + + @{$cmanager->{gui}{networks_list}{data}} = (); + + if ($cmanager->{connection}) { + $cmanager->check_setup || $cmanager->setup_connection or return; + + my $wait = $cmanager->{connection}->network_scan_is_slow && $cmanager->{in}->wait_message(N("Please wait"), N("Scanning for networks...")); + $cmanager->{connection}{networks} = $cmanager->{connection}->get_networks($cmanager->{net}); + $cmanager->{connection}{network} ||= find { $cmanager->{connection}{networks}{$_}{current} } keys %{$cmanager->{connection}{networks}}; + + my $routes = network::tools::get_routes(); + my $interface = $cmanager->{connection}->get_interface; + my $connected = exists $routes->{$interface}{network}; + + my @networks = filter_networks($cmanager->{connection}); + foreach my $network (@networks) { + my $ap = $network->{ap}; + my $connected_pixbuf = $network->{current} ? $connected ? $cmanager->{gui}{pixbufs}{state}{connected} : $cmanager->{gui}{pixbufs}{state}{refresh} : undef; + my $network_name = !$network->{essid} && exists $cmanager->{net}{wireless}{$ap} && $cmanager->{net}{wireless}{$ap}{WIRELESS_ESSID} || $network->{name}; + my $strength_pixbuf = network::signal_strength::get_strength_icon($network); + + if ($cmanager->{gui}{show_unique_network}) { + gtkset($cmanager->{gui}{networks_list}, children => [ + 1, $network_name, + 0, Gtk2::Image->new_from_pixbuf($strength_pixbuf), + ]); + $cmanager->{connection}{network} = $network_name; + } else { + push @{$cmanager->{gui}{networks_list}{data}}, [ + $ap || $network->{name}, + $connected_pixbuf, + $network_name, + $strength_pixbuf, + $cmanager->{gui}{pixbufs}{encryption}{$network->{flags} =~ /WPA/i ? 'strong' : $network->{flags} =~ /WEP/i ? 'weak' : 'open'}, + $network->{mode}, + ]; + } + } + + if ($cmanager->{connection}{network} && !$cmanager->{gui}{show_unique_network}) { + my $index = eval { find_index { $_->[0] eq $cmanager->{connection}{network} } @{$cmanager->{gui}{networks_list}{data}} }; + $cmanager->{gui}{networks_list}->select($index) if defined $index; + } + + undef $wait; + } + + $cmanager->update_on_status_change; +} + +sub update_on_status_change { + my ($cmanager) = @_; + + if ($cmanager->{gui}{buttons}{connect_toggle}) { + my $disconnect = $cmanager->toggle_would_disconnect; + $cmanager->{gui}{buttons}{connect_toggle}->set_label($disconnect ? N("Disconnect") : N("Connect")); + gtkset($cmanager->{gui}{buttons}{connect_toggle}, image => gtknew('Image', file => $disconnect ? 'stop-16' : 'activate-16')) + if $cmanager->{gui}{buttons}{connect_toggle}->get_image; + $cmanager->{gui}{buttons}{connect_toggle}->set_sensitive( + $cmanager->{connection} && ( + !$cmanager->{connection}->can('get_networks') || + $cmanager->{connection}->get_status || #- always allow to disconnect if connected + $cmanager->{connection}{network} + )); + } + + $cmanager->{gui}{buttons}{connect_start}->set_sensitive($cmanager->{connection} && (!$cmanager->{connection}->get_status || $cmanager->{connection}{network})) + if $cmanager->{gui}{buttons}{connect_start}; + $cmanager->{gui}{buttons}{connect_stop}->set_sensitive($cmanager->{connection} && $cmanager->{connection}->get_status) + if $cmanager->{gui}{buttons}{connect_stop}; + + my $allow_configure; + if ($cmanager->{connection}) { + my $may_have_network = + !$cmanager->{connection}->can('get_networks') || + $cmanager->{connection}{network}; + $allow_configure = $may_have_network || !$cmanager->check_setup; + } + + $cmanager->{gui}{buttons}{configure}->set_sensitive($allow_configure) + if $cmanager->{gui}{buttons}{configure}; + + my $has_interface = to_bool($cmanager->{connection} && $cmanager->{connection}->get_interface); + $cmanager->{gui}{buttons}{refresh}->set_sensitive($has_interface) + if $cmanager->{gui}{buttons}{refresh}; + $cmanager->{gui}{buttons}{monitor}->set_sensitive($has_interface) + if $cmanager->{gui}{buttons}{monitor}; + + if ($cmanager->{gui}{status_image} && $cmanager->{connection}) { + my $icon = $cmanager->{connection}->get_status_icon; + ugtk2::_find_imgfile($icon) or $icon = $cmanager->{connection}->get_type_icon; + gtkset($cmanager->{gui}{status_image}, file => $icon); + } +} + +sub _get_network_event_message { + my ($connections, $member, @args) = @_; + #- FIXME: the hostname.d script and s2u use a different D-Bus interface + if ($member eq 'hostname') { + my ($hostname) = @args; + N("Hostname changed to \"%s\"", $hostname); + } elsif ($member eq 'status') { + my ($status, $interface) = @args; + my $event_connection = find { $_->get_interface eq $interface } @$connections; + $event_connection && $event_connection->get_status_message($status); + } +} + +sub setup_dbus_handlers { + my ($cmanagers, $connections, $on_network_event, $dbus) = @_; + #- FIXME: use network::monitor? + $dbus->{connection}->add_filter( + sub { + my ($_con, $msg) = @_; + if ($msg->get_interface eq 'com.mandriva.network') { + my $member = $msg->get_member; + my $message = _get_network_event_message($connections, $member, $msg->get_args_list); + $on_network_event->($message) if $on_network_event && $message; + if ($member eq 'status') { + my ($status, $interface) = $msg->get_args_list; + print "got connection status event: $status $interface\n"; + my $cmanager = find { $_->{connection}->get_interface eq $interface } @$cmanagers + or return; + #- FIXME: factorize in update_on_status_change() and check why update_networks() calls update_on_status_change() + if ($cmanager->{connection}->can('get_networks') && !$cmanager->{connection}->network_scan_is_slow) { + $cmanager->update_networks; + } else { + $cmanager->network::connection_manager::update_on_status_change; + } + if ($cmanager->{wait_message}) { + if ($status eq 'interface_up') { + undef $cmanager->{wait_message}; + } elsif ($status =~ /_failure$/) { + undef $cmanager->{wait_message}; + $cmanager->{in}->ask_warn(N("Error"), join("\n", N("Connection failed."), if_($message, $message))); + } + } + } + } + if ($msg->get_interface eq 'com.mandriva.monitoring.wireless' && $msg->get_member eq 'Event') { + my ($event, $interface) = $msg->get_args_list; + print "got wireless event: $event $interface\n"; + # eugeni: wpa_supplicant seems to issue 'Authentication..timed out messages' even if they + # are not fatal (#54002). We should either handle them with more care, or just ignore them altogether +# my $cmanager = find { $_->{connection}->get_interface eq $interface } @$cmanagers; +# if ($cmanager && $cmanager->{wait_message}) { +# # CTRL-EVENT-CONNECTED does not have to be handled, further status will be handled by interface status code +# if ($event =~ /Authentication with (.+?) timed out/) { +# undef $cmanager->{wait_message}; +# $cmanager->{in}->ask_warn(N("Error"), N("Connection failed.")); +# } +# } + } + }); + $dbus->{connection}->add_match("type='signal',interface='com.mandriva.network'"); + $dbus->{connection}->add_match("type='signal',interface='com.mandriva.monitoring.wireless'"); + dbus_object::set_gtk2_watch_helper($dbus); +} + +1; |