Archiv ‘Programmierung’

Mittwoch, 18. Dezember 2013

Den Netatmo PHP API-Client mit weniger strikten SSL-Anforderungen patchen

Vor einigen Tagen hörte mein Raspberry Pi-Dashboard auf, die Werte meiner Netatmo NWS01 Wetterstation für Apple iPhone und Android anzuzeigen.

Auf meinem lokalen Mac funktionierte das Dashboard hingegen problemlos; d.h. ich konnte mittels dem Netatmo PHP API-Client die JSON-Datei mit den aktuellen Messwerten wie Temperatur, Luftdruck und -feuchtigkeit abrufen.

Die genaue Ursache hinter dem Problem kenne ich bis heute nicht, doch ich vermute mit dem jetzigen Wissensstand, dass die Cyon-Ingenieure an der Konfiguration ihrer Server herumgewerkelt haben und dabei unter anderem das Root-Zertifikat entfernt haben, welches der Netatmo API-Client zur HTTPS-verschlüsselten Kommunikation mit den Netatmo-Servern verwendet.

Nachdem ich nämlich die Exception mittels vardump() genauer betrachtete, welche NACurlErrorType zurücklieferte, war der Fall schnell sonnenklar:

...
[message:protected] => SSL peer certificate or SSH remote key was not OK
...

Nun … gut! Was macht man da? Ich habe die Datei NAApiClient.php gepatcht, indem ich cURL mit der auf false gesetzten Option CURLOPT_SSL_VERIFYHOST sage, unverifizierte SSL-Zertifikate kommentarlos zu akzeptieren:

...
        else 
        {
            $opts[CURLOPT_HTTPHEADER] = array('Expect:');
        }
        
        $opts[CURLOPT_SSL_VERIFYHOST] = false;
        
        curl_setopt_array($ch, $opts);
...

Bei einer API wie Netatmo ist diese manuell herbeigeführte Schwachstelle zu verantworten. Ginge es um Mailverkehr oder Online-Banking, würde ich eine solche Option definitiv nicht aktivieren.

Tags: , , , , , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Donnerstag, 24. Oktober 2013

Mit PowerShell Dateien finden, die eine bestimmte Zeichenkette angehängt haben

Folgendes PowerShell-Script zeigt mir in einem Verzeichnis mit unzähligen PDF-Dateien diejenigen an, für welche es eine Version mit %NAMEN%.pdf sowie %NAMEN%-native.pdf gibt:

$duplicateIndicator = "-native"

function Get-ScriptDirectory {
  $Invocation = (Get-Variable MyInvocation -Scope 1).Value
  Split-Path $Invocation.MyCommand.Path
}

$scriptDir = Get-ScriptDirectory

$pattern = "*" + $duplicateIndicator + ".pdf"
Write-Host "Looking for $pattern ..."
Write-Host ""

$counter = 0
Dir $pattern | ForEach-Object {
	$counter++
	
	$duplicate = $_.name
	$original = $duplicate -replace $duplicateIndicator, ""
	$originalPath = $scriptDir + '\' + $original
	
	#Write-Host $duplicate
	#Write-Host $original
	
	#Write-Host "Looking for original file $originalPath"
	$fileExists = Test-Path $originalPath
	
	If ($fileExists) {
		Write-Host "Found duplicate for "
		Write-Host "    $original"
		Write-Host ""
	}
	
	#Dir $original
}

Write-Host "Scanned $counter file(s)."
Write-Host "Done."
Write-Host ""

Tags: , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 23. Oktober 2013

Mit Scripts generierte ICS-Dateien mit dem korrekten Zeichensatz in Google Calendar anzeigen

Folgender kleiner Text (hier: in einem PHP-Script) hilft:

...
header('Content-Type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename="calendar.ics"');
...

Gibt man das Charset nicht explizit an, erscheinen in Google Calendar statt den Sonderzeichen schwarze Rhomben mit einem Fragezeichen: �

Via: Character encoding in external public calendars – Why is Danish UTF-8 character not show correctly?

Tags: , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 23. Oktober 2013

Mehrere Dateien in einem Rutsch mit PowerShell umbenennen

Dir | Rename-Item –NewName { $_.name –replace ".jpg",".jpeg" }

Tags: , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Montag, 7. Oktober 2013

Mit Google Docs Spreadsheet eine Variable von einer Internet-Seite abrufen und als Zellwert verwenden

In Google Docs Spreadsheet ist es äusserst einfach, eigene Zellfunktionen zu definieren und diese danach mit einer JavaScript-basierten Funktion zu hinterlegen.

Ich habe mir diese Funktionalität zu Nutzen gemacht, um via meinen Linux-Server die aktuellen Gold- und Silberpreise in Schweizer Franken von einer Web-Site auszulesen und als „rohe“ Zahl an eine Zelle eines Google Docs Spreadsheet weiterzugeben. Basierend auf diesem Wert werden danach weitere Berechnungen in anderen Zellen angestossen.

Zuerst habe ich hierzu ein Spreadsheet eröffnet und sogleich unter Tools > Script editor… ein Script namens „Code.gs“ erstellt. Dort habe ich folgende (hier anonymisierte) JavaScript-Funktion abgelegt:

function getQuote(item) {
  if(item.length < 3) {
    return '#ERROR: parameter too short';
  }
  
  var response = UrlFetchApp.fetch("http://site.tld/index.php?item=" + item + "&raw=true");
  
  quoteRaw = response.getContentText();
  quote = parseFloat(quoteRaw).toFixed(2);
  
  return quote;
}

Anschliessend habe ich in der Zelle A1 den Wert „Gold“ hinterlegt. In Zelle A2 habe ich notiert „=getQuote(A1)“. Sobald man die Zelle A2 verlässt, versucht Google, die URL aufzurufen und die Antwort in eine Zahl umzuwandeln. Dies setzt natürlich voraus, dass das PHP-Script auf dem entfernten keinen HTML-Code zurückgibt, sondern Plaintext. Sobald Google die Funktion getQuote() aufruft, steht in der Zelle für ein bis zwei Sekunden „Thinking…“, während Google im Hintergrund meinen Server kontaktiert und die hinterlegte URL aufruft. Sobald der Wert empfangen wurde, wird dieser in der Zelle angezeigt.

Nebenbemerkung: Ja, den Umweg über meinen eigenen Server könnte ich mir selbstverständlich sparen. Ich bin mir es aber zu Schade, a) ausgedehntes JavaScript zu programmieren, zumal b) Google keine guten Debug-Werkzeuge im Script-Editor bereitstellt. Stattdessen verlasse ich mich auf PHP, Apache und auf dem Server installierte Linux-Tools mit ihren ausgefeilten Funktionen und einem sauberen Debug Logging.

Tags: , , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Montag, 7. Oktober 2013

Ein PHP-Script so geschwätzig wie möglich machen

<?php
	ini_set('error_reporting',E_ALL);
	ini_set('display_errors',1);

	...
?>

Tags: , , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Freitag, 27. September 2013

Mit PHPs SimpleXML auf XML-Namespaces zugreifen

Das klappt tatsächlich, auch wenn der Code damit nicht mehr so simpel wird.

XML

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss">
<channel>
<title>foursquare checkin history for Mario A.</title>
<description>foursquare checkin history for Mario A.</description>
<link>https://foursquare.com/emeidi</link>
<atom:link type="application/rss+xml" rel="self" href="https://feeds.foursquare.com/history/.rss" />
<item>
<title>Migros</title>
<description>@ Migros</description>
<link>https://foursquare.com/emeidi/checkin/</link>
<pubDate>Fri, 27 Sep 13 17:22:13 +0000</pubDate>
<guid isPermaLink="false"></guid>
<georss:point>46.9490403456011 7.440057740911321</georss:point>
</item>
<item>
<title>Bahnhof Bern</title>
<description>@ Bahnhof Bern</description>
<link>https://foursquare.com/emeidi/checkin/</link>
<pubDate>Fri, 27 Sep 13 17:21:57 +0000</pubDate>
<guid isPermaLink="false"></guid>
<georss:point>46.94901360281286 7.440102388543716</georss:point>
</item>
<item>
...

PHP

...
foreach($xml->channel->item as $item) {
    $coords = $item->children('http://www.georss.org/georss')->point;
}
...

Via: SimpleXML and namespaces

Tags: , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Freitag, 30. August 2013

urllib unter Python 3 Debug-Meldungen entlocken

Im Grunde ganz simpel – vorausgesetzt, man weiss wie (angelehnt an how to debug a urllib.urlopen in python!, wo aber noch mit urllib unter Python 2 gearbeitet wird):

import urllib.request
import http.client

http.client.debuglevel = 1
http.client.HTTPConnection.debuglevel = 1

url = 'http://www.eMeidi.com/?action=hack'
headers = {}

Request = urllib.request.Request(url,None,headers)
response = urllib.request.urlopen(Request)	
html = response.read()

Dies produziert Ausgaben in folgender Form:

send: b'GET http://www.eMeidi.com/?action=hack HTTP/1.1\r\nAccept-Encoding: iden
tity\r\nHost: www.eMeidi.com\r\nUser-Agent: Python-urllib/3.3\r\nConnection: clo
se\r\n\r\n'
reply: 'HTTP/1.0 407 Proxy Authentication Required\r\n'
header: Date header: Proxy-Authenticate header: Proxy-Connection header: Content
-Type header: Content-Length

Tags: , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 14. August 2013

URLs mit Python dekodieren

War gestern auf der Arbeit sehr handlich, um URL-kodierte GET-Parameter wieder lesbar zu machen:

$ python
>>> import urllib.parse
>>> urllib.parse.unquote('http://domain.tld/index.php?t=12%2F7%2F2013%2011%3A19%3A40%201%20-120&ce=UTF-8')
'http://domain.tld/index.php?t=12/7/2013 11:19:40 1 -120&ce=UTF-8'

Tags: , , , ,
Labels: Programmierung

Keine Kommentare | neuen Kommentar verfassen

Mittwoch, 22. Mai 2013

Konkurrenz-Probleme in PHP

In den letzten Wochen habe ich intensiv an meinem Raspberry Pi-Dashboard für unser Apartment geschraubt und gebastelt. Verschiedene Tiles werden mittels AJAX-Requests, welche über jQuery abgesetzt werden, mit Inhalten versorgt — und dies regelmässig alle paar Minuten.

Ein mysteriöser Bug machte mir in den letzten Tagen das Leben schwer: Es konnte vorkommen, dass zwei Tiles mit den Aktienkursen desselben Unternehmens gefüllt wurden, obwohl die Tile-IDs unterschiedliche ISINs trug.

Nach einigen Minuten Debugging schwante mir dann plötzlich, was das Problem war: Der auf dem Server lokal zwischengespeicherte HTML-Cache war zwar in Dateien mit der richtigen ISIN im Dateinamen abgelegt — der Inhalt war aber identisch. Konkret enthielt die HTML-Datei über das Unternehmen AAPL die Daten für das Unternehmen GOOG.

Wie sich heraustellte, hatte ich mein Script nicht dermassen intelligent programmiert, dass es mit mehreren, nur wenige Millisekunden auseinanderliegenden Requests ordnungsgemäss umging. Um nämlich den Aktienkurs einer ISIN abzufragen, muss dieser zuerst über einen HTTP-Request an ein Suchscript meines unfreiwilligen Kursanbieters in eine vom Anbieter intern verwendete NOTATION_ID umgewandelt werden (was wissbegierigen Lesern dieses Blogs einen Hinweis gibt, von welcher Web-Site ich die Kursdaten beziehe). Erst danach kann ich die Informationsseite des auf dem Aktienmarkt gehandelten Unternehmens aufrufen, herunterladen und im lokalen Dateisystem ablegen.

Konkret war mein Fehler, dass ich die Suchergebnisse zur Umwandlung der ISIN in die NOTATION_ID in eine Cache-Datei mit statischem Dateinamen ablegte. Da die Requests mit wenigen Millisekunden Versatz beim Server ankamen, war die Cache-Datei manchmal bereits von einem späteren Request überschrieben worden, als ich den HTML-Code endlich in PHP einlesen wollte (ich denke, dass wir hier von einigen wenigen hundert Millisekunden sprechen). Dies führte dazu, dass ich einer ISIN fälschlicherweise die NOTATION_ID des nachfolgenden Requests zuwies und diese plötzlich für zwei Kursanbieter galt. Da ich dieses Mapping aus Performance-Gründen ebenfalls serialisiert in einer Cache-Datei ablegte, wurden ab diesem Zeitpunkt zwei Tiles mit identischen Inhalten befüllt.

Die erste Lösung, dem Dateinamen einen sich ständig ändernden Wert einzupflegen, scheiterte grandios, weil ich time() verwendete. Da die Requests innerhalb der selben Sekunde auf dem Server eintreffen konnten, konnte die Cache-Datei im schlimmsten Fall weiterhin vom nachfolgenden Request überschrieben werden. Die zweite Lösung war deshalb, durch das Einfügen einer mittels rand(1000,9999) definierten Zufallszahl einen wahrlich zufälligen Wert in den Dateinamen einzuschleusen. Die Chancen, einen identischen Cache-Namen zu erwischen, ist hier 1:10000 — good enough, würde ich sagen.

Natürlich hätte ich auch einfach die ISIN in den Namen der Cache-Datei aufnehmen können. Hier hegte ich aber Zweifel, weil ich nicht wollte, dass bei einem Downloadfehler einfach unbemerkt die bestehenden, veralteten Daten eingelesen würden.

Tags: , , , , , , ,
Labels: Programmierung

1 Kommentar | neuen Kommentar verfassen