individuals Certificate Revocation List checker

Έλεγχος ανάκλησης πιστοποιητικών ιδιωτών της παλιάς ΑΠΕΔ (από 1/8/2013 και μετά).

Συχνά ο υπάλληλος έρχεται αντιμέτωπος με την ανάγκη ελέγχου εγκυρότητας των πιστοποιητικών με τα οποία έχουν υπογραφεί κάποια ψηφιακά έγγραφα. Η διασικασία όπως της υπογραφής και ελέγχου των πιστοποιητικών έχει αλλάξει και πλέον οι αδηγίες που δίνονται από την ΑΠΕΔ βάζουν πολλά χειροκίνητα βήματα και κάποια είναι μάλλον σκέτη ζαλάδα.

Οι οδηγίες που δίνονται στον σύνδεσμο Έλεγχος εγκυρότητας Εγκεκριμένης Ηλεκτρονικής Υπογραφής της παλιάς ΑΠΕΔ απαιτούν την εκτέλεση των (σχετικά απλών) βημάτων 1, 2, 3, 4 και μετά αρχίζουν να γίνονται πιο δύσκολα τα πράγματα. Στο βήμα 5 πρέπει να βρούμε και να σημειώσουμε τον σειριακό αριθμό (Serial Number) του πιστοποιητικού και στο βήμα 6 μας δίνει οδηγίες πως να κατεβάσουμε στον υπολογιστή μας τη λίστα ανάκλησης ψηφιακών πιστοποιητικών.

Αυτό δεν μας προκαλεί ιδιαίτερη αίσθηση, μέχρι να παρατηρήσουμε πως αυτό το αρχείο είναι φτιαγμένο για να το διαβάζει ο υπολογιστής, κάποια μηχανή, κάποιο πρόγραμμα και σε κάθε περίπτωση όχι άνθρωπος. Όχι λογικός άνθρωπος!
Και αυτό μας ζητείται να κάνουμε στο βήμα 7, σαν παράλογοι! Να ανοίξουμε το αρχείο και να ψάξουμε μέσα στην ατέλειωτη λίστα με τους σειριακούς αριθμούς να βρούμε τον σειριακό αριθμό του πιστοποιητικού που βρήκαμε στο βήμα 5. Πόσες ατέλειωτες ώρες μπορούμε να φάμε για να ελέγξουμε ένα έγγραφο; Αν έχει μάλιστα δύο ή τρεις υπογραφές το καθ’ ένα, με 2-3 έγγραφα έχουμε φάει χαλαρά όλη τη μέρα μας.

Για να βοηθήσουμε λίγο την κατάσταση ξεκίνησα με μία προσπάθεια να αυτοματοποιηθούν τα βήματα 6 και 7. Στη σελίδα https://www.vasileiadis.eu/crl μπήκε ένα πεδίο αναζήτησης στο οποίο αντιγράφουμε τον σειριακό αριθμό και πατώντας το διπλανό κουμπί ή το πλήκτρο [ENTER], αναλαμβάνει η σελίδα να κάνει την αναζήτηση για εμάς, επιστρέφοντας μας για την ημερομηνία ανάκλησης, εάν έχει ανακληθεί το πιστοποιητικό. Ξεμπερδεύουμε λοιπόν πολύ πιο γρήγορα και ασύγκρητα πιο εύκολα. Ο έλεγχος τώρα από θέμα ωρών έγινε θέμα 2-3 δευτερολέπτων.

Αναζήτηση ανάκλησης με σειριακό αριθμό πιστοποιητικού

Για να πάμε ένα βήμα ακόμα τη διευκόλυνση μας ώστε να μην χρειάζεται να ψάχνουμε και τον σειριακό αριθμό του πιστοποιητικού, μπήκε στη σελίδα και ένα δεύτερο πλαίσιο στο οποίο με σύρσιμο και απόθεση (drag ‘n’ drop) ελέγχουμε τα αρχεία pdf για την ύπαρξη πιστοποιητικών (κατάλληλων για ψηφιακή υπογραφή) και εμφανίζουμε πληροφοριακά στοιχεία για να μπορεί εύκολα ο χρήστης να αποφασίσει για την εγκυρότητα του υπογεγραμμένου pdf αρχείου.

Έλεγχος ανάκλησης των ψηφιακών υπογραφών που υπάρχουν σε ένα PDF αρχείο (μεγέθους μέχρι 16MB).

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

Κουτί-τασάκι

Κουτί-τασάκι

Με την ευστροφία και τη δημιουργικότητά τους, φροντίζουν οι ευφάνταστοι χρήστες μας να μας εκπλήσσουν με τις νέες, επιπλέον χρήσεις των υπολογιστών, πέρα από τις προφανείς.

Δεν χρησιμεύει λοιπόν μόνο το cd reader σαν θήκη για καφέ.
Το κουτί του υπολογιστή, πέρα από το να φιλοξενεί τις κάρτες του υπολογιστή και το υπόλοιπο υλικό, κατά τον έλεγχο για καθάρισμα από τη σκόνη, όπως φαίνεται μπορεί κάλλιστα να χρησιμεύσει και για τασάκι.
Υπάρχει βέβαια μια δυσκολία στο να το αδειάζεις, αλλά γι αυτό φροντίζουν τα τμήματα πληροφορικής, τα τμήματα τεχνικής υποστήριξης και οι τεχνικοί υπολογιστών που θα αναλάβουν και χρέη καθαρίστριας.

Δήλωση

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.

Γιατί η άδεια διακοπών είναι σαν τα software demos

vacation phoneΕτοιμάζεσαι να παραδώσεις ένα software project, το έχεις τεστάρει, το έχεις ξανατεστάρει, το έχεις πακετάρει, έχεις βάλει το users manual, έχεις κάνει αρκετές φορές δοκιμαστικές εγκαταστάσεις και τρεξίματα με τα διάφορα σενάρια που έχεις ετοιμάσει. Είσαι λοιπόν μαζί με τα σκονάκια και τις σημειώσεις σου πανέτοιμος να κάνεις την εγκατάσταση στον πελάτη για να ολοκληρώσεις πανηγυρικά την επίδειξη.

Και όμως εκείνη την ώρα θα αρχίσουν να ξυπνάνε από το λήθαργο και τη χειμερία νάρκη όλα εκείνα τα bugs που έμεναν υπομονετικά κρυμμένα για να σου κάνουν τη χειρότερη στιγμή τη ζωή όσο πιο δύσκολη γίνεται. Γιατί όπως και να το κάνεις, όσο προετοιμασμένος κι αν είσαι, αφού ξέρεις ότι τα bugs είναι σαν τα μικρόβια – όσο κι αν προσπαθείς να τα ξεφορτωθείς είναι εκεί και σε περιμένουν, όσες πειστικές δικαιολογίες κι αν έχεις, όσο εύκολα κι αν μπορείς να τα ξεπεράσεις ή να τα παρακάμψεις, μόλις πεταχτούν σου χαλάνε τη μέρα ή έστω τη διάθεση.

Αυτά τα software demos μου μοιάζουν με τις άδειες διακοπών. Κάνεις όλο το προηγούμενο διάστημα προετοιμασία, κλείνεις όλες τις επείγουσες υποθέσεις, κανονίζεις όλες τις λιγότερο σημαντικές για τις ημέρες μετά την επιστροφή σου, αφήνεις οδηγίες για το τι πρέπει να γίνει σε υποθέσεις που θα συνεχίσουν να εξελίσσονται κατά τη διάρκεια που θα λείπεις. Είσαι λοιπόν έτοιμος να φύγεις με άδεια έχοντας καθαρίσει από υποχρεώσεις στη δουλειά.

Κι όμως… Την πρώτη μέρα που θα λείψεις με άδεια, τότε είναι που θα αρχίσει το τηλέφωνο να χτυπάει σαν δαιμονισμένο, γιατί ξύπνησαν από το λήθαργο μερικές δεκάδες υποθέσεις υπερεπείγουσες, οι οποίες ήταν για μήνες ολόκληρους στο ψυγείο και τώρα που έφυγες, μόλις έλιωσαν οι πάγοι και ξαναζωντάνεψαν. Όχι μόνο ζωντάνεψαν αυτές αλλά μαζί τους γεννιούνται ή μεταλλάσσονται και άλλες τόσο επείγουσες που το deadline τους λήγει μέσα σε ελάχιστες μέρες. Το πόσες ημέρες διακοπών έχεις πάρει δεν έχει διόλου σημασία, γιατί το γενετικό τους υλικό είναι έτσι προγραμματισμένο ώστε να γεννηθούν αφού έχεις φύγει και περιορίζουν τόσο δυναμικά το deadline τους ώστε να λήγει πάντα πριν επιστρέψεις.

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

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;
    }

    // $url = 'http://opendata.diavgeia.gov.gr/api/decisions?ada=%s&output=json';
    $url = 'https://diavgeia.gov.gr/luminapi/api/decisions/%s.json';
    $response = getWebResponse(sprintf($url,$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);
?>