Thread JS MD5 Funktion nachbauen (19 answers)
Opened by bianca at 2015-10-12 20:06

bianca
 2015-10-22 16:42
#182682 #182682
User since
2009-09-13
7016 Artikel
BenutzerIn

user image
Hier mal das ganze Script, womit die Frage zu tun hat. Falls das mal jemand gebrauchen kann.
Es geht um den DSL Reconnect der FRITZ!Box per Screenscraping mit Perl nebst WWW::Mechanize und Digest::MD5
Warum braucht man das? Wenn man das zeitlich präziser steuern möchte, als die FRITZ!Box das selbst täte.
more (64.3kb):
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/perl
use strict;
use warnings;

my %oberkonfig = (
    cron_errorlog   => 'cron_error.txt',
);

print "Rueckgabe: '".fritz_reconnect(\%oberkonfig)."'\n";
# Rückgaben:
# ==========
# ''                    -> jetzt keine Ausführung, da die aktuelle Stunde nicht $job_konfig{ausfuehrung_um} entspricht
#                          oder die aktuelle IP von heute ist (es wird nur ein Reconnect pro Tag ausgeführt)
# String                -> Fehler aufgetreten, im String steht eine sprechende Fehlermeldung
# String mit Präfix |  -> Auftrag ausgeführt, der String enthält alte und neue Verbindungsdaten
#
# hier im Beispiel soll der Reconnect immer erst durchgeführt werden,
# wenn die aktuelle Stunde auf der Uhr $job_konfig{ausfuehrung_um} entspricht,
# es wird von einem Aufruf per Cronjob in jeder vollen Stunde des Tages ausgegangen

# --------------------------------------------------------------------------------------------------------------
sub systemlog_eintrag {
    # Möglichkeit, in einer Art Protokoll die Verbindungsdaten aufzuzeichnen
    return;
}
# --------------------------------------------------------------------------------------------------------------
sub error {
    print STDERR '['.localtime().'] [error] [MELDUNG JOB '.$_[1].'] '.$_[0]."\n";
}
# --------------------------------------------------------------------------------------------------------------
sub fritz_reconnect {
    #
    # DSL Reconnect der FRITZ!Box per Screenscraping mit Perl nebst WWW::Mechanize und Digest::MD5
    #
    # Getestet mit der FRITZ!Box 7490
    # mit FRITZ!OS 06.30
    # Firmware-Version: 113.06.30
    # im Oktober 2015
    #
    my %job_konfig = (
        ausfuehrung_um          => 4,                                                                           # Stunde des Tages, wann der Reconnect durchgeführt werden soll
        url                     => 'http://fritz.box',
        pass                    => '***',
#       debug                   => 'test_%s.html',                                                              # Dateiname und ggf. Pfad für die Dateien in die die einzelnen Seiten gespeichert werden, %s ist die laufende Nummer, z.B. '/meinpfad/debug_%2.html', nicht definieren, wenn kein Debug erwünscht
        response_reconnect      => 'done:0',                                                                    # was die FRITZ!Box auf "Neu verbinden" zurück liefert
        wait_nach_reconnect     => 20,                                                                          # max. Sekunden Wartezeit nach dem Reconnect zum Abruf der neuen Verbindungsdaten
        verbindungsstatus       => sub {                                                                        # hier den Regex für den Verbindungsstatus pflegen; stattdessen im HTML Baum mit HTML::TreeBuilder rum zu suchen bietet sich hier nicht an
            my ($content,$data) = @_;
            # für FRITZ!OS 6.30:
            if ($$content =~ m~<td><div id='ipv4_title'>Internet, IPv4</div></td><td id='ipv4_led'><div class='led_green'>&nbsp;</div></td><td><div id='ipv4_info'><span class="limited">verbunden seit (\d+).(\d+).(\d+), (\d+):(\d+) Uhr</span>, <span class="limited" style="max-width:240px;">([^<]+)</span><span class="limited">,<br>IP-Adresse: (\d+).(\d+).(\d+).(\d+)</span></div></td>~) {
                $data->{datum} = "$1.$2.$3";
                $data->{zeit} = "$4:$5";
                $data->{provider} = $6;
                $data->{ip4} = "$7.$8.$9.$10";
                $data->{found} = defined;
            }
        },

        # ----- ab hier nichts mehr ändern -----
        fritzbox_hex_md5        => sub {
            # Dank an Jan! https://www.perl-community.de/bat/poard/thread/19845#ms_182588
            my ($string) = @_;
            my @chars = split(//,$$string);
            require Digest::MD5;
            return Digest::MD5::md5_hex(join("\0",@chars)."\0");
        },
        makeDots                => sub {
            my ($str) = @_;
            my $newStr = '';
            my $len = length($str);
            for (my $i = 0; $i < $len; $i ++) {
                my $char = substr($str,$i,1);
                $newStr .= (ord($char) > 255 ? '.' : $char);
            }
            return $newStr;
        },
        write_debug_file        => sub {
            my ($k,$mech,$nr) = @_;
            if (defined $k->{debug}) {
                $$nr ++;
                $$mech->save_content(
                    sprintf($k->{debug},$$nr),
                    binmode             => ':raw',
                    decoded_by_headers  => 1,
                );
            }
        },
    );

    my ($env) = @_;
    my ($back,$content,$g_challenge);
    my @jetzt = localtime(time());
    return '' if $jetzt[2] != $job_konfig{ausfuehrung_um};

    require WWW::Mechanize;
    my $mech = WWW::Mechanize->new(
        agent       => 'Automat',                                                                               # ist der FRITZ!Box egal
        quiet       => 1,                                                                                       # prophylaktisch
        onwarn      => \&{                                                                                      # sollte definiert werden sonst schreibt es bei einer Warnung trotz quiet=>1 auf STDERR
            sub {
                $back = "Warnung '".join('',@_)."'";
                goto FEHLER_UND_RUECKSPRUNG_ZUM_HAUPTPROGRAMM;
            }
        },
        onerror     => \&{                                                                                      # sollte definiert werden sonst schreibt es bei einem Fehler trotz quiet=>1 auf STDERR
            sub {
                $back = "Fehler '".join('',@_)."'";
                goto FEHLER_UND_RUECKSPRUNG_ZUM_HAUPTPROGRAMM;
            }
        },
        stack_depth => 0,                                                                                       # die history Tabelle wird nicht gebraucht, Speicher schonen
    );

    my $debug_nr = 0;
    do {{                                                                                                       # sonst werden die Einrückungen im Code zu breit
        my $dest_url = "$job_konfig{url}/login.lua";

        # Login Formular aufrufen
        $mech->get($dest_url);
        $content = $mech->content();
        if (!defined $content || $content eq '') { $back = "Abruf der Ressource '$dest_url' gescheitert"; last; }

        $job_konfig{write_debug_file}->(\%job_konfig,\$mech,\$debug_nr);
        if ($content =~ /g_challenge( ?)=( ?)"([^"]{8})"/) { $g_challenge = $3; }
        else { $back = "Finde die Variablendefinition zu &gt;g_challenge&lt; nicht"; last; }

        my $form_id = 'uiMainForm';
        my $formular = $mech->form_id($form_id);                                                                # selektiert auch das default Formular obwohl es in der Doku anders steht
        if (!defined $formular) { $back = "Finde das Formular mit der ID '$form_id' nicht"; last; }

        my $temp = $g_challenge.'-'.$job_konfig{makeDots}->($job_konfig{pass});
        my $var_response = $g_challenge.'-'.$job_konfig{fritzbox_hex_md5}->(\$temp);
        my $feldname = 'response';
        $mech->field($feldname,$var_response,1);                                                                # field() liefert leider kein def/undef zurück

        # Login Formular absenden
        if (!$mech->submit()) { $back = "Das Formular mit der ID '$form_id' konnte nicht abgesendet werden"; last; }

        $content = $mech->content();
        if (!defined $content || $content eq '') { $back = "Nach Absenden des Formulars mit der ID '$form_id' wird nichts mehr geliefert"; last; }

        $job_konfig{write_debug_file}->(\%job_konfig,\$mech,\$debug_nr);
        my $link_zu_dsl;
        if ($content =~ /\/internet\/inetstat_monitor\.lua\?sid\=([0-9a-z]+)/) {                                # nutze kein $mech->follow_link(), weil der Link unten nochmal gebraucht wird
            $link_zu_dsl = "$job_konfig{url}/internet/inetstat_monitor.lua?sid=$1";
        }
        else { $back = "Finde den Menüpunkt 'Internet' nicht"; last; }

        # Menüpunkt 'Internet' anklicken
        $mech->get($link_zu_dsl);
        $content = $mech->content();
        if (!defined $content || $content eq '') { $back = "FRITZ!Box liefert im Menüpunkt 'Internet' nichts zurück"; last; }

        $job_konfig{write_debug_file}->(\%job_konfig,\$mech,\$debug_nr);
        my %data;
        $job_konfig{verbindungsstatus}->(\$content,\%{$data{alt}});
        if (!defined $data{alt}{found}) { $back = "Im Menüpunkt 'Online-Monitor' konnte der Verbindungsstatus für 'Internet, IPv4' nicht gefunden werden"; last; }

        if ($data{alt}{datum} eq sprintf('%02d.%02d.%04d',$jetzt[3],$jetzt[4]+1,$jetzt[5]+1900)) { return ''; }

        if ($content =~ /internet\/inetstat_monitor\.lua\?sid=([0-9a-z]+)&useajax=1&action=disconnect/) {
            $dest_url = "$job_konfig{url}/internet/inetstat_monitor.lua?sid=$1&useajax=1&action=disconnect";
        }
        else { $back = "Im Menüpunkt 'Online-Monitor' konnte der Schalter 'Neu verbinden' nicht gefunden werden"; last; }

        # Schalter 'Neu verbinden anklicken'
        my $time_reconnect = time();
        $mech->get($link_zu_dsl.'&useajax=1&action=disconnect');
        $content = $mech->content();
        if (!defined $content || $content eq '') { $back = "Die FRTIZ!Box liefert auf 'Neu verbinden' nichts zurück"; last; }

        $job_konfig{write_debug_file}->(\%job_konfig,\$mech,\$debug_nr);
        if ($content ne $job_konfig{response_reconnect}) { $back = "Der Aufruf 'Neu verbinden' liefert nach disconnect nicht '$job_konfig{response_reconnect}' zurück sondern '$content'"; last; }

        # wieder Menüpunkt 'Internet' aufrufen
        my $start = time();
        while(                                                                                                  # als Schleife, weil das Connect unterschiedlich lange dauert
            (
                !exists $data{neu}
                    ||
                "$data{alt}{datum}$data{alt}{zeit}" eq "$data{neu}{datum}$data{neu}{zeit}"
            )
                &&
            time() - $start < $job_konfig{wait_nach_reconnect}
        ) {
            sleep(2);
            $mech->get($link_zu_dsl);
            $content = $mech->content();
            if (!defined $content || $content eq '') { $back = "FRITZ!Box liefert im Menüpunkt 'Internet' beim zweiten Aufruf nichts zurück"; last; }

            $job_konfig{write_debug_file}->(\%job_konfig,\$mech,\$debug_nr);
            delete $data{neu} if exists $data{neu};
            $job_konfig{verbindungsstatus}->(\$content,\%{$data{neu}});
            if (!defined $data{neu}{found}) { $back = "Nach dem Reconnect konnte im Menüpunkt 'Online-Monitor' der Verbindungsstatus für 'Internet, IPv4' nicht gefunden werden"; last; }
        }
        last if defined $back;

        if ("$data{alt}{datum}$data{alt}{zeit}" eq "$data{neu}{datum}$data{neu}{zeit}") {
            my @t = localtime($time_reconnect);
            $back = "Etwas muss schief gelaufen sein, trotz Reconnect am "
                .sprintf("%02d.%02d.%04d um %02d:%02d Uhr",@t[3..5],$t[2],$t[1])
                ." und einer Wartezeit von "
                .(time() - $start)
                ." Sekunde(n) werden keine neuen Verbindungsdaten ausgegeben, nur die alte IP Adresse $data{alt}{ip4} von $data{alt}{datum} um $data{alt}{zeit} Uhr konnte ermittelt werden"
            ;
            last;
        }

        my $msg = "|Job erledigt, bisherige IP Adresse seit $data{alt}{datum} um $data{alt}{zeit} Uhr lautete"
            ." $data{alt}{ip4}, neue IP Adresse seit $data{neu}{datum} um $data{neu}{zeit} Uhr lautet"
            ." $data{neu}{ip4}"
        ;
        main::systemlog_eintrag(0,1,20,$msg);
        return $msg;

    }} while(0);

    FEHLER_UND_RUECKSPRUNG_ZUM_HAUPTPROGRAMM:
    error($back,87);
    return "Fehler aufgetreten siehe '$env->{cron_errorlog}'";
}
10 print "Hallo"
20 goto 10

View full thread JS MD5 Funktion nachbauen