#!/usr/bin/perl -w # # Copyright (C) 2004 Domenico Delle Side (dds@gnulinux.it) # # TimSMS: Interfaccia testuale per il servizio di sms gratuiti di # Telecom Italia Mobile (solo per clienti) # # Versione 0.7.6 # # use strict; use utf8; use encoding 'utf-8'; use Encode qw(encode); use LWP::UserAgent; use Getopt::Long; use URI::Escape; use Gtk2 '-init'; use Gtk2::SimpleMenu; use Gtk2::SimpleList; use constant TRUE => 1; use constant FALSE => 0; my $username = ""; # numero di telefono my $password = ""; # password numerica my $db_file = $ENV{HOME} . '/.timsms.db'; my $cfg_file = $ENV{HOME} . '/.timsms.rc'; my $agent = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7) Gecko/20040928 Firefox/0.9.3'; # sostituibile my $cookie_file = ".cookies"; my ($dest_number, $sms_text, $ua, $sender); my ($success, $failure) = (0, 1); my ($help, $verbose) = ('', ''); my %numeri; my %service_values = ( CODSMS => '', SENDER => '', NOME_LISTA => ' ', OTPWD => ' ', contatoreSms => '', chr => '', DEST => '', SHORT_MESSAGE => '', SHORT_MESSAGE2 => '' ); # gui my $entry_mittente; my $entry_destinatario; my $view; my $window; # /gui #################################################################### sub help { # Stampa a schermo una breve descrizione del programma e delle sue # opzioni print < ) { chomp; s/^[\#].*$//; # Kill comments next if /^\s*$/; # ignore empty lines ($var, $val) = split('=', $_); $username = $val if ( $var eq 'username' ); $password = $val if ( $var eq 'password' ); $service_values{'SENDER'} = $val if ( $var eq 'sender' ); } close(CONF); } sub parse_db_rubrica { my $var; my $val; open(NUMDB, "<$db_file") or die 'Unable to open configuration file' . "\n."; while () { chomp; s/^[\#].*$//; # Kill comments next if /^\s*$/; # ignore empty lines ($var, $val) = split(';', $_); $numeri{$var} = $val; } close(NUMDB); } sub my_uri_escape { # Funzione wrapper per evitare di ripetere continuamente lo stesso # testo. Si è dovuto ricorrere a questa soluzione poiché il # modulo URI::Escape poteva far sorgere noie maneggiando stringhe # codificate con UTF-8, perciò si è deciso di forzare la # codifica ad iso-8859-1 che è sicuramente ben supportato. my $text = shift; return uri_escape(encode("iso-8859-1", $text)); } sub set_request_field { # Riempie gli elementi dell'hash che contiene i valori da passare alla # form per l'invio degli sms, eseguendo alcuni calcoli (giusti?) per # determinare se l'sms è singolo o multiplo. my ($sender, $dest, $text) = @_; my $single_sms_head_len = 21; my $first_long_sms_head_len = 27; my $other_long_sms_head_len = 6; my $text_len = length($text); my $sender_len = length($sender); my $num = 1; my $total_sms_len = $single_sms_head_len; $num = int(($text_len - 133)/154) + 2 if ( $text_len > 141 ); # spieghiamo l'algoritmo usato: se il testo totale del messaggio è # lungo più di 141 caratteri, allora occorrerà mandare un sms di # tipo LONG. Per sapere di quante parti è composto il messaggio # LONG (al massimo 4), sottraiamo al totale il numero di caratteri del # primo messaggio (133, ovvero 160 - $first_long_sms_head_len) e # dividiamo il risultato per 154 (la lunghezza degli altri messaggi di # tipo long, 160 - $other_long_sms_head_len). Otterremo un intero che # ci dice "per difetto" il numero di parti all'infuori della prima; # per ottenere il numero esatto di parti, occorre aggiungere 2 (uno # per la prima parte ed uno per il fatto che il conto avviene per # difetto). if ( $num > 1 ) { $total_sms_len = $first_long_sms_head_len; $total_sms_len += ($num - 1) * $other_long_sms_head_len; } $total_sms_len += $text_len + $sender_len; $service_values{'DEST'} = $dest; $service_values{'SHORT_MESSAGE'} = my_uri_escape($text); $service_values{'SHORT_MESSAGE2'} = $text; $service_values{'chr'} = 640 - $total_sms_len; $service_values{'contatoreSms'} = $num.'/4'; $service_values{'SENDER'} = $sender if $sender; } sub connect_error { # Stampa un messaggio di errore generico e termina lo script. print 'Problemi durante la connessione a http://www.tim.it', "\n"; exit($failure); } sub tim_login { # Effettua il login, preoccupandosi di verificare se questo è # avvenuto con successo o meno. In caso affermativo, ritorna l'oggetto # LWP::UserAgent creato. my ($user, $pass) = @_; my $login_url = 'https://www.tim.it/itims_cas_auth/1,,,00.html'; my $res; $login_url .= '?user=' . $user . '&pwd=' . $pass; $ua = LWP::UserAgent->new(); $ua->cookie_jar({ file => $cookie_file }); $ua->agent($agent); $res = $ua->get($login_url); if ( $res->is_success ) { return $ua if ($res->content =~ /timn_post_autenticazione/); print "Impossibile autenticarsi\n"; print "\n". $res->content if $verbose; exit($failure); } connect_error(); } sub choose_free_sms { # Seleziona il servizio di sms gratuiti, di modo che questa # informazione venga salvata nei cookie. my $free_sms_url = sprintf("http://webmail.posta.tim.it/ews/jsp/it_IT-TIM-UM/jsp/SMS/composerSMS.jsp?msisdn=%s&locale=it_IT-TIM-UM", $username); my $res; $res = $ua->get($free_sms_url); if ( !$res->is_success ) { print $res->content if $verbose; print 'Errore nel selezionare il servizio di sms gratuiti', "\n"; connect_error(); } } sub send_message { # Dati i valori di sender, destinatario e testo, effettua la richiesta # (post) HHTP per inviare il messaggio, controllando la risposta del # server. my ($sender, $dest, $text) = @_; my $service_url = 'http://webmail.posta.tim.it/ews/jsp/it_IT-TIM-UM/jsp/SMS/sendSMS2.jsp'; my $res; set_request_field($sender, $dest, $text); $res = $ua->post($service_url, \%service_values); return 'Messaggio inviato' if ( $res->is_success && ($res->content =~ /Invio effettuato/) ); return 'Messaggio non inviato'; } sub do_all { my ($mittente, $dest, $text) = @_; my $status; $ua = tim_login($username, $password); choose_free_sms(); $status = send_message($mittente, $dest, $text); return $status; } #################################################################### #################################################################### sub create_window { my $win; $win = Gtk2::Window->new; $win->set_title ('TimSMS'); $win->signal_connect (destroy => sub { Gtk2->main_quit; }); $win->set_border_width(2); return $win; } sub create_menu { my $menu_tree = [ _File => { item_type => '', children => [ _Esci => { callback => sub { Gtk2->main_quit; exit(TRUE); }, callback_action => 1, accelerator => 'E', }, ], }, _Rubrica => { item_type => '', children => [ '_Scegli o rimuovi un numero' => { callback => \&mostra_rubrica, callback_action => 1, accelerator => 'S', }, '_Aggiungi un numero' => { callback => \&dialog_inserisci_numero, callback_action => 1, accelerator => 'A', }, ], }, _Aiuto => { item_type => '', children => [ '_Informazioni' => { callback => sub { status("TimSMS\n Copyright (C) 2004\n Domenico Delle Side \n http://digilander.libero.it/bash/timsms/"); }, callback_action => 2, accelerator => 'H', }, ], }, ]; my $menu = Gtk2::SimpleMenu->new( menu_tree => $menu_tree, default_callback => \&return, ); return $menu; } sub create_main_container { my $win = shift; my $vbox; $vbox = Gtk2::VBox->new(FALSE, 6); $win->add($vbox); return $vbox; } sub create_frame { my ($parent, $title) = @_; my $frame; $frame = Gtk2::Frame->new($title); $parent->pack_start($frame, TRUE, TRUE, 0); $frame->set_border_width(3); return $frame; } sub create_entry_box { my $parent = shift; my ($hbox, $entry); $hbox = Gtk2::HBox->new(FALSE, 6); $parent->add($hbox); $hbox->set_border_width(3); $entry = Gtk2::Entry->new(); $hbox->pack_start($entry, FALSE, FALSE, 5); return $entry; } sub create_text_box { my $parent = shift; my ($hbox, $view, $scroll); $hbox = Gtk2::HBox->new(FALSE, 6); $parent->add($hbox); $hbox->set_border_width(3); $view = Gtk2::TextView->new; $view->set_wrap_mode('word'); $scroll = Gtk2::ScrolledWindow->new( undef, undef ); $scroll->set_policy('automatic', 'automatic'); $scroll->set_shadow_type('in'); $scroll->add($view); $hbox->pack_start($scroll, TRUE, TRUE, 5); return $view; } sub add_send_button { my $parent = shift; my $button; my $tooltip; $button = Gtk2::Button->new('_Invia'); $button->signal_connect( clicked => \>k_send_sub ); $tooltip = Gtk2::Tooltips->new(); $tooltip->set_tip($button, 'Invia il messaggio (Alt+I)'); $parent->pack_start($button, FALSE, FALSE, 1); } sub gtk_send_sub { my ($mittente, $destinatario, $testo, $buffer); $mittente = $entry_mittente->get_text(); $destinatario = $entry_destinatario->get_text(); $buffer = $view->get_buffer(); $testo = $buffer->get_text($buffer->get_start_iter(), $buffer->get_end_iter(), FALSE); # print "$mittente - $destinatario - $testo\n"; my $sms_status = do_all($mittente, $destinatario, $testo); status($sms_status); TRUE; } sub status { my $message = shift; my $win = create_window(); $win->set_transient_for($window); $win->set_modal(TRUE); $win->signal_connect (delete_event => sub { $_[0]->destroy; }); my $vbox = create_main_container($win); $vbox->set_border_width(8); my $label = Gtk2::Label->new("\n" . $message . "\n"); $label->set_justify('center'); $vbox->pack_start($label, TRUE, TRUE, 0); my $button = Gtk2::Button->new('_Indietro'); $vbox->pack_start($button, FALSE, FALSE, 0); $button->signal_connect( clicked => sub { $win->destroy; } ); $win->show_all; Gtk2->main; } ###################################################################### # Codice Rubrica ###################################################################### sub get_selected_item { my $list = shift; my $selection; my @row; $selection = ($list->get_selected_indices)[0]; @row = @{(@{$list->{data}})[$selection]}; return [$row[0], $row[1]]; } sub mostra_rubrica { my $win; my $list; my $scroll; my $button; my $canc_button; my $dele_button; my $vbox; my ($tooltip_norm, $tooltip_dele, $tooltip_canc); my $value; $win = create_window(); $win->set_default_size(300, 300); $win->signal_connect('delete_event', sub { $_[0]->destroy }); $win->set_transient_for($window); $vbox = create_main_container($win); $list = Gtk2::SimpleList->new('Nome' => 'text', 'Numero' => 'text'); parse_db_rubrica(); foreach $value ( sort(keys %numeri) ) { push @{$list->{data}}, [ $value, $numeri{$value} ]; } $scroll= Gtk2::ScrolledWindow->new; $scroll->set_policy('automatic', 'automatic'); $scroll->add_with_viewport($list); $vbox->pack_start($scroll, TRUE, TRUE, 0); $button = Gtk2::Button->new_from_stock('gtk-ok'); $button->signal_connect('clicked', sub { $entry_destinatario->set_text(get_selected_item($list)->[1]); $win->destroy; }); $tooltip_norm = Gtk2::Tooltips->new(); $tooltip_norm->set_tip($button, 'Usa il numero selezionato'); $vbox->pack_start($button, FALSE, TRUE, 0); $dele_button = Gtk2::Button->new_from_stock('gtk-delete'); $dele_button->signal_connect('clicked', sub { dialog_rimuovi_numero($win, $list); }); $tooltip_dele = Gtk2::Tooltips->new(); $tooltip_dele->set_tip($dele_button, 'Elimina il numero selezionato'); $vbox->pack_start($dele_button, FALSE, TRUE, 0); $canc_button = Gtk2::Button->new_from_stock('gtk-cancel'); $canc_button->signal_connect('clicked', sub { $win->destroy; }); $tooltip_canc = Gtk2::Tooltips->new(); $tooltip_canc->set_tip($canc_button, 'Annulla la selezione e torna indietro'); $vbox->pack_start($canc_button, FALSE, TRUE, 0); $win->show_all; Gtk2->main; } sub dialog_inserisci_numero { my $win; my $vbox; my $entry_nome; my $entry_numero; my $button; my $canc_button; my ($tooltip_norm, $tooltip_canc); $win = create_window(); $win->signal_connect('delete_event', sub { $_[0]->destroy }); $win->set_transient_for($window); $vbox = create_main_container($win); $entry_nome = create_entry_box(create_frame($vbox, 'Nome')); $entry_numero = create_entry_box(create_frame($vbox, 'Numero')); $button = Gtk2::Button->new_from_stock('gtk-ok'); $button->signal_connect('clicked', sub { inserisci_numero($entry_nome->get_text, $entry_numero->get_text); $win->destroy; }); $tooltip_norm = Gtk2::Tooltips->new(); $tooltip_norm->set_tip($button, 'Inserisci il numero'); $vbox->pack_start($button, FALSE, TRUE, 0); $canc_button = Gtk2::Button->new_from_stock('gtk-cancel'); $canc_button->signal_connect('clicked', sub { $win->destroy; }); $tooltip_canc = Gtk2::Tooltips->new(); $tooltip_canc->set_tip($canc_button, 'Annulla l\'inserimento'); $vbox->pack_start($canc_button, FALSE, TRUE, 0); $win->show_all; Gtk2->main; } sub dialog_rimuovi_numero { my ($parent, $slist) = @_; my $win; my $ok_button; my $canc_button; my $tooltip_ok; my $tooltip_canc; my $label; my $vbox; my $numref; $numref = get_selected_item($slist); $win = create_window(); $win->signal_connect('delete_event', sub { $_[0]->destroy }); $win->set_transient_for($parent); $vbox = create_main_container($win); $vbox->set_border_width(8); $label = Gtk2::Label->new("\n" . 'Sei sicuro di voler cancellare ' . $numref->[0] . "?\n"); $label->set_justify('center'); $vbox->pack_start($label, TRUE, TRUE, 0); $ok_button = Gtk2::Button->new_from_stock('gtk-ok'); $ok_button->signal_connect('clicked', sub { rimuovi_numero($numref->[0]); $win->destroy; remove_from_slist($slist); }); $tooltip_ok = Gtk2::Tooltips->new(); $tooltip_ok->set_tip($ok_button, 'Rimuovi il numero'); $vbox->pack_start($ok_button, FALSE, TRUE, 0); $canc_button = Gtk2::Button->new_from_stock('gtk-cancel'); $canc_button->signal_connect('clicked', sub { $win->destroy; }); $tooltip_canc = Gtk2::Tooltips->new(); $tooltip_canc->set_tip($canc_button, 'Annulla rimozione'); $vbox->pack_start($canc_button, FALSE, TRUE, 0); $win->show_all; Gtk2->main; } sub remove_from_slist { my $list = shift; my $selected_item; $selected_item = ($list->get_selected_indices())[0]; splice(@{$list->{data}}, $selected_item, 1); TRUE; } sub inserisci_numero { my ($nome, $numero) = @_; my $string = encode('utf-8', $nome . ';' . $numero) . "\n"; open(DB, ">>$db_file"); print DB $string; close(DB); return 1; } sub rimuovi_numero { my $who = shift; my $key; delete($numeri{$who}); open(DB, ">$db_file"); foreach $key (keys %numeri) { print DB encode("utf-8", "$key;$numeri{$key}") . "\n"; } close(DB); } ######################################################################## # GUI principale ######################################################################## sub main_gui { my $vbox_main_container; my $frame_mittente; my $frame_destinatario; my $frame_testo; my $menu; $window = create_window(); $menu = create_menu(); $window->add_accel_group($menu->{accel_group}); $vbox_main_container = create_main_container($window); $vbox_main_container->add($menu->{widget}); # $frame_mittente = create_frame($vbox_main_container, 'Mittente'); $entry_mittente = create_entry_box($frame_mittente); # $frame_destinatario = create_frame($vbox_main_container, 'Destinatario'); $entry_destinatario = create_entry_box($frame_destinatario); # $frame_testo = create_frame($vbox_main_container, 'Testo'); $view = create_text_box($frame_testo); add_send_button($vbox_main_container); $window->show_all; Gtk2->main; } #################################################################### #################################################################### sub main { parse_config(); Getopt::Long::Configure('auto_abbrev'); GetOptions('destinatario=s' => \$dest_number, 'testo=s' => \$sms_text, 'username=s' => \$username, 'password=s' => \$password, 'help' => \$help, 'verbose' => \$verbose, 'mittente=s' => \$sender); help() if ( !($username && $password ) || $help); main_gui(); } main();