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();
?>