Category Archives: code

Δήλωση

experiment on natural language forms tweaked

Χρειάστηκε πριν λίγες μέρες να συμπληρώσω κάποιες αιτήσεις με λίγα στοιχεία αλλά αρκετή χαρτούρα. Τα ίδια πράγματα να συμπληρώνονται 32 φορές σε 256 διαφορετικά έντυπα με 1024 διαφορετικούς τρόπους. Ο παράδεισος του χαρτογιακά και η κόλαση οποιουδήποτε άλλου.

Πριν ξεκινήσω ανοιχτό πόλεμο με το χαρτοβασίλειο είπα να δοκιμάσω αρχικά  να φτιάξω μια απλή φόρμα που να δημιουργεί ένα άλλο συμπληρωμένο έντυπο. (το οποίο είναι λιγότερο σημαντικό και ως τέτοιο ο καθένας βαριέται να το συμπληρώσει μέχρι να έρθει η ώρα που κάποιος θα το ζητήσει. Αυτή την ώρα θα ψάχνεις να βρεις κάποιο παλιό έγγραφο που είχε συμπληρώσει κάποιος άλλος, να αλλάξεις τα 2 ή 3 σημεία που θέλεις και να το εκτυπώσεις για να το δώσεις.

Ξεκινώντας να φτιάξω αυτή τη φόρμα θυμήθηκα ότι είχα δει μια καλή ιδέα στο codrops για φόρμες σε φυσική γλώσσα.

Πήρα τον κώδικα από το παράδειγμα, άλλαξα το κείμενο, ετοίμασα το template για να δημιουργείται το έγγραφο σε word document μορφή συμπληρωμένο με τα στοιχεία και πέρα από μερικά προβλήματα στην ονοματοδοσία του αρχείου ήταν σχεδόν έτοιμο. Το μόνο που χρειαζόταν ακόμα ρύθμιση ήταν δύο select elements που έπρεπε να συνδεθούν, ώστε όταν αλλάζει η επιλογή στο ένα να αλλάζει και το άλλο αντίστοιχα.

Ο κώδικας που υπήρχε στο codrops δεν το υποστήριζε (προφανώς επειδή δεν χρειαζόταν αφού το συγκεκριμένο παράδειγμα αφορά κυρίως styling του UI και όχι λειτουργικές απαιτήσεις), οπότε ήταν απαραίτητο να γίνουν κάποιες ρυθμίσεις.

Πρώτα απ’ όλα πρόσθεσα το id attribute στα selects, ώστε να μπορώ να ταυτοποιήσω τα πεδία που θέλω να συνδέσω. Μετά χρειαζόμουν κάτι, να κρατάει με ποιο άλλο πεδίο γίνεται η σύνδεση. Το πιο απλό ήταν να μπει ένα custom attribute, οπότε έβαλα το link-id για να δείχνει (κάπως) ότι θα κρατά το id κάποιου άλλου συνδεδεμένου πεδίου.

Οπότε ο κώδικας

<select name="starttime">
    <option value="07:00">07:00</option>
    <option value="07:30" selected="selected">07:30</option>
    ...

έγινε

<select id="starttime" name="starttime" link-id="leavetime">
    <option value="07:00">07:00</option>
    <option value="07:30" selected="selected">07:30</option>
    ...

Οι επόμενες αλλαγές πλέον αφούν τον javascript κώδικα στο αρχείο nlform.js ώστε να αξιοποιήσει αυτά τα attributes που προστέθηκαν.

Η πρώτη αλλαγή αφορά τη δημιουργία των custom dropdown elements. Εκεί, προστέθηκε μια κλήση στην _createItemExtended().

_createDropDown : function() {
    ...
    this.elOriginal.parentNode.insertBefore( this.fld, this.elOriginal );
    this.elOriginal.style.display = 'none';

    //Added call to _createItemExtended();
    this._createItemExtended();
},

Η δεύτερη αλλαγή αφορά την ενημέρωση των πεδίων όταν κλείνουν τα custom πεδία αφού έχουν συμπληρωθεί. Εδώ μας ενδιαφέρουν και πάλι μόνο τα dropdowns και όχι τα inputs, οπότε προστέθηκε μια κλήση στην _updateLinkedItem() στο σημείο που κλείνουν τα dropdowns.

close : function( opt, idx ) {
    ...
    if( this.type === 'dropdown' ) {
        if( opt ) {
            ...
            this.selectedIdx = idx;
            // update original select element´s value
            this.elOriginal.value = this.elOriginal.children[ this.selectedIdx ].value;

            //Added call to _updateLinkedItem();
            this._updateLinkedItem();
        }
    }
    else if( this.type === 'input' ) {
    ...
    }
},

Ο λόγος που δημιουργήθηκαν νέες functions αντί να μπεί χύμα ο κώδικας είναι για να είναι πιο καθαρές οι αλλαγές.

Το μόνο που μένει πλέον είναι να συμπληρωθούν αυτές οι νέες functions που κάνουν τη “χαμαλοδουλειά”.

_createItemExtended : function () {
    if (this.elOriginal.attributes.hasOwnProperty('id')) {
        this.toggle.id = this.elOriginal.id;
        this.elOriginal.attributes.removeNamedItem('id');
    }
},
_updateLinkedItem : function() {
    if (this.elOriginal.attributes.hasOwnProperty('link-id')) {
        var linkId = this.elOriginal.attributes['link-id'].value;
        var len = this.form.fields.length;
        for (var i = 0; i<len; i++) {
            if (this.form.fields[i].toggle.id == linkId) {
                this._selectIndex( this.form.fields[i], this.selectedIdx );
                break;
            }
        }
    }
},
_selectIndex : function( opt, idx ) {
 opt.optionsList.children[ opt.selectedIdx ].className = '';
 opt.optionsList.children[ idx ].className = 'nl-dd-checked';
 opt.toggle.innerHTML = opt.optionsList.children[ idx ].innerHTML;
 opt.selectedIdx = idx;
 opt.elOriginal.value = opt.elOriginal.children[ opt.selectedIdx ].value;
}

Το εκνευριστικό (ναι, δεν έχω συνηθίσει ακόμα την αρρώστια να κάνει ο κάθε browser και η κάθε έκδοση τα δικά του) είναι ότι ενώ ο chrome και ο firefox δούλευαν σωστά, ο IE στα συνδεδεμένα dropdowns έχανε το name attribute, οπότε οι τιμές των αντίστοιχων πεδίων ποτέ δεν γίνονταν post στη σελίδα και το έγγραφο που συμπληρώνονταν από το template ήταν ημιτελές. Αν και η αρχική μου σκέψη ήταν “δεν &$#*&%@ πειράζει, θα χρησιμοποιώ chrome ή firefox”, τελικά ένας συνάδελφος που χρησιμοποιεί κατά κύριο λόγο IE με έπεισε να του δώσω μια ακόμα ευκαιρία.

Μετά από λίγο debugging το εύρημα ήταν το εξής:

Οταν σε internet explorer (σε IE11 που έχω) αφαιρείς με κώδικα το id attribute αυτόματα χάνεται και η τιμή που υπάρχει στο name attribute.

Ωραία πράγματα τα features, προσθέτουν ενδιαφέρον στην ζωή μας που θα ήταν ανιαρή αν τα πάντα δούλευαν χωρίς αυτές τις &$#*&%@*#% χαρούμενες νότες έκπληξης. Οπότε έμενε να κρατήσουμε την τιμή του name και αφού αφαιρέσουμε το id να το ξαναβάλουμε. Άλλαξα τον κώδικα για μια ακόμα φορά ώστε να καταλήξω σε αυτό:

_createItemExtended : function () {
    if (this.elOriginal.attributes.hasOwnProperty('id')) {
        this.toggle.id = this.elOriginal.id;
        var name = this.elOriginal.name;
        this.elOriginal.attributes.removeNamedItem('id');
        this.elOriginal.name = name;
    }
},

και τώρα τα πράγματα δουλεύουν όπως πρέπει.
Τουλάχιστον μέχρι αποδείξεως του εναντίου. 🙁

Οπότε, Let the war begin!

download αρχείων με ελληνικά ονόματα

Ήθελα πριν λίγο καιρό να ξεκινήσω ένα μικρό template για να συμπληρώνω κάποια σημεία και να δημιουργείται αυτόματα ένα word έγγραφο, ώστε να μπορώ στη συνέχεια να το τυπώσω όπου θέλω, να συνεχίσω να το επεξεργάζομαι και να το μορφοποιώ. Ξεκίνησα σαν χαρούμενος πληροφορικάριος και κλίκι-κλίκι, άρχισα να γράφω το template το έβαλα σε ένα χώρο, έφτιαξα και μία φόρμα στο web για να συμπληρώνεις τα 2-3 πραγματάκια που χρειάζονται και το τελικό συμπληρωμένο αρχείο το έστελνα για download. Tο δοκίμασα στον chrome που χρησιμοποιώ κυρίως και όλα ήταν μια χαρά. Ευτυχής είπα να το δοκιμάσω και σε Firefox και σε Internet Explorer. Εκεί ήταν που τελείωσε (τόσο σύντομα) το αίσθημα ευτυχίας και τη θέση του πήραν διάφορα κοσμητικά επίθετα. Ο Firefox αποθήκευε μεν το αρχείο με ελληνικά, αλλά δεν έπαιρνε όλο το όνομα, παρά μόνο ότι υπήρχε μέχρι το πρώτο κενό. Ο Internet Explorer έπαιρνε μεν όλο το όνομα, αλλά δεν διαβαζόταν γιατί τα ελληνικά είχαν χαλάσει γιατί τροποποιούσε το όνομα του αρχείου από utf8 encoding που ήταν σε ότι encoding ήθελε. Τελικά με λίγο ψάξιμο κατέληξα στο συμπέρασμα ότι ο κάθε browser έχει τα δικά του βίτσια όχι μόνο ως προς την κατανόηση, τη μορφοποίηση και την εμφάνιση του css, του html και του javascript κώδικα, αλλά ακόμα και στον τρόπο που πρέπει να δίνεις τα ονόματα των αρχείων που θέλεις να στείλεις σαν attachments για download. O κώδικας τελικά από αυτό (σε aspx):

void WriteFile(string filename, string data)
{
    this.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8";
    this.Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
    this.Response.Write(data);
}

και αυτό (σε php):

function writeFile($filename, $data)
{
    header('content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8');
    header("Content-Disposition: attachment; filename=$filename;");
    echo $data;
}

Έγινε αυτό:

void WriteFile(string filename, string data)
{
    this.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8";

    if (this.Request.Browser.Browser == "Firefox")
    {
        filename = filename.Replace(" ","%20");
        this.Response.AddHeader("Content-Disposition", "attachment; filename*=UTF8''" + filename);
    }
    else
    {
        filename = HttpUtility.UrlEncode(filename).Replace('+', ' ');
        this.Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
    }

    this.Response.Write(data);
}

και αυτό αντίστοιχα:

function writeFile($filename, $data)
{
    header('content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8');

    $agent = $_SERVER['HTTP_USER_AGENT'];
    if (strlen(strstr(strtolower($agent), 'firefox')) > 0) {
        $filename = str_replace(' ','%20',$filename);
        header("Content-Disposition: attachment; filename*=UTF8''$filename;");
    }
    else {
        $filename = str_replace('+',' ',urlencode($filename));
        header("Content-Disposition: attachment; filename=$filename;");
    }
    echo $data;
}

Με μερικές δοκιμές, μια λίγο πιο απλοποιημένη μορφή θα μπορούσε να είναι:

void WriteFile(string filename, string data)
{
    if (this.Request.Browser.Browser == "InternetExplorer")
    {
        filename = HttpUtility.UrlEncode(filename).Replace('+', ' ');
    }
    this.Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8";
    this.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filename + "\";");
    this.Response.Write(data);
}

και

function writeFile($filename, $data)
{
    header('content-type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=UTF-8');
    $agent = $_SERVER['HTTP_USER_AGENT'];
    if (strpos(strtolower($agent),'firefox') == false) {
        $filename = str_replace('+',' ',urlencode($filename));
}
header("Content-Disposition: attachment; filename=\"$filename\";");
echo $data;
}

Ώστε να στέλνει διαφορετικό header – ή διαφορετικά κωδικοποιημένο το όνομα – ανάλογα τον browser, σύμφωνα με τα ιδιαίτερα γούστα τους. Αυτό φυσικά δεν σημαίνει ότι άλλες εκδόσεις των browsers ή “περίεργοι” χαρακτήρες στα ονόματα δεν θέλουν περισσότερη εξειδικευμένη αντιμετώπιση. Όμως δεν έχω browsers – ούτε όρεξη προς το παρόν – να το ψάξω… Ίσως κάποια στιγμή αργότερα.

Δείτε το demo.

mysql connect error

Τι σπαστικό πράγμα αυτό, να στήνεις ολόκληρο virtual machine με linux (centos 6.3), apache και php, αλλά τελικά να μην μπορεί να συνδεθεί στη mysql (που τρέχει σε διαφορετικό server σε διαφορετικό δίκτυο) από τον apache και τις ιστοσελίδες τελικά, ενώ ο ίδιος κώδικας όταν τον τρέχεις από το command line με php-cgi να τρέχει κανονικά…

Η μεγαλύτερη αρρώστια είναι ότι σε άλλα linux μηχανήματα και σε άλλα windows μηχανήματα η σύνδεση γίνεται κανονικά, αλλά στο μηχάνημα που θέλεις, επιμένει σαν πεισματάρικο μουλάρι να μην τρέχει. Σου πετάει φαρδύ πλατύ στη μούρη ένα

SQLSTATE[HY000] [2003] Can’t connect to MySQL server on ‘xxx.xxx.xxx.xxx’

Δεν λέω καλό το linux και το open source, αλλά έχει από διανομή σε διανομή και μερικά κολλήματα πολύ άγρια που σου κάνουν τα νεύρα κουρέλια. Μετά από αρκετά μπινελίκια δοκιμές και ψάξιμο του selinux, firewall, permissions, iptables, και ότι μπορούσε να μου περάσει από το μυαλό… …τελικά η μαγική εντολή ήταν η ακόλουθη (από root user πάντα):

[root@server ~]# setsebool -P httpd_can_network_connect=1

Τέλος καλό και… μακρυά και αγαπημένοι.

Ημέρες – Μήνες – Έτη (php)

Λίγος κώδικας σε PHP που επιστρέφει τη διαφορά δύο ημερομηνιών σε Ημέρες – Μήνες – Έτη, αφού οι DateTime::Diff() και date_diff() που παρέχει η PHP τα κάνουν μαντάρα.
Για συντομία και καθαρότητα του κώδικα δεν μπήκαν έλεγχοι με ποια σειρά έχουν δοθεί οι ημερομηνίες, οπότε περιμένει να είναι πρώτα η παλαιότερη και μετά η μεταγενέστερη.

function dmydiff($dateS, $dateE)
{
    $dS = getdate($dateS);
    $dE = getdate($dateE);

    $DMY = array('Y'=>0, 'M'=>0, 'D'=>0);

    $DMY['Y'] = $dE['year'] - $dS['year'];
    $DMY['M'] = $dE['mon'] - $dS['mon'];
    $DMY['D'] = $dE['mday'] - $dS['mday'];

    if ($DMY['D'] < 0)
    {
        $dT = mktime(0,0,0,$dS['mon']+1,1,$dS['year']);
        $dT = intval(($dT - $dateS) / (24*60*60));
        $DMY['D'] =  $dT + $dE['mday'] - 1;
        $DMY['M']--;
    }
    if ($DMY['M'] < 0)
    {
        $DMY['Y']--;
        $DMY['M'] += 12;
    }

    return $DMY;
}

Ο δοκιμαστικός κώδικας που φαίνεται παρακάτω βγάζει τα εξής αποτελέσματα.

Start Date: 31/01/2012
End Date  : 20/03/2013

Using DateTime::Diff() or date_diff() functions
Years  : 1
Months : 1
Days   : 17

Using custom PHP Source code
Years  : 1
Months : 1
Days   : 20

Και τέλος ο κώδικας…

<?php
header('Content-type: text/plain');
date_default_timezone_set('Europe/Athens');

//----------------------------------------------------------------------------
$dtStart = mktime(0,0,0,1,31,2012); //31/01/2012
$dtStop = mktime(0,0,0,3,20,2013);  //20/03/2013
$daySecs = 24 * 60 * 60;

echo PHP_EOL;
echo 'Start Date: ',strftime('%d/%m/%Y',$dtStart),PHP_EOL;
echo 'End Date : ',strftime('%d/%m/%Y',$dtStop),PHP_EOL,PHP_EOL;

//----------------------------------------------------------------------------
echo 'Using DateTime::Diff() or date_diff() functions',PHP_EOL;
$dtStart = date_date_set(date_create(),2012,1,31); //31/01/2012
$dtStop = date_date_set(date_create(),2013,3,20); //20/03/2013
$diff = date_diff($dtStart,$dtStop);
echo 'Years : ',$diff->y,PHP_EOL,
'Months : ',$diff->m,PHP_EOL,
'Days : ',$diff->d,PHP_EOL,PHP_EOL;
unset($diff);

//----------------------------------------------------------------------------
echo 'Using custom PHP Source code',PHP_EOL;
function dmydiff($dateS, $dateE)
{
    $dS = getdate($dateS);
    $dE = getdate($dateE);

    $DMY = array('Y'=>0, 'M'=>0, 'D'=>0);

    $DMY['Y'] = $dE['year'] - $dS['year'];
    $DMY['M'] = $dE['mon'] - $dS['mon'];
    $DMY['D'] = $dE['mday'] - $dS['mday'];

    if ($DMY['D'] < 0)
    {
        $dT = mktime(0,0,0,$dS['mon']+1,1,$dS['year']);
        $dT = intval(($dT - $dateS) / (24*60*60));
        $DMY['D'] =  $dT + $dE['mday'] - 1;
        $DMY['M']--;
    }
    if ($DMY['M'] < 0)
    {
        $DMY['Y']--;
        $DMY['M'] += 12;
    }

    return $DMY;
}

$dtStart = mktime(0,0,0,1,31,2012); //31/01/2012
$dtStop = mktime(0,0,0,3,20,2013);  //20/03/2013
$diff = dmyDiff($dtStart, $dtStop);
echo 'Years : ',$diff['Y'],PHP_EOL,
'Months : ',$diff['M'],PHP_EOL,
'Days : ',$diff['D'],PHP_EOL,PHP_EOL;
unset($diff);
//----------------------------------------------------------------------------

?>

Ελεγχος ΑΔΑ

<?php

    if (!isset($_GET['ada']))
    {
        header("HTTP/1.0 205 Reset Content", true, 205);
        header('Status: Reset Content', false, 205);
        exit;
    }

    $ada = $_GET['ada'];
    $ada = htmlspecialchars($ada);

    function getWebResponse($url)
    {
       $ch = curl_init();
       curl_setopt($ch, CURLOPT_URL,$url);
       curl_setopt($ch, CURLOPT_HEADER, false);
       curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
       $str = curl_exec($ch);
       curl_close($ch);

       return $str;
    }

    $response = getWebResponse(sprintf('http://opendata.diavgeia.gov.gr/api/decisions?ada=%s&output=json',$ada));
    $json = json_decode($response);

    $timeZone = 'Europe/Athens';
    date_default_timezone_set($timeZone);

    $senddate = date('d/m/Y H:i:s',$json->model->decisions[0]->submissionTimestamp / 1000);
    $docdate = date('d/m/Y', $json->model->decisions[0]->submissionTimestamp / 1000);
    $corrected = $json->model->decisions[0]->metadata->isCorrectedByAda;
    $correction = $json->model->decisions[0]->metadata->isCorrectionOfAda;
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="content-language" content="el">
<title>Έλεγχος ΑΔΑ</title>
</head>
<body>
<?php
    if ($json->model->queryInfo->total == 0)
    {
        echo 'Ο ΑΔΑ: ',$ada,' δεν βρέθηκε.';
    }
    else
    {
?>
<p>ΑΔΑ: <?php echo $ada; ?></p>
<p>Ημερομηνία Εγγράφου: <?php echo $docdate; ?><br/>
Ημερομηνία Αποστολής: <?php echo $senddate; ?></p>
<?php
        if ($corrected)
        {
            echo "<div class='important'><p>Το έγγραφο έχει νεώτερη διόρθωση με ΑΔΑ: <a href='?ada={$corrected}'>{$corrected}</a></p></div>",PHP_EOL;
        }
        if ($correction)
        {
            echo "<p>Το έγγραφο είναι διόρθωση παλαιότερου ΑΔΑ: <a href='?ada={$correction}'>{$correction}</a></p>",PHP_EOL;
        }
?>
<p>Πηγαίνετε στη σελίδα στη Δι@ύγεια: <a href="http://et.diavgeia.gov.gr/f/all/ada/<?php echo $ada; ?>">http://et.diavgeia.gov.gr/f/all/ada/<?php echo $ada; ?></a></p>
<?php
    }
?>
</body>
</html>

pdo σύνδεση σε sql server 2005

Έκανα μια δοκιμή σε Ubuntu server να συνδεθώ σε sql server 2005 μέσω pdo και όχι με τις mssql_* functions. Αν και η σύνδεση έπαιξε αμέσως μια χαρά και έβλεπα ελληνικά (utf8) από την php χρησιμοποιώντας τις mssql functions, από το pdo είχα ένα περίεργο πρόβλημα:

Τρέχοντας τον κώδικα από το shell έβλεπα στο output κανονικά τα ελληνικά, αλλά τρέχοντας τον κώδικα από τον apache η ιστοσελίδα μου έφερνε μόνο ερωτηματικά ????????????

Μετά από ψάξιμο μιας μέρας και αρκετές δοκιμές η λύση τελικά ήταν στο connection string. Για να βλέπεις τα ελληνικά αρκεί να προσθέσεις το ;charset=utf8"

<?php
    try {
        $hostname = 'Ο sql server';
        $dbname = 'Η βάση δεδομένων';
        $username = '...';
        $pw = '...';

        $dbh = new PDO (
                "dblib:host=$hostname;dbname=$dbname;charset=utf8",
                "$username",
                "$pw");
    } catch (PDOException $e) {
        echo "Failed to get DB handle: " . $e->getMessage() . "n";
        exit;
    }
    $stmt = $dbh->prepare('SELECT * FROM users');
    $stmt->execute();
    while ($row = $stmt->fetchobject())
    {
        print_r($row);
    }
    unset($dbh);
    unset($stmt);
?>

Δι@ύγεια OpenData API

Συνεχίζοντας την προσπάθεια σε C# να χρησιμοποιήσω το API της Δι@ύγειας για να πάρω πληροφορίες για το έγγραφο που με ενδιαφέρει και μετά από αρκετές αποτυχημένες δοκιμές είπα να προσπαθήσω με παλιότερο κώδικα που ξέρω ότι δουλεύει.

Οπότε παίρνοντας τον παλιότερο κώδικα που χρησιμοποιούσα για να διαβάζω τον html κώδικα μιας ιστοσελίδας (και να βρίσκω την εξωτερική ip του router μου) έκανα την πρώτη δοκιμή στο http://et.diavgeia.gov.gr/f/all/search/results.php?include_ada=1&field_ada=%CE%924%CE%A9%CE%9B%CE%A7-%CE%A8%CE%9A%CE%92

Ο αρχικός κώδικας ήταν λοιπόν:

public static bool CheckDiavgeiaADA(string ADA)
{
    string url = "http://et.diavgeia.gov.gr/f/all/search/results.php?include_ada=1&field_ada=" + ADA;
    string webPage = GetWebResponse(url);
    return true;
}

public static string GetWebResponse(string url)
{
    try
    {
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
        request.Method = WebRequestMethods.Http.Get;
        request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.Reload);
        System.Net.HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);
        String responseText = reader.ReadToEnd();
        reader.Close();
        dataStream.Close();
        response.Close();
        return responseText;
    }
    catch (System.Net.WebException ex)
    {
        System.Diagnostics.Debug.Write(ex.Message);
        return string.Empty;
    }
}

Η πρώτη προσπάθεια ήταν επιτυχής, μπορούσα να διαβάσω το περιεχόμενο της ιστοσελίδας. Μετά ήρθε η ώρα να διαβάσω τη σελίδα όπως λέει το api (http://opendata.diavgeia.gov.gr/api/decisions?ada=Β4ΩΛΧ-ΨΚΒ)

Ο κώδικας έγινε

public static bool CheckDiavgeiaADA(string ADA)
{
    try
    {
        //ADA = System.Web.HttpUtility.UrlEncode(ADA);
        string s = "http://opendata.diavgeia.gov.gr/api/decisions?ada=" + ADA;
        string xmlStr = GetWebResponse(s);
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xmlStr);
    }
    catch (WebException e)
    {
        System.Diagnostics.Debug.Print(e.ToString());
    }
    return false;
}

και εμφανίστηκε πάλι το γνωστό web exception όπως και πριν. Πλέον όμως ο κώδικας έσκαγε μέσα στην GetWebResponse που σε άλλες σελίδες δουλεύει κανονικά. Έκανα μια δοκιμή σε άλλες σελίδες που επιστρέφουν xml document για να επιβεβαιώσω ότι ο κώδικας αυτός δεν περιορίζεται στο να διαβάζει μόνο text και html documents και πάλι έπαιξε κανονικά.

Άρα κάτι “βιτσιόζικο” έχει ο συγκεκριμένος server της διαύγειας. Είτε είναι πολύ αυστηρός στις απαιτήσεις του για να επιστρέψει ένα σωστό xml document, είτε πολύ “αρρωστιάρικος” και αν δεν του δώσεις το request όπως ακριβώς το χρειάζεται, τα χάνει και “βαράει μπιέλες”. Θα έλεγα μάλλον το δεύτερο, κρίνοντας από το exception stack.

Αφού είχα ήδη την προηγούμενη εμπειρία από τον php κώδικα που δούλεψε προσθέτοντας μερικά επιπλέον directions στο request (για την file_get_contents) είπα να κάνω μια προσπάθεια να βάλω τα ίδια directions μήπως και γίνει κάτι.

Ξεκίνησα προσθέτοντας τη γραμμή

request.KeepAlive = true;

μετά το request.CachePolicy αλλά δεν υπήρξε καμία διαφορά. Σειρά είχε λοιπόν να προστεθεί και το

request.Accept = "*/*";

και το θαύμα έγινε. Η σελίδα μου επέστρεψε το xml document. Μα είναι δυνατόν; Τέλος πάντων αφού έπαιξε…

Ο κώδικας τελικά που παίζει και για τις υπόλοιπες ιστοσελίδες και για το OpenData web service της Δι@ύγειας έγινε:

public static bool CheckDiavgeiaADA(string ADA)
{
    try
    {
        //ADA = System.Web.HttpUtility.UrlEncode(ADA);
        string s = "http://opendata.diavgeia.gov.gr/api/decisions?ada=" + ADA;
        string xmlStr = GetWebResponse(s);
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xmlStr);
    }
    catch (WebException e)
    {
        System.Diagnostics.Debug.Print(e.ToString());
    }
    return false;
}

public static string GetWebResponse(string url)
{
    try
    {
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
        request.Method = WebRequestMethods.Http.Get;
        request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.Reload);
        request.Accept = "*/*";
        request.KeepAlive = true;
        System.Net.HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream);
        String responseText = reader.ReadToEnd();
        reader.Close();
        dataStream.Close();
        response.Close();
        return responseText;
    }
    catch (System.Net.WebException ex)
    {
        System.Diagnostics.Debug.Write(ex.Message);
        return string.Empty;
    }
}

…εντάξει όχι ακριβώς έτσι γιατί η function CheckDiavgeiaADA δεν κάνει κάτι χρήσιμο έτσι όπως είναι, αλλά τώρα έχουμε το xml για να πάρουμε τα στοιχεία που μας ενδιαφέρουν.

Δι@ύγεια OpenData API – PHP Version

Δοκίμασα να κάνω μια διαφορετική δοκιμή να χρησιμοποιήσω το OpenData API της δι@ύγειας σε php αυτή τη φορά μήπως και έχω περισσότερη τύχη. Μετά από δοκιμές και άπειρα “500 internal errors” κατέληξα σε αυτά τα δύο κομμάτια κώδικα (μη ρωτήσετε που τα βρήκα, ούτε που θυμάμαι, κάπου στο internet) με όνομα works που δείχνει να δουλεύει και με όνομα fails που αρνείται πεισματικά και επιστρέφει πάντα 500 internal error.

<?php 
$ada = 'Β4ΩΛΧ-ΨΚΒ'; 
$url = 'http://opendata.diavgeia.gov.gr/api/decisions?ada='.urlencode($ada); 

function works($url) 
{ 
   $ch = curl_init(); 
   curl_setopt($ch, CURLOPT_URL,$url); 
   curl_setopt($ch, CURLOPT_HEADER, false); 
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
   $xmlstr = curl_exec($ch); 
   curl_close($ch); 

   return $xmlstr; 
} 

function fails($url) 
{ 
   $xmlstr = file_get_contents($url); //<-- line 19
   return $xmlstr; 
} 

$xml1 = simplexml_load_string(works($url)); 
$xml2 = simplexml_load_string(fails($url)); 

print_r($xml1); 
print_r($xml2); 
?>

Στο ini to allow_url_fopen το έχω on. Εκανα και το allow_url_include on (που δεν χρειάζεται αλλά να υπάρχει), έβαλα και το user_agent=”PHP” μιας και το ανέφερε και αυτό κάποιος στο documentation της file_get_contents.

Αυτό που παίρνω όταν τρέχω το αρχείο από κονσόλα είναι:

[dva@test ~]# php diavgeia.php 
PHP Warning:  file_get_contents(http://opendata.diavgeia.gov.gr/api/decisions?ada=%CE%924%CE%A9%CE%9B%CE%A7-%CE%A8%CE%9A%CE%92): failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error 
 in /var/www/html/test/xml/diavgeia.php on line 19 
SimpleXMLElement Object 
( 
    [queryInfo] => SimpleXMLElement Object 
        ( 
            [count] => 1 
            [from] => 1 
            [order] => desc 
            [total] => 1 
        ) 
)

Το οποίο δείχνει ότι η function works πέρασε, η fails κράσαρε κάτι στον server της διαύγειας, και μετά τυπώνει το xml που διάβασε η works, (για τη fails δε δείχνει τίποτα).

Δι@ύγεια OpenData API

Προσπαθώ να πάρω μερικές πληροφορίες για συγκεκριμένα έγγραφα από το web service της διαύγειας, σύμφωνα με αυτά που αναφέρονται στο http://opendata.diavgeia.gov.gr/

Εχω λοιπόν τον εξής απλούστατο κώδικα:

try
{
 ADA = "Β4ΩΛΧ-ΨΚΒ";
 //ADA = System.Web.HttpUtility.UrlEncode("Β4ΩΛΧ-ΨΚΒ");
 System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
 string s = "http://opendata.diavgeia.gov.gr/api/decisions?ada=" + ADA;
 doc.Load(s);
}
catch (WebException e)
{
 System.Diagnostics.Debug.Print(e.ToString());
}

To οποίο πάντα (μα πάντα) μου πετάει το εξής web exception:

A first chance exception of type 'System.Net.WebException' occurred in System.Xml.dll
System.Net.WebException: The remote server returned an error: (500) Internal Server Error.
 at System.Net.HttpWebRequest.GetResponse()
 at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri, ICredentials credentials)
 at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials)
 at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
 at System.Xml.XmlTextReaderImpl.OpenUrlDelegate(Object xmlResolver)
 at System.Threading.CompressedStack.runTryCode(Object userData)
 at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
 at System.Threading.CompressedStack.Run(CompressedStack compressedStack, ContextCallback callback, Object state)
 at System.Xml.XmlTextReaderImpl.OpenUrl()
 at System.Xml.XmlTextReaderImpl.Read()
 at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
 at System.Xml.XmlDocument.Load(XmlReader reader)
 at System.Xml.XmlDocument.Load(String filename)
 at Τest.TestADA.ADACheck(String ADA)

Μόλις πάρω internal server error, φαίνεται ότι κρασάρει ολόκληρο το web service στον server της Διαύγειας και δεν μπορώ να δω ούτε από web browser το xml δίνοντας τη διεύθυνση. Απλώς ο web server της διαύγειας απαντάει με το εξής:

HTTP Status 500 -
type Exception report
message
description The server encountered an internal error () that prevented it from fulfilling this request.
exception
javax.servlet.ServletException: Could not resolve view with name 'xmlView' in servlet with name 'diavgeia-api'
 org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1042)
 org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:798)
 org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
 org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:647)
 org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:552)
 javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
 javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
 org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:344)
 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:110)
 org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:98)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:95)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:79)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:55)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:36)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:178)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
 org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:356)
 org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:150)
 org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
 org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
note The full stack trace of the root cause is available in the Apache Tomcat/6.0.29 logs.
Apache Tomcat/6.0.29

Μετά από λίγο (δεν έχω μετρήσει να δώ αν είναι 1 λεπτό ή 10) συνέρχεται ο web server, μέχρι να ξανατρέξω τον κώδικα και να ξανακρασάρει 😮

Υπάρχει καμιά πρόταση, ώστε να παίρνω τις πληροφορίες που θέλω;
Εχω μπροστά μου κάποιο λάθος και δεν το βλέπω;
Δεν ζητάω πολλά, αυτό που δείχνει η διεύθυνση για παράδειγμα http://opendata.diavgeia.gov.gr/api/decisions?ada=Β4ΩΛΧ-ΨΚΒ

PHP Password generator extension

Και το αντίστοιχο password generator σε php extension για να υπάρχει παντού διαθέσιμο χωρίς να χρειάζεται includes και copy/paste, αλλά και να είναι λίγο ή πολύ πιο γρήγορο από την αντίστοιχη function.

Δημιουργούμε ένα νέο φάκελο passwd και εκεί μέσα δημιουργούμε τα εξής αρχεία:

config.m4

PHP_ARG_ENABLE(passwd, whether to enable passwd support, [ --enable-passwd Enable passwd support])

if test "$PHP_PASSWD" = "yes"; then
  AC_DEFINE(HAVE_PASSWD, 1, [Whether you have passwd])
  PHP_NEW_EXTENSION(passwd, passwd.c, $ext_shared)
fi

INC_CHECK_DIRS="/usr /usr/local"

for i in $INC_CHECK_DIRS ; do
  if test -f $i/include/php/ext/standard/php_rand.h; then
    PHP_ADD_INCLUDE($i/include/php/ext/standard)
    break
  fi
done

passwd.h

#ifndef __PASSWD_H__
#define __PASSWD_H__ 1
#ifndef _WIN32
#include
#endif
#include
#ifdef ZTS
#include "TSRM.h"
#endif

#ifndef BOOL
#define BOOL	short
#endif //BOOL
#ifndef FALSE
#define FALSE	(0)
#endif
#ifndef TRUE
#define TRUE	(1)
#endif

#define PHP_PASSWD_WORLD_VERSION "1.0.0"
#define PHP_PASSWD_WORLD_EXTNAME "passwd"

PHP_FUNCTION(passwd_version);
PHP_FUNCTION(passwd_create);

PHP_MINIT_FUNCTION(passwd);
PHP_MINFO_FUNCTION(passwd);

extern zend_module_entry passwd_module_entry;
#define phpext_passwd_ptr &passwd_module_entry

#endif //__PASSWD_H__

passwd.c

#include "php.h"
#include "php_rand.h"

#ifdef _WIN32
#include "ext/standard/info.h"
#endif

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "passwd.h"

#ifndef LONG
typedef long LONG;
#endif

static function_entry passwd_functions[] = {
    PHP_FE(passwd_version, NULL)
    PHP_FE(passwd_create, NULL)
    {NULL, NULL, NULL}
};

zend_module_entry passwd_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    PHP_PASSWD_WORLD_EXTNAME,
    passwd_functions,
    PHP_MINIT(passwd),
    NULL,
    NULL,
    NULL,
    PHP_MINFO(passwd),
#if ZEND_MODULE_API_NO >= 20010901
    PHP_PASSWD_WORLD_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_PASSWD  
ZEND_GET_MODULE(passwd)
#endif

char* passwordChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()-_=+[]{}\|,<.>/?~;:";
int passwordCharsLen = 0; 

static void ZEND_MODULE_GLOBALS_CTOR_N(passwd)(void *passwd_globals TSRMLS_DC)
{
    //php_srand( time(NULL) );  
}

PHP_MINIT_FUNCTION(passwd)
{
    passwordCharsLen = strlen(passwordChars);
    return SUCCESS;  
}

PHP_MINFO_FUNCTION(passwd)
{
    php_info_print_table_start();
    php_info_print_table_row(2, "passwd", "passwd Functions");
    php_info_print_table_row(2, "passwd_create", "return a random password string");
    php_info_print_table_end();
}

PHP_FUNCTION(passwd_version)
{
    RETURN_STRING(PHP_PASSWD_WORLD_VERSION, 1);
} 

PHP_FUNCTION(passwd_create)
{
    long i, llen, rnd_idx;
    char* tmp;
    if (ZEND_NUM_ARGS() != 1) {
        WRONG_PARAM_COUNT;
    } 
    llen = 8;
    if ((zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &llen) == FAILURE) ||
        (llen < 4) || (llen > 128))
    {
        RETURN_STRING("len should be an integer between 4 and 128", 1);
    } 

    tmp = emalloc(llen+1);

    for (i=0; i<llen; i++)
    {
        rnd_idx = php_rand(TSRMLS_C);
        RAND_RANGE(rnd_idx, 0, passwordCharsLen-1, PHP_RAND_MAX);
        tmp[i] = passwordChars[rnd_idx];
    }
    tmp[i]=(char)0; 
    RETURN_STRING(tmp, 0);
}

Τώρα o αντίστοιχος κώδικας του σχετικού post θα μπορούσε να γίνει ως εξής:

<?php
header('Content-type: text/plain');
    if (isset($_GET['len'])) $len = (int)$_GET['len'];
    if (($len < 4) || ($len > 128)) $len = 8;
    echo passwd_create($len);
?>