callback = $func_name; } function doRequest($method, $url, $vars) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); # curl_setopt($ch,CURLOPT_WRITEFUNCTION, array($this, "write_function")); # curl_setopt($ch,CURLOPT_READFUNCTION, array($this, "read_function")); # curl_setopt($ch,CURLOPT_PROGRESSFUNCTION, array($this, "curl_progress_func")); # curl_setopt($ch, CURLOPT_VERBOSE, true); verbose mode curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt'); curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt'); if ($method == 'POST') { curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $vars); } $data = curl_exec($ch); curl_close($ch); if ($data) { if ($this->callback != null) { $callback = $this->callback; $this->callback = false; return call_user_func($callback, $data); } else { return $data; } } else { return curl_error($ch); } } public function get($url) { return $this->doRequest('GET', $url, 'NULL'); } public function post($url, $vars) { return $this->doRequest('POST', $url, $vars); } public function write_function($ch, $string){ echo "write function\n"; return strlen($string); } public function read_function($ch, $string){ echo "read function\n"; return strlen($string); } public function curl_progress_func($fp, $dltotal, $dlnow, $ultotal, $ulnow){ echo "curl_progress_func()\n"; } } class FreeConsole{ protected $login; protected $passwd; protected $id; protected $idt; protected $curl; # handle IO with curl library protected $error; protected $timeout = 300; # connexion (login) timeout in seconds # protected $timeout = 10; # connexion (login) timeout in seconds protected $keep_alive = true; # public function __construct(){ $this->curl = new Curl(); $this->error = ''; if($this->keep_alive) Gtk::timeout_add(($this->timeout-1)*1000, array($this, 'connexion_keep_alive')); } /** login method - on OK, return true, - on error : return false */ public function login($login=null, $passwd=null){ $login_url = 'http://subscribe.free.fr/login/login.pl'; if($login != null){ $this->login = $login; $this->passwd = $passwd; } $vars = array( "login" => $this->login, "pass" => $this->passwd ); $data = $this->curl->post($login_url, $vars); # Location: http://adsl.free.fr/compte/console.pl?id=XXXX&idt=YYYY if(preg_match('#^Location:.+?http://adsl.free.fr/compte/console.pl\?id=(.+?)&idt=(.+?)$#m', $data, $values)){ $this->id = $values[1]; $this->idt = $values[2]; $this->idt = substr($this->idt, 0, 16); # remove leading chars (\n, \r) return true; } else return false; } public function relogin(){ $this->login(); } # try to see if server is responding at given host adresse public function connexion_check($timeout=5){ $host = 'subscribe.free.fr'; # $url = 'http://subscribe.free.fr/login/'; $status = @fsockopen($host, $port=80, $errno, $errstr, $timeout); if($status === false) return false; else return true; } # return true if has timed-out. protected function response_timeout_check($html){ # Votre session a expir if(preg_match('#Votre session a expir#mi', $html)) return true; return false; } public function connexion_keep_alive(){ if(!$this->connexion_check()) throw new Exception("connexion error - (login)", ERROR_LOGIN); $url = sprintf('http://adsl.free.fr/compte/console.pl?id=%s&idt=%s', $this->id(), $this->idt()); $data = @file_get_contents($url); if($this->response_timeout_check($data)) throw new Exception('connexion timeout ; please relog', ERROR_CONNEXION_TIMEOUT); return true; } public function id(){ return $this->id; } public function idt(){ return $this->idt; } } class FreeFax extends FreeConsole{ protected $error = ''; public function get_error(){ return $this->error; } public function fax_get_captcha($id = null, $idt = null){ if($id == null) $id = $this->id(); if($idt == null) $idt = $this->idt(); # get this URL before reading captcha image $foobar = file_get_contents( sprintf('http://adsl.free.fr/admin/tel/sendfax.pl?id=%s&idt=%s', $id, $idt) ); $file = file_get_contents(sprintf('http://adsl.free.fr/admin/tel/captcha.pl?id_client=%d', $this->id)); return $file; } public function send_fax($dest, $captcha, $file, $masque='N', $id=null, $idt=null){ # suppress dot on phone number $dest = str_replace('.', '', $dest); $url = 'http://adsl.free.fr/admin/tel/send_fax_valid.pl'; if($id == null) $id = $this->id(); if($idt == null) $idt = $this->idt(); $vars = array( "cap" => $captcha, "dest" => $dest, "uploaded_file" => "@$file", "id" => $id, "idt" => $idt, 'masque' => $masque, # 'nom' => 'MQ', # 'email' => 'm.quinton@gmail.com' ); $data = $this->curl->post($url, $vars); # file_put_contents('/tmp/freefax-log.html', $data); /* ok : Votre Fax est en cours d'envoi vers le numéro error : Vous n'aves pas correctement rempli les informations error : Le numéro du destinataire est incorrect 11 */ if($this->response_timeout_check($data)){ $this->error = 'timeout sur connexion ; reconnectez vous.'; return false; } if(preg_match('#Le num.+ro du destinataire est incorrect#m', $data)){ $this->error = 'Numero incorrect'; return false; } if(preg_match("#Vous n'ave.+ pas correctement rempli les informations#m", $data)){ $this->error = 'Code de controle incorrect'; return false; } if(!preg_match('#Votre Fax est en cours d\'envoi#m', $data)){ $this->error = 'erreur inconnue'; return false; } else{ $this->error = ''; return true; } } public function fax_params_get(){ $nom = $email = $msque = ''; $url = sprintf('http://adsl.free.fr/admin/tel/chfax_01.pl??id=%s&idt=%s', $this->id(), $this->idt()); $html = file_get_contents($url); # if(preg_match('# # if(preg_match('##mi', $html, $values)){ $masque = 1; } else $masque = 0; # if(preg_match('# $nom, 'email' => $email, 'masque' => $masque ); } public function fax_params_set($nom, $email, $masque){ $url = sprintf('http://adsl.free.fr/admin/tel/chfax_01.pl??id=%s&idt=%s', $this->id(), $this->idt()); # post -> /admin/tel/chfax_02.pl?id=208142&idt=803eef8b8ae9c912 # vars : nom, email, masque (0,1) $url = sprintf('http://adsl.free.fr/admin/tel/chfax_02.pl?id=%s&idt=%s', $this->id(), $this->idt()); $vars = array( 'nom' => $nom, 'masque' => $masque, 'email' => $email ); $data = $this->curl->post($url, $vars); # file_put_contents('/tmp/freefax-params-set-log.html', $data); } } define('PROG_NAME', 'freeFAX'); class Config { public $login = ''; public $pass = ''; } class Core extends GtkWindow{ protected function process_all_events(){ while(Gtk::events_pending()) {Gtk::main_iteration();} } protected function dialog_warn($string){ $dialog = new GtkMessageDialog( null,//parent 0, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, $string ); $answer = $dialog->run(); $dialog->destroy(); } } class GladeUI extends Core{ protected $glade_file; protected $glade_main; protected $glade_xml; public function __construct() { parent::__construct(); # $this->glade_file = PROG_NAME . '.glade'; # $this->glade_main = 'main'; # main widget (root) $this->buildGUI(); $this->add($this->widget_get($this->glade_main)); $this->set_position(Gtk::WIN_POS_CENTER); } protected function buildGUI(){ if(file_exists($this->glade_file)){ $this->glade_xml = $this->glade_xml = new GladeXml($this->glade_file, $this->glade_main); return true; } else throw new Exception ("can't file glade file {$this->glade_file}"); } protected function widget_get($name, $xml = null){ if($xml == null) $xml = $this->glade_xml; $widget = $xml->get_widget($name); if($widget == null) throw new Exception ("can't find widget named '$name' in glade file {$this->glade_file}"); return $widget; } } class FileSelectionDialog extends GtkFileSelection{ protected $attached_widget = null; public function __construct($title){ parent::__construct($title); $this->ok_button->connect_simple( 'clicked' , array($this, 'activate')); $this->cancel_button->connect_simple( 'clicked' , array($this, 'cancel')); } public function cancel(){ $this->hide_all(); } public function activate(){ $this->hide_all(); } public function attach_widget($widget){ $this->attached_widget = $widget; } public function run(){ if($this->attached_widget != null){ $this->set_filename($this->attached_widget->get_text()); } parent::run(); if($this->attached_widget != null){ $this->attached_widget->set_text($this->get_filename()); $this->attached_widget->set_position(-1); } } } class ConfigUI extends GladeUI { protected $login; protected $pass; protected $freeFAX; public function __construct($freeFAX) { $this->glade_file = PROG_NAME . '.glade'; $this->glade_main = 'main'; # main widget (root) parent::__construct(); $this->freeFAX = $freeFAX; $this->set_title('Parametres'); $this->setup(); } protected function buildUI(){ parent::buildUI(); } protected function setup(){ $this->widget_get('title')->modify_font(new PangoFontDescription("Aria Bold 16")); $this->widget_get('button_cancel')->connect_simple('clicked', array($this, 'on_button_cancel')); $this->widget_get('button_ok')->connect_simple('clicked', array($this, 'on_button_ok')); $this->widget_get('button_test')->connect_simple('clicked', array($this, 'on_button_test')); $this->config_load(); $this->widget_get('entry_login')->set_text($this->config->login); $this->widget_get('entry_pass')->set_text($this->config->pass); # FAX infos $this->fax_config_load(); } public function login(){ return $this->widget_get('entry_login')->get_text(); } public function pass(){ return $this->widget_get('entry_pass')->get_text(); } public function on_button_cancel(){ $this->hide_all(); } public function on_button_ok(){ $this->config_save(); $this->fax_config_save(); $this->hide_all(); } public function on_button_test(){ $console = new FreeConsole(); $status = $console->login($this->login(), $this->pass()); if($status) $string = "connexion reussie"; else $string = "connexion non reussie"; $this->dialog_warn($string); } protected function fax_config_load(){ $params = $this->freeFAX->fax_params_get(); $this->widget_get('entry_email')->set_text($params['email']); $this->widget_get('entry_nom')->set_text($params['nom']); $this->widget_get('checkbutton_masquer')->set_active($params['masque']); } protected function fax_config_save(){ $email = trim($this->widget_get('entry_email')->get_text()); $nom = trim($this->widget_get('entry_nom')->get_text()); $masque = $this->widget_get('checkbutton_masquer')->get_active(); if($email != ''){ $this->freeFAX->fax_params_set($nom , $email, $masque); } } protected function config_load(){ $dir = sprintf('%s/.freeFAX', getenv('HOME')); if(!is_dir($dir)) mkdir($dir); $config_file = sprintf('%s/.freeFAX/config', getenv('HOME')); if(file_exists($config_file)){ $this->config = unserialize(file_get_contents($config_file)); } else $this->config = new Config(); } protected function config_save(){ $dir = sprintf('%s/.freeFAX', getenv('HOME')); $config_file = $dir . '/config'; if(!is_dir($dir)) mkdir($dir); if(file_exists($config_file)) unlink($config_file); $config = new Config; $config->login = $this->login(); $config->pass = $this->pass(); file_put_contents($config_file, serialize($config)); } public function connect_simple($signal, $callback){ if($signal == 'clicked') $this->widget_get('button_ok')->connect_simple_after('clicked', $callback); else parent::connect_simple($signal, $callback); } } class LedStatus extends GtkImage{ protected $file; protected $color; protected $blink_timeout = 150; protected $blinking; protected $blinking_current; protected $color1; protected $color2; public function __construct($color=null){ parent::__construct(); $this->blinking = false; } public function set_color($color){ $file = $color . 'led.png'; if(file_exists($file)) $this->set_from_file($file); $this->blinking = false; $this->process_all_events(); } public function set_blinking_colors($color1, $color2){ $file1 = $color1 . 'led.png'; $file2 = $color2 . 'led.png'; $this->set_from_file($file1); $this->process_all_events(); if(file_exists($file1) && file_exists($file2)){ $this->blinking = true; $this->color1 = $file1; $this->color2 = $file2; Gtk::timeout_add($this->blink_timeout, array($this, 'do_blink')); } } public function do_blink(){ static $current_color = null; if(!$this->blinking) return false; if($current_color == $this->color1) $current_color = $this->color2; else $current_color = $this->color1; $this->set_from_file($current_color); $this->process_all_events(); return true; } protected function process_all_events(){ while(Gtk::events_pending()) {Gtk::main_iteration();} } } class PhoneBookUI extends GladeUI { protected $phonebook; # phonebook database protected $treeview; protected $model; # treeview model protected $selection; # treeview selection protected $_name; protected $_tel; protected $_id; protected $_iter; protected $attached_widget = null; public function __construct() { $this->glade_file = 'phonebook.glade'; $this->glade_main = 'main'; # main widget (root) parent::__construct(); $file = sprintf('%s/.freeFAX/phonebook.sqlite', getenv('HOME')); $this->phonebook = new Phonebook($file); $this->setup(); $this->populate_treeview(); # populate treeview from database ($this->phonebook db). $this->set_size_request(600, 400); } protected function setup(){ $this->set_title(PROG_NAME); $this->widget_get('title')->modify_font(new PangoFontDescription("Aria Bold 26")); $this->connect_simple('destroy', array($this, 'on_button_close')); $this->widget_get('button_close')->connect_simple('clicked', array($this, 'on_button_close')); $this->widget_get('button_select')->connect_simple('clicked', array($this, 'on_button_select')); $this->widget_get('button_insert')->connect_simple('clicked', array($this, 'on_button_insert')); $this->widget_get('button_update')->connect_simple('clicked', array($this, 'on_button_update')); $this->widget_get('button_delete')->connect_simple('clicked', array($this, 'on_button_delete')); $this->widget_get('button_filter')->connect_simple('clicked', array($this, 'on_button_filter')); $this->treeview = $this->widget_get('treeview_phonebook'); $this->_name = $this->widget_get('entry_name'); $this->_tel = $this->widget_get('entry_tel'); $this->model = new GtkListStore(Gtk::TYPE_STRING, Gtk::TYPE_STRING, Gtk::TYPE_STRING); $this->treeview->set_model($this->model); $this->selection = $this->treeview->get_selection(); $this->selection->connect_simple('changed', array($this, 'on_selection_changed')); $this->treeview->connect_simple('row-activated', array($this, 'on_treeview_row_activated')); } protected function populate_treeview(){ # ID - hidden # $cell_renderer = new GtkCellRendererText(); # $column = new GtkTreeViewColumn('ID', $cell_renderer, 'text', 0); # $this->treeview->append_column($column); # Name $cell_renderer = new GtkCellRendererText(); $column = new GtkTreeViewColumn('Name', $cell_renderer, 'text', 1); $this->treeview->append_column($column); # Name $cell_renderer = new GtkCellRendererText(); $column = new GtkTreeViewColumn('Tel', $cell_renderer, 'text', 2); $this->treeview->append_column($column); $list = $this->phonebook->get_all(); foreach($list as $rec){ $this->model_append(array($rec['id'], $rec['name'], $rec['tel'])); } } public function model_append($list){ $this->model->append($list); } # activated on double-click or return on treeview public function on_treeview_row_activated(){ $this->on_button_select(); } public function on_selection_changed(){ list($model, $iter) = $this->selection->get_selected(); if($iter == null){ $this->_tel->set_text(''); $this->_name->set_text(''); return; } $this->_name->set_text($model->get_value($iter, 1)); $this->_tel->set_text($model->get_value($iter, 2)); $this->_iter = $iter; $this->_id = $model->get_value($iter, 0); } public function on_button_close(){ $this->hide_all(); } public function on_button_insert(){ $name = $this->_name->get_text(); $tel = $this->_tel->get_text(); $id = $this->phonebook->insert($name, $tel); $this->model_append(array($id, $name, $tel)); } public function on_button_update(){ $name = $this->_name->get_text(); $tel = $this->_tel->get_text(); $this->phonebook->update($this->_id, $name, $tel); $this->model->set($this->_iter, 0, $this->_id, 1, $name, 2, $tel); } public function on_button_delete(){ list($model, $iter) = $this->selection->get_selected(); $id = $model->get_value($iter, 0); $this->phonebook->delete_id($id); $this->model->remove($iter); } public function on_button_filter(){ } public function on_button_select(){ if($this->attached_widget != null){ $this->attached_widget->set_text($this->_tel->get_text()); } $this->hide_all(); } public function attach_widget($w){ $this->attached_widget = $w; } } class App extends GladeUI { protected $freeFAX; protected $captcha_file; protected $captcha_img; protected $phonebook; protected $connexion_status_led; public function __construct() { $this->glade_file = PROG_NAME . '.glade'; $this->glade_main = 'vbox_main'; # main widget (root) parent::__construct(); $this->freeFAX = new FreeFAX(); $this->setup(); } # this method handles Gtk main loop and connexion exceptions. public function run(){ $this->quit = false; while(! $this->quit){ try{ Gtk::main(); } catch(Exception $e){ # echo "exception : "; print_r($e); switch($e->getCode()){ case ERROR_CONNEXION_TIMEOUT: $this->status('connexion timeout, tentative de reconnexion'); $this->freeFAX->relogin(); break; case ERROR_CONNEXION: $this->status('probleme de connexion reseau ; rechargez dans quelques minutes'); break; } } } } public function main_quit(){ $this->quit = true; Gtk::main_quit(); } protected function setup(){ $this->set_title(PROG_NAME); $this->connect_simple('destroy', array($this, 'main_quit')); $this->widget_get('button_quit')->connect_simple('clicked', array($this, 'main_quit')); $this->widget_get('button_envoyer')->connect_simple('clicked', array($this, 'fax_send')); $this->widget_get('button_parametres')->connect_simple('clicked', array($this, 'on_button_parametres')); $event_box = new GtkEventBox(); # $this->widget_get('frame_control')->add($event_box); $this->widget_get('hbox_captcha')->add($event_box); $this->captcha_img = new GtkImage(); $event_box->add($this->captcha_img); $this->widget_get('button_reload')->connect_simple('clicked', array($this, 'connect_and_load_captcha')); $img = GtkImage::new_from_stock(Gtk::STOCK_REFRESH, Gtk::ICON_SIZE_SMALL_TOOLBAR); $this->widget_get('button_reload')->set_image($img); # add button clic event to reload chaptcha file as needed, when captcha is unreadable $event_box->add_events(Gdk::BUTTON_PRESS_MASK); $event_box->connect('button-press-event', array($this, 'connect_and_load_captcha')); $this->widget_get('frame_control')->show_all(); $this->statusbar = $this->widget_get('statusbar'); $this->widget_get('title')->modify_font(new PangoFontDescription("Aria Bold 26")); $this->widget_get('title')->set_label('freeFAX'); $this->widget_get('button_file_select')->set_label('...'); $this->widget_get('button_file_select')->connect_simple('clicked', array($this, 'on_file_select')); $this->widget_get('button_select_phone')->connect_simple('clicked', array($this, 'on_button_select_phone')); $this->phonebook = new PhoneBookUI(); $this->phonebook->attach_widget($this->widget_get('entry_dest')); # attach select button to this entry $this->connexion_status_led = new LedStatus(); $this->widget_get('eventbox_status')->add($this->connexion_status_led); $this->status_img_set_color('blink'); $this->widget_get('hbox_status')->show_all(); $this->widget_get('eventbox_status')->add_events(Gdk::BUTTON_PRESS_MASK); $this->widget_get('eventbox_status')->connect('button-press-event', array($this, 'connect_and_load_captcha')); # contruire completement l'interface avant d'ouvrir une connexion qui est bloquante # on va donc le faire apres un certain delai ; la main_loop s'executera entre temps. Gtk::timeout_add(100,array($this,'connect_and_load_captcha')); } public function connect_and_load_captcha(){ $this->status_img_set_color('blink'); if(!$this->config_load()) $this->on_button_parametres(); # on s'assure que l'interface est completement finalisée avant de continuer $this->process_all_events(); try { if(!$this->freeFAX->connexion_check()){ $this->status('erreur de connexion au serveur'); $this->dialog_warn('erreur de connexion au serveur'); $this->status_img_set_color('red'); } if($this->config->login != ''){ $this->status('connecting ...'); if($this->freeFAX->login($this->config->login, $this->config->pass)) $this->status(sprintf('connection done %s %s', $this->freeFAX->id(), $this->freeFAX->idt())); else throw new Exception("erreur de connexion - (login)", ERROR_LOGIN); $this->load_captcha(); $params = $this->freeFAX->fax_params_get(); $this->widget_get('checkbutton_masquer')->set_active($params['masque']); $this->status_img_set_color('green'); } } catch (Exception $e){ if($e->getCode() == ERROR_LOGIN){ $this->status('erreur de connexion ; veuillez verifier vos parametres'); $this->status_img_set_color('red'); } } } protected function load_captcha(){ $this->status_img_set_color('blink'); $this->status('lecture du code de controle (captcha)'); $img_data = $this->freeFAX->fax_get_captcha(); # need to write to a temp file for image reading ; no direct mapping to php-gtk img (need bundled php-gd) $this->captcha_file = tempnam("/tmp", "freefax-captcha-"); file_put_contents($this->captcha_file, $img_data); $this->status('affichage du code de controle'); if(!file_exists($this->captcha_file)) throw new Exception ("captcha file not found {$this->captcha_file}"); $this->captcha_img->set_from_file($this->captcha_file); unlink($this->captcha_file); $this->widget_get('entry_captcha')->set_text(''); $this->status('pret pour envoyer FAX'); $this->status_img_set_color('green'); } public function fax_send(){ $this->status_img_set_color('blink'); $dest = $this->widget_get('entry_dest')->get_text(); $file = $this->widget_get('entry_file')->get_text(); $captcha = $this->widget_get('entry_captcha')->get_text(); $masquer = $this->widget_get('checkbutton_masquer')->get_active()?'1':'0'; $this->status("fax en cours d'envoi $dest, $captcha, $file"); if($this->freeFAX->send_fax($dest, $captcha, $file, $masquer)){ $this->status('fax envoyé'); $this->status_img_set_color('green'); } else { $msg = 'fax non envoye : ' . $this->freeFAX->get_error(); $this->status($msg); $this->dialog_warn($msg); $this->status_img_set_color('red'); } # reload captcha $this->load_captcha(); } public function on_file_select(){ $dialog = new FileSelectionDialog('Selection fichier'); $dialog->attach_widget($this->widget_get('entry_file')); $response = $dialog->run(); } public function on_button_parametres(){ static $config = null; if($config == null){ $this->status_img_set_color('blink'); $config = new ConfigUI($this->freeFAX); $config->connect_simple('clicked', array($this, 'on_parametres_button_ok')); } $this->status_img_set_color('green'); $config->show_all(); } public function on_parametres_button_ok(){ $this->config_load(); $this->connect_and_load_captcha(); } public function on_button_select_phone(){ $this->phonebook->show_all(); } protected function status_img_set_color($color){ if($color == 'blink') $this->connexion_status_led->set_blinking_colors('yellow', 'red'); else $this->connexion_status_led->set_color($color); } protected function status($str){ static $context_id = null; if($context_id == null){ $context_id = $this->statusbar->get_context_id('trace-message'); } $this->statusbar->push($context_id, $str); $this->process_all_events(); } protected function config_load(){ $dir = sprintf('%s/.freeFAX', getenv('HOME')); if(!is_dir($dir)) mkdir($dir); $config_file = $dir . '/config'; if(file_exists($config_file)){ $this->config = unserialize(file_get_contents($config_file)); return true; } else{ $this->config = new Config(); return false; } } } $app = new App; $app->show(); $app->run(); ?>