Archiv der Kategorie: Allgemein

Minibian – Raspberry PI – Installieren

Ziel: ein minimales Linux für einen kompletten (Web)server auf Basis eines Raspberry Pi

Quelle: https://minibianpi.wordpress.com/features/

Software: Win32DiskImagerPuTTY

Hardware: Raspberry Pi 2B (oder 3), SD Mikro Karte 32 GB (oder größer)

Netzwerk: (W)LAN-Router mit LAN-Anschluss

Image auf SD-Karte installieren

  • Download https://sourceforge.net/projects/minibian/
  • Das Image (Endung: img) entzippen und mit Win32DiskImager auf SD-Karte schreiben. Vorsicht, nicht aus Versehen einen falschen Laufwerksbuchstaben wählen.
  • SD-Karte auswerfen, in Raspberry Pi packen
  • Pi per LAN mit Router verbinden
  • Pi mit Strom versorgen und einige Minuten warten, bis der Name im Netzwerk bekannt ist

Mit Maschine verbinden

  • PuTTY starten und mit „minibian“ verbinden.
    • Login: root
    • Passwort: raspberry
passwd # Passwort ändern

Festplatte voll ausreizen

fdisk /dev/mmcblk0 # dann folgende Kommandos
  • p – Partitionen zeigen (i. d. R. zwei) – Startwert der zweiten, der Hauptpartition notieren (hier 125056)
  • d – zweite Partition löschen
  • n – neue Partition anlegen, Partition type: Primary, Partition number: 2, Startwert: 125056 (der notierte, NICHT der default Wert), Endwert: Default für volle Größe
  • p – überprüfen der Eingaben
  • w – schreiben der Partitionen (Fehlermeldung ignorieren)
reboot
resize2fs /dev/mmcblk0p2
df -h # sollte jetzt (nahezu) die volle Größe unter /dev/root/ belegt haben

Vorbereitungen

apt-get install sudo
adduser BENUTZER # besser nicht mit root arbeiten
usermod -aG sudo BENUTZER # packen wir ihn zur sudo-Gruppe
su - BENUTZER # das Minus-Zeichen, damit auch wirklich ein kompletter Wechsel stattfindet
sudo apt-get install nano # bloß kein vi

Bash-Tuning

sudo nano ~/.bashrc # folgendes ans Ende eintragen:

alias +=’pushd .‘
alias ..=’cd ..‘
alias …=’cd ../..‘
alias beep=’echo -en „\007″‚
alias dir=’ls -l‘
alias gzcat=’gunzip -c‘
alias l=’ls -alF‘
alias la=’ls -la‘
alias ll=’ls -la‘
alias ls=’/bin/ls $LS_OPTIONS‘
alias ls-l=’ls -l‘
alias md=’mkdir -p‘
alias o=’less‘
alias rd=’rmdir‘
alias rehash=’hash -r‘
alias rm=’rm -i‘
alias unzip=’tar xfvz‘
alias which=’type -p‘
alias zip=’tar cfvz‘

logout # wieder ausloggen und einloggen, damit das Bash-Tuning anschlägt
su - BENUTZER

Betriebssystem und Komponenten auf den neuesten Stand bringen

sudo apt-get -y update
sudo apt-get -y dist-upgrade # kann dauern
sudo apt-get -y autoremove
sudo apt-get -y autoclean
sudo apt-get install cpufrequtils
cpufreq-info # zeigt Informationen zur CPU an

Firmware updaten

sudo apt-get install rpi-update # nur für Raspberry
sudo rpi-update # nur für Raspberry, neue Firmware
sudo reboot #  neustart, kann etwas länger dauern, bis Rechner wieder gefunden wird

Benchmarks

Diese Befehle benötigt man natürlich nur, wenn man die aktuelle Rechenleistung mit anderen Systemen vergleichen möchte.

sudo apt-get install sysbench
sysbench --test=cpu --num-threads=4 run # CPU Test
sysbench --test=memory --memory-block-size=500MB --memory-total-size=10GB run # Speichertest
sysbench --test=fileio --file-total-size=20G prepare # Lese/Schreibtest benötigt ein File
sysbench --test=fileio --file-total-size=4G prepare #  viermal soviel wie RAM, kann dauern
sysbench --test=fileio --file-total-size=4G --file-test-mode=rndrw --init-rng=on --max-time=300 --max-requests=0 run # Kb/sec-Wert wichtig
sysbench --test=fileio --file-total-size=4G cleanup # und wieder entfernen

Sicherheitsupdates automatisieren

sudo apt-get -y update && sudo apt-get -y install cron-apt # kann dauern
sudo nano /etc/cron-apt/action.d/5-secupdates # neue Datei mit Inhalt:

upgrade -y -o APT::Get::Show-Upgraded=true

dann

sudo mkdir /etc/apt/sources.d
sudo nano /etc/apt/sources.d/security.sources.list # neue Datei mit Inhalt:

deb https://security.debian.org/ wheezy/updates main contrib non-free
deb-src https://security.debian.org/ wheezy/updates main contrib non-free

und

sudo nano /etc/cron-apt/config.d/5-secupdates # neue datei mit Inhalt:

OPTIONS=“-o quiet=1 -o Dir::Etc::SourceList=/etc/apt/sources.d/security.sources.list -o Dir::Etc::SourceParts=\“/dev/null\““

abschließend

sudo nano /etc/cron.d/cron-apt # zeiten anpassen, wenn gewünscht
sudo cron-apt -s # test, kann dauern
sudo cat /var/log/cron-apt/log # prüfen

LAMP

sudo groupadd www-data # eventuell schon vorhanden
sudo usermod -a -G www-data www-data
sudo apt-get install apache2 -y
sudo apt-get install php5 -y
sudo apt-get install mysql-server -y # passwörter für MySQL müssen erstellt werden
sudo a2enmod rewrite # mod_rewrite fuer URL-Manipulation
sudo service apache2 restart
sudo apt-get install php5-mysql phpmyadmin -y # dauert, verlangt MySQL-Passwort (s.o.), für apache2 und lighttpd einrichten

Webmin

sudo apt-get update
cd ~
wget http://prdownloads.sourceforge.net/webadmin/webmin_1.831_all.deb
sudo apt-get -y install perl libnet-ssleay-perl openssl libauthen-pam-perl libpam-runtime libio-pty-perl apt-show-versions python
sudo dpkg --install webmin_1.831_all.deb # dauert lang
rm webmin*l.deb -f
sudo reboot

Test im Browser:

http://minibian/

http://minibian/phpmyadmin (Benutzer: root, Passwort: siehe oben)

https://minibian:10000/

Tweaking.com Windows Repair -Die Software gegen Krebs

Eine reißerische Überschrift. Aber diese Software löst so viele Windows-Probleme auf einen Schlag, dass ich keine besseren Start liefern konnte.

Letzte Woche passierte es mir doch: Nach über 4,5 Jahren (so alt ist mein Windows 7 Ultimate) kam es zu mehreren Katastrophen gleichzeitig: Ein DRAM-Baustein verabschiedete sich schleichend, daraufhin gab es einen Bluescreen (den ersten seit Jahren). Das System fuhr zwar noch hoch, lief aber so instabil, dass ich gezwungen war, mehrere Dienste abzuschalten, denn mir kam leider nicht in den Sinn, dass es an der Hardware liegen könne.

Dann verabschiedete sich auch noch die Windows-Firewall, woraufhin der Virenscanner nicht mehr richtig lief bzw. das ganze System in die Knie zwang: nichts ging mehr; vernünftiges Arbeiten wurde zum Glücksspiel.

Mit jedem Start hakte es bei immer mehr Komponenten, es war wie in einem IT-Horrorfilm: Eben noch war alles ruhig, und nach und nach fielen immer mehr Protagonisten den Zombies zum Opfer.

Hier eine Auswahl der besten Fehlermeldungen in der Ereignisanzeige (die komischerweise noch funktionierte):

  • Der Dienst „Heimnetzgruppen-Anbieter“ ist vom Dienst „Funktionssuchanbieter-Host“ abhängig, der aufgrund folgenden Fehlers nicht gestartet wurde:
    Der Abhängigkeitsdienst oder die Abhängigkeitsgruppe konnte nicht gestartet werden.
  • Der Dienst „WWAN – automatische Konfiguration“ wurde mit folgendem Fehler beendet:
    Zugriff verweigert
  • Der Dienst „Windows-Firewall“ ist vom Dienst „Basisfiltermodul“ abhängig, der aufgrund folgenden Fehlers nicht gestartet wurde:
    Zugriff verweigert
  • Der Dienst „Basisfiltermodul“ wurde mit folgendem Fehler beendet:
    Zugriff verweigert
  • Der Dienst „DHCP-Client“ wurde mit folgendem Fehler beendet:
    Zugriff verweigert
  • Der Dienst „NLA (Network Location Awareness)“ wurde mit folgendem dienstspezifischem Fehler beendet: %%-1073741288.
  • Der Dienst „Netzwerklistendienst“ ist vom Dienst „NLA (Network Location Awareness)“ abhängig, der aufgrund folgenden Fehlers nicht gestartet wurde:
    Der Dienst wurde nicht gestartet.
  • Fehler 1610: Die Konfigurationsdaten für dieses Produkt sind beschädigt. Wenden Sie sich an den Support.

Ein Teufelskreis der Dienste-Knieschüsse

Jeder Windows-Nutzer, der weiß, was Regedit und die Diensteverwaltung ist, kennt solche Meldungen: Ohne Wohnung keinen Job; ohne Job keine Wohnung und für eine Versicherung braucht es beides. Ein Teufelskreis.

Mühselig schaffte ich es mit Hilfe eines zweiten Rechners zumindest das Internet wieder zum Laufen zu bringen. Und bei meiner Recherche fiel dann der Groschen: Konnte es doch die Hardware sein? Nach 30 Jahren in der IT habe ich die Erfahrung gemacht, dass 99% aller Probleme mit der Software zusammenhängen. Auch wenn in jedem Forum spätestens an dritter Stelle der Tipp kommt, mal die Platine oder die Festplatte auszutauschen. Bisher hatte ich erst eine defekte Festplatte – und die hatte sich dank S.M.A.R.T. auch mit Vorankündigung verabschiedet, so das mir genug Zeit verblieben war. Memtest86 brachte es an den Tag, ein erst vor drei Monaten erworbener 8GB-DDR3-Chip war defekt – nachdem die alten ohne Probleme 4,5 Jahre gehalten hatten. Daher der Bluescreen, daher die Abstürze. Also raus damit und den alten Riegel wieder rein, um wenigstens auf der Hardware-Seite einen Fehler ausradiert zu haben.

Mittlerweile war ich schon mit den Nerven fertig, weil ich seit zwei Tagen nicht in der Lage war, vernünftig zu arbeiten. Die weitere Fehlersuche machte es nicht besser.

Wenn alle Tipps nichts helfen: dumme Foren

Besonders geärgert habe ich mich über die typischen Foren in der Amateure ihre schlechten Ratschläge ablassen, egal ob deutsch oder englisch, egal ober gutefrage.net, trojaner-board.de, sysprofile.de, drwindows.de, winheloponline.com oder answers.microsoft.com – überall nur stümperhafte bis schlechte Ratschläge:

  • „… bestimmt ist es ein Trojaner, installier mal neu …“
  • “ … tuneup tools genutzt? Die machen nur Ärger.  Installier mal neu …“
  • „… bestimmt die Festplatte kaputt, SSD taugt sowieso nichts. Installier mal neu …“
  • “ … das lässt sich nicht mehr reparieren. Installier mal neu …“

„Installier mal neu!“ ist das neue „mit Linux wäre das nicht passiert.“

Und zwischendurch die Copy&Paste-Antworten von indischen  Support-Mitarbeitern (leicht zu erkennen an den Namen), die von IT soviel Ahnung haben wie mein Kater von Rechtschreibung. Angereichert mit gut getarnten, locker eingestreuten Werbelinks zu zweifelhaften Reparatur-Programmen, die selbst ich nicht mehr als Werbung erkennen konnte, denn leider war mein Werbefilter auch ausgefallen.

Hier eine weitere Auswahl der abstrusesten Tipps:

  • Linux nehmen und Windows nur noch in einer virtuellen Maschine nutzen
  • einen neuen Rechner kaufen, der alte würde eh nichts mehr taugen
  • irgendeinen der gefühlt hundert Anti-Malware-Tools nutzen
  • mit Regedit am besten allen Nutzern alle Zugriffsrechte gewähren, dann würden die Fehlermeldungen schon verschwinden
  • eine Jungfrau um Mitternacht bei Vollmond an einem Kreuzweg opfern

Zwei Tage spielte ich IT-Ping-Pong: Eintrag in Regedit sichern, ändern, neu starten, testen, rückgängig machen.

Mittlerweile war ich so verzweifelt, dass ich mit dem Gedanken spielte, eine ca. zwei Wochen alte Notfall-Sicherung einzuspielen.  Dann stieß ich auf folgenden Beitrag eines Seelenverwandten: I receive error code 5 when I try to start Windows Firewall.

„… Somebody should paste this tool into the foreheads of microsoft developers, but the hard way. How come some hobby programmers can create such simple tools which beat anything from microsoft developers? …“

Um welches Tool handelte es sich hier? Gemeint war (bzw. ist) Windows Repair von Tweaking.com. Ich bin normalerweise kein Fan von Tuning-, Tweaking- oder Sonstigen Repair-Tools, die meist nur das Blaue vom Himmel versprechen, aber nicht selten mehr Schaden anrichten als sie beheben.

Aber was hatte ich noch zu verlieren? Meine Arbeitsdateien waren in täglichen Backups alle wunderbar extern gesichert. Es galt ja „nur“ mein Windows wieder stabil zum Laufen zu bringen. Also probierte ich die Software aus.

Windows Repair zeigt sich zunächst einmal in einem „Look&Feel“, das technisch irgendwo zwischen Windows 3.11 und Windows 95 hängen geblieben ist. Hässliches grau, hässliche Knöpfe, hässliche Schrift, dafür aber sehr viel Text. Also das genaue Gegenteil von den aktuell angesagt App-Style mit großer Schrift, wenig Text, und 2-dimensionalen Design.

Tweaking.com - Windows Repair

Hässlich und sehr textlastig, so präsentiert sich die Software.

Angeblich soll die Software 45 schwere Macken beheben, u.a.

  • Probleme mit Shortcuts und Verknüpfungen (das kann ich auch allein)
  • WMI
  • Windows Firewall (Hoffnung keimt auf!)
  • Internet Explorer
  • Windows Installer (MSI)
  • Hosts File (vielleicht hat die lmhosts auch was abbekommen?)
  • Policies Set By Infections (es wird wärmer)
  • Winsock & DNS Cache
  • Windows Updates
  • Reset Registry and File Permissions
  • Register System Files

Die Übersicht schien mir vielversprechend. Besonders die Rechtevergabe bzw. die fehlenden oder plötzlich falschen Rechte für einzelne Einträge in der Registry (s.o.) machte mir zu schaffen.

Was mir besonders gefiel: alles wird ausführlich aber nicht mit zuviel Vertriebsprosa erklärt und per Default werden vor jeder Änderung Sicherungen der Registry gemacht.

Der wichtigste Menüpunkt: die Reparatur-Einstellungen

Ich ließ die Software also zweimal laufen, weil sich laut Aussage der Entwickler manche Fehler erst bei einem zweiten Durchgang offenbaren – Fehlerkaskaden halt.

Ich starte also den Rechner wieder, will mich gerade voller Erwartung aber doch mit einer gehörigen Portion Skepsis zurücklehnen, da ist der Rechner schon hochgefahren und online (SSD sei dank). Ein mit zitterigen Fingern gestarteter Blick in die Ereignisanzeige zeigte es mir: alle schlimmen Fehlermeldungen waren verschwunden, alle wichtigen Dienste tatsächlich stabil am Laufen. Die Firewall ließ sich auch wieder aktivieren.

Fazit: Ich bin glücklich

Lange nicht mehr, war ich so verblüfft, angenehm überrascht, ja fast euphorisch. Mein Rechner lief nicht nur wieder stabil, er startete auch wieder so schnell wie lange nicht mehr – wer weiß, welche unerkannten Macken die Software noch ausgebeult hat; ich will es gar nicht wissen.

Ein Software-Werkzeug von Kennern, das viel verspricht und hält!

Und das I-Tüpfelchen: Die Software kostet nichts. Aber man kann sich registrieren lassen und erhält dann noch ein bisschen Bonus-Software dazu.

Noch mein persönlicher Tipp: Wenn die Software geladen bzw. gestartet ist, vom Internet abstöpseln und den Anti-Virenscanner deaktivieren oder zur Not auch deinstallieren, da es sonst passieren kann, das bestimmte Korrekturen unter Kontrolle des Scanners verbleiben und nicht gefixt werden können. Wenn der Rechner wieder stabil ist, kann man das digitale Kondom ja wieder überziehen, bevor es online geht.

Eine simple API für WordPress, Teil 3

Kommen wir nun zum wichtigen Thema: Wie füge ich dem Artikel ein Bild bei?

Auch das wird in vielen Beiträgen im Internet gerne angerissen, aber mal ein paar schnelle, praktikable Lösungen werden selten präsentiert. Dabei ist es nicht so schwer, wenn man weiß, was zu tun ist.

Der Ablauf

Im Grunde müssen folgende Schritte abgearbeitet werden:

  1. Wir erzeugen einen Artikel und merken uns die ID (siehe Teil 2)
  2. Wir laden ein Bild per FTP auf den WordPress-Server
  3. Wir verbinden Artikel und Bild mit Hilfe der Funktionen: wp_generate_attachment_metadatawp_insert_attachment und set_post_thumbnail

Damit das funktioniert, müssen wir unseren Server gleich zweimal bemühen: Beim Erzeugen des Artikels und beim Verbinden des Bildes mit ersterem.

Der Upload per FTP

Keine Angst, das ist per PHP einfacher, als man denkt.

Zunächst benötigen wir auf dem WordPress-Blog einen Zielordner. Den kann man per Konsole oder FTP anlegen:

md /var/www/domain.tld/wp-content/uploads/maschine/

Auf diesen Server werden die Bilder (oder was immer man später mit den Artikeln verknüpfen möchte: ZIP-File, E-Book, PDF etc.) zwischengespeichert. Man sollte darauf achten, dass die Dateinamen dabei immer einzigartig sind, denn sonst werden sie gnadenlos überschrieben. Erreichen kann man das z.B. durch eine Ergänzung mit einer Time-id und einer laufenden Nummer.

Wir erweitern den Client um einige Zeilen:

$ftp_server='domain.tld';
$ftp_user='john';
$ftp_password='secret007';
$remote_folder='/var/www/domain.tld/wp-content/uploads/maschine/';
$local_folder='/var/upload/bilder/';
$file_name='bild.jpg';

$conn_id = ftp_connect($ftp_server);
ftp_login($conn_id, $ftp_user, $ftp_password);
ftp_put($conn_id, $remote_folder.$file_name, $local_folder.$file_name, FTP_BINARY);
ftp_close($conn_id);

Damit wird die Datei „bild.jpg“ vom lokalen Verzeichnis „/var/upload/bilder/“ auf das entfernte Verzeichnis „/var/www/domain.tld/wp-content/uploads/maschine/“ des Servers „domain.tld“ kopiert. Wenn Sie nicht wissen, wie das entfernte Verzeichnis lautet, fragen Sie ihren Provider.

Bevor wir nun an den Client gehen, packen wir dessen Kernaufgabe in eine Funktion, damit wir sie mehrfach benützen können.

function call_server($postdata_array) {
	$username='mueller';
	$password='geheim';
	$serverurl='http://domain.tld/maschine/';

	$postdata = http_build_query(
		array(
			'postdata_string' => serialize($postdata_array)
		)
	);

	$auth = base64_encode($username.':'.$password);
	$header = array("Authorization: Basic $auth",
		'Content-type: application/x-www-form-urlencoded'
		);

	$opts = array('http' =>
		array(
			'method'  => 'POST',
			'header'  => $header,
			'content' => $postdata,
			'timeout' => 5000,  //5 Sekunden
		)
	);

	$context = stream_context_create($opts);
	return file_get_contents($serverurl, false, $context);
}

Diese Funktion sendet ein Array an unseren Server und gibt dessen Antwort wieder zurück.

Die Aufgaben des Servers werden jetzt erweitert. Er kann nicht nur Artikel anlegen, sondern auch Bilder verarbeiten. Das geschieht durch folgende Zeilen:

$filetype = wp_check_filetype( basename( $postdata_array['file'] ), null );
$wp_upload_dir = wp_upload_dir();
$attachment = array(
	'guid'           => $wp_upload_dir['url'] . '/' . basename( $postdata_array['file']), 
	'post_mime_type' => $filetype['type'],
	'post_title'     => preg_replace( '/\.[^.]+$/', '', basename( $postdata_array['file'] ) ),
	'post_content'   => '',
	'post_status'    => 'inherit'
);
$attach_id = wp_insert_attachment( $attachment, $postdata_array['file'], $postdata_array['parent_post_id'] );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
$attach_data = wp_generate_attachment_metadata( $attach_id, $postdata_array['file'] );
wp_update_attachment_metadata( $attach_id, $attach_data );
set_post_thumbnail( $postdata_array['parent_post_id'], $attach_id );

Zeile 1 ermittelt den Mime-Type der Datei (hier: „image/jpeg“), das Array mit den Meta-Daten (Titel, eindeutiger Bezeichner etc.) wird automatisch gefüllt. Natürlich können Sie den Titel des Bildes auch aus den eigenen Daten erzeugen.

Zeile 10 fügt dann das eben hochgeladene Bild der Mediendatenbank des Blogs hinzu. Von nun an finden Sie das Bild dort, aber es ist noch nicht mit einem Artikel verbunden. Das erledigen wir gleich.

Zeile 12 versorgt das Bild in der Mediendatenbank mit den soeben festgelegten Metadaten und legt die einzelnen Vorschaubilder in den verschiedenen Größen an, die in den Medien-Einstellungen von WordPress festgelegt sind.

Zeile 14 macht die Magie: Das Bild wird mit dem Artikel verbunden.

Zusammengefasst

Der Server

<?php
define('WP_USE_THEMES', false);
header('Content-Type: text/html; charset=utf-8');
if (count($_GET)>0) $postdata_array=unserialize(array_shift($_GET));
if (count($_POST)>0) $postdata_array=unserialize(array_shift($_POST));
require('../wp-load.php');

if ($postdata_array['function']=='wp_insert_post') {
	print wp_insert_post($postdata_array['wp_post_array']);
}

if ($postdata_array['function']=='wp_insert_attachment') {
	$filetype = wp_check_filetype( basename( $postdata_array['file'] ), null );
	$wp_upload_dir = wp_upload_dir();
	$attachment = array(
		'guid'           => $wp_upload_dir['url'] . '/' . basename( $postdata_array['file']), 
		'post_mime_type' => $filetype['type'],
		'post_title'     => preg_replace( '/\.[^.]+$/', '', basename( $postdata_array['file'] ) ),
		'post_content'   => '',
		'post_status'    => 'inherit'
	);
	$attach_id = wp_insert_attachment( $attachment, $postdata_array['file'], $postdata_array['parent_post_id'] );
	require_once( ABSPATH . 'wp-admin/includes/image.php' );
	$attach_data = wp_generate_attachment_metadata( $attach_id, $postdata_array['file'] );
	wp_update_attachment_metadata( $attach_id, $attach_data );
	set_post_thumbnail( $postdata_array['parent_post_id'], $attach_id );	
}


exit();
?>

Der Client

<?php
function call_server($postdata_array) {
	$username='mueller';
	$password='geheim';
	$serverurl='http://domain.tld/maschine/';

	$postdata = http_build_query(
		array(
			'postdata_string' => serialize($postdata_array)
		)
	);

	$auth = base64_encode($username.':'.$password);
	$header = array("Authorization: Basic $auth",
		'Content-type: application/x-www-form-urlencoded'
		);

	$opts = array('http' =>
		array(
			'method'  => 'POST',
			'header'  => $header,
			'content' => $postdata,
			'timeout' => 5000,  //5 Sekunden
		)
	);

	$context = stream_context_create($opts);
	return file_get_contents($serverurl, false, $context);
}


$postdata_array=array(
	'function' => 'wp_insert_post', 
	'wp_post_array' => array(
		'post_content' => '<p>Das ist ein <b>Test äöüß</b><br>Und noch eine Zeile</p>',
		'post_title' => 'Meine erste Überschrift',
		'post_status' => 'publish',
		'post_category' => null
		)
	);
$parent_post_id = call_server($postdata_array);

# FTP Upload des Bildes
$ftp_server='domain.tld';
$ftp_user='john';
$ftp_password='secret007';
$remote_folder='/var/www/domain.tld/wp-content/uploads/maschine/';
$local_folder='/var/upload/bilder/';
$file_name='bild.jpg';

$conn_id = ftp_connect($ftp_server);
ftp_login($conn_id, $ftp_user, $ftp_password);
ftp_put($conn_id, $remote_folder.$file_name, $local_folder.$file_name, FTP_BINARY);
ftp_close($conn_id);

$postdata_array=array(
	'function' => 'wp_insert_attachment', 
	'file' => $remote_folder.$file_name,
	'parent_post_id' => $parent_post_id
	);

print call_server($postdata_array);

exit();
?>

Das war ja nicht so schwer, wenn man weiß, wie es geht – aber das gilt wohl für alles im Leben. Im nächsten Schritt lernen wir, unseren Artikel nachträglich mit Daten anzureichern.

Microsoft Word 2016 schluckt die letzte Zeile – mal wieder

Ich ärgere mich gerade maßlos, dass Microsoft es 2016 immer noch schafft, auf dem eigenen Betriebssystem und mit der eigenen Software hundertprozentig zuverlässig Textkopien zu erstellen.

Word 2016 schneidet den letzten Satz ab.

Sebastian Brück, ein befreundeter Journalist, mit dem ich zusammen das Projekt Krimischätze  ins Leben gerufen habe, hat mir einen längeren Text, genauer gesagt eine Abschrift eines Roman von 1926 geschickt. Meine Aufgabe ist, daraus ein E-Book zu gestalten.

Der letzte Satz im Text müsste lauten:

Selig sind die Heimatlosen. Denn ich glaube, sie werden nach Hause kommen.

Zufällig bemerke ich, dass die allerletzte Zeile, bzw. der allerletzte Satz im Dokument abgeschnitten ist. Offensichtlich kann „mein“ Word 2016, das ich im Zuge des Office 2016 Pakets für 10 Euro Monatsgebühr abonniert habe nicht das Word-Dokument meines Freundes öffnen.

fehler

Speichern als RTF und alles ist wieder gut.

Wenn ich die selbe Datei nun als RTF speichere und mit bzw. Libre Office Writer öffne, ist die Zeile auf einmal wieder da.

alles_ok

Woher das letzte kleine „s“ auf einmal herkommt, kann ich auch nicht sagen – wieso auch? – ich weiß ja nicht einmal, wieso Word nach Gusto einfach was abschneidet.

Warum kann Word nicht fehlerfrei Word-Dokumente öffnen und anzeigen?

Jetzt wird es noch bunter und es fehlt immer noch die Conclusio: Wenn ich den Text komplett mit STRG+A markiere und mit STRG+C kopiere, dann in einem reinen Texteditor meiner Wahl (hier natürlich Notepadd++) einfüge, ist der Satz wieder da… zumindest die Hälfte davon

immer_noch_nichts

Was fehlt noch?

Was fehlt noch? Was ist mir in den letzten Monaten und Jahren noch durch die Finger geschlüpft? Ich weiß, dass ich dieses Problem vor einigen Monaten schon einmal hatte, damals mit einem mit Open Office erzeugten Word-Dokument; dasselbe Problem: der letzte Satz war abgeschnitten. Damals habe ich das dem exotischen Format „.doc“ zugerechnet und einer fehlerhaften Exportfunktion von Open Office Writer. Aber nun, was ist nun der Grund?

Ich werde diesen Text mitsamt der Datei an den Microsoft-Support schicken, mal schauen, was denen so dazu einfällt.

Und es gibt Ingenieure, die wollen tatsächlich selbstfahrende Autos zu Millionen auf die Menschheit loslassen!

Search and replace inside zip with PHP

Ever wanted to search and replace inside a zip container with PHP?

# this will replace every 'a' by 'A' inside every html file inside file.zip
replaceinzip("file.zip", '/a/', 'A', "/.+?html/");
exit();

function replaceinzip($zipfile, $search, $replace, $filefilter) {
   
   # extract to random temp folder
   $tempfolder=md5(mt_rand());
   $zip = new ZipArchive;
   if ($zip->open($zipfile) === TRUE) {
      $zip->extractTo('./'.$tempfolder.'/');
      $zip->close();
   } else {
      trigger_error(htmlentities("not a zip error"), E_USER_ERROR);
   }


   # replace in files
   $files=rsearch($tempfolder, $filefilter);
   foreach ($files as $file) {
      $file_content = file_get_contents($file);
      $file_content = preg_replace($search, $replace, $file_content);
      file_put_contents($file,$file_content);   
   }

   # create new temp zip
   $zip = new ZipArchive();
   if($zip->open($zipfile.'_temp', ZIPARCHIVE::OVERWRITE) !== true) {
         return false;
   }
   
   # add files to new temp zip without foldername
   $files=rsearch($tempfolder, "/.*/");
   # add mimetype first to overcome epub checking errors
   $zip->addFile($tempfolder.'/mimetype', "mimetype");   
   foreach($files as $file) {
      $zip_filename = substr($file, strpos($file,'/') + 1);
      if (file_exists($file) && is_file($file) && $zip_filename!="mimetype") $zip->addFile($file, $zip_filename);
   }
   
   $zip->close();
   
   # rename new zip to old zip
   rename($zipfile.'_temp', $zipfile);
   
   # remove temp folder and files
   delete($tempfolder);
   
   return true;
   
}

# http://stackoverflow.com/questions/17160696/php-glob-scan-in-subfolders-for-a-file#17161106
function rsearch($folder, $pattern) {
    $dir = new RecursiveDirectoryIterator($folder);
    $ite = new RecursiveIteratorIterator($dir);
    $files = new RegexIterator($ite, $pattern, RegexIterator::GET_MATCH);
    $fileList = array();
    foreach($files as $file) {
        $fileList = array_merge($fileList, $file);
    }
    return $fileList;
}

#http://stackoverflow.com/questions/1334398/how-to-delete-a-folder-with-contents-using-php/1334425#1334425
function delete($path) {
    if (is_dir($path) === true) {
        $files = array_diff(scandir($path), array('.', '..'));
        foreach ($files as $file) {
            delete(realpath($path) . '/' . $file);
        }
        return rmdir($path);
    } else if (is_file($path) === true) {
        return unlink($path);
    }
    return false;
}