Ijsjes in de regen

Paraplus begeleiden mensen haastig naar hun bestemming terwijl ik met een glimlach op een bankje gezeten de hulp van een afdak geniet om me te beschermen tegen het hemelvocht. De regen is wel erg tegenwoordig voor wat een zomerse dag in juni zou moeten zijn. Soms vraag ik me af of daar een reden voor is. Dan bedenk ik me dat dat vast een straf is voor mensen die verbitterd over de aardkorst lopen. Een teken dat het best wat vrolijker mag.

Het raam van de gesloten broodjeszaak weerspiegelt onze gedaanten. “Kijk, dat zijn wij”, zeg ik nogal overbodig. Je glimlacht. De weergoden moeten harder hun best doen om jou te misnoegen. Mijn weerspiegeling drukt de jouwe tegen zich aan.

Een man draagt op handige wijze twee ijsjes in zijn linkerhand en een paraplu in de rechter. Ik schat hem een jaar of zeventig. Hij verlaat het ijssalon op de hoek en wandelt voorbij, terug naar de gezelligheid waar zijn levensgezel zit te wachten en waar hij straks met een dichtgevouwen, lekkende paraplu de huiskamer binnenstapt met de ijsjes in de hand. Het lijkt alsof hij dat al jarenlang alle dagen doet. Terwijl ik hem nakijk, vindt hij mijn blik. Hij glimlacht, knikt en beweegt met zijn linkerhand in mijn richting in een gebaar waarmee hij bevestigt wat ik dacht.

Mijn gedachten dwalen af. Dat geeft niet want we zeggen niets. We wachten tot het stopt.

Wordfeud for PC

For the readers that don’t know, Wordfeud is a word game for Android, iPhone and Windows phones. It’s good, it’s addictive, but it’s limited to phones only. That’s bizarre, to say the least, because it wouldn’t be hard to officially support it on laptops or PC’s. I present the solution: Wordfeud for PC. It’s a simple/plain port from the mobile game to a browser version. It does contain some bugs, it is unfinished, but it’s playable.

Features

An example game on Wordfeud for PC– Log in with your Wordfeud username (no need to re-register if you had registered on your phone)
– Register for new players
– Play against people on phones and computers alike.
– Pick up where you left off if you switch from phone to computer or vice versa.
– Support for Dutch games only (will be upgraded upon request)
– Invite other players by username
– Publish a finished game to Facebook

Lacks

– Other languages than Dutch
– Random board layout support
– A very pretty interface
– Challenge random opponent

PLAY NOW

I’ll be interested to read your comments, suggestions and requests below. Enjoy!

Een adolescente gedachtegang

Volgend gedicht bewaarde ik ettelijke jaren in een van de krochten van mijn portemonnee. Ik weet niet precies hoe lang. Lang genoeg om te vergeten wat mijn drijfveren waren. Het schept in ieder geval een beeld van mijn adolescente gedachtegang.

Uw aller goedheid doet mij deugd
Maar toch ontbreekt mij nog de vreugd’
Die ik mezelf niet toewens

Het is misschien niet echt mijn recht
Maar toch betreur ik zeer oprecht
Gevoelens die ik koester

Daarom betwijfel ik altijd,
Dat rouw gevoel dat mijn hart slijt,
Of ik je lief mag hebben 

Als u denkt met koolstofdatering te kunnen achterhalen hoe oud het schrijfsel is, mag u mij contacteren.

Download Flickr Photos

If you were looking for an easy way to download a bunch of Flickr photos, have a look at the perl script I wrote for exactly that purpose.

Features

  • Can download from photo stream as well as sets
  • Downloads photos in maximum available resolution
  • Saves downloaded photos to a hierarchically structured folder in the same directory as the script
  • Works on Linux, can be modified to work on Windows/Mac

Example Use

The following will download all photos in maximum resolution from the user Aspyrantis’ photostream.

$> ./download-flickr-set.pl
*** DOWNLOAD FLICKR SET 1.0 ***
By Pieter Hiele

Enter the URL of the Flickr set you wish to download (e.g. http://www.flickr.com/photos/aspyrantis/sets/72157626467366362/).
URL: http://www.flickr.com/photos/aspyrantis/

Pictures: 44
Pages: 3

Downloading http://farm8.staticflickr.com/7055/6970675053_73c1593807_b.jpg
Downloading http://farm8.staticflickr.com/7061/6824545696_541018cec0_b.jpg
Downloading http://farm8.staticflickr.com/7210/6964900687_a91818dd31_b.jpg
Downloading http://farm8.staticflickr.com/7203/6964894589_7edf7c37d0_b.jpg

$> ./download-flickr-set.pl
*** DOWNLOAD FLICKR SET 1.0 ***
By Pieter Hiele

Enter the URL of the Flickr set you wish to download (e.g. http://www.flickr.com/photos/aspyrantis/sets/72157626467366362/).
URL: http://www.flickr.com/photos/grandvelasrivieramaya/sets/72157625876516828/

Pictures: 150
Pages: 3

Downloading http://farm6.staticflickr.com/5161/5375897713_466604a009_z.jpg

Code
Note! The script has only been tested very superficially and on a linux machine.

#! /usr/bin/perl
use LWP::Simple;
use POSIX ();

my $aantal = 0;
my $pages = 0;

system("clear");
print "*** DOWNLOAD FLICKR SET 1.0 ***\nBy Pieter Hiele\n\n";
print "Enter the URL of the Flickr set you wish to download (e.g. http://www.flickr.com/photos/aspyrantis/sets/72157626467366362/).\nURL: ";
$url = <>;
chomp($url);

$set = get($url);

$set =~ /<div class=\"Results\">\((\d+)/;
$aantal = $1;

while($set =~ /data-track="page-(\d+)/g) {
$pages = $1;
};
print "Pictures: $aantal\n";
print "Pages: $pages\n";
print "\n";

#parse page 1
parse_page($set);

#parse other pages
for($i=2;$i<=$pages;$i++) {
parse_page(get("$url?page=$i"));
}

sub parse_page {
my ($content) = @_;
while($content =~ /<a data-track=\"photo-click\" href=\"(.*?)\"/g) {
@parts = split("\/in\/",$1);
$original = get("http://www.flickr.com".$parts[0]."/sizes/o/in/".$parts[1]);
$original =~ /<img src=\"http:\/\/farm(.*)\.jpg/;
print "Downloading http://farm$1.jpg\n";
system("wget http://farm$1.jpg -P $url -a logfile.txt");
}
}

Comments

Please leave your feedback in the comments below.

Winters Geluk

Ik heb nog geen sneeuw gezien. Het is half januari en ik heb nog steeds geen sneeuw gezien. De schoenen die ik in het najaar kocht met een aangekondigde horrorwinter in gedachten staan te verkommeren in een kast op mijn kot. Onaangeroerd. Ik had het me anders voorgesteld.

Het heeft enkel wat geregend op de avond dat ik opnieuw mijn geluk toets aan de tweeënvijftig kaarten in het pak. Torens chips stapelen zich voor mij op en ik verkneukel me om het ongeluk van de enkelingen die mij bij aanvang van het kansspel toejuichten omdat ik voor hen sowieso winst beteken. De melkkoe noemden ze me. De melkkoe. Dat ligt nu wel even anders.

Het is niet dat ik sta te springen om me door een halve meter sneeuw te worstelen. Maar de romanticus in mij heeft nood aan extrema om zich in te wentelen en wil ‘s winters weemoedig uit het raam kunnen staren terwijl ie zich een zorgeloze kindertijd voor de geest haalt en een viool een hartverscheurend thema door de boxen strijkt. Vroeger was immers alles beter.

Ik heb een winstformule uitgedacht. Ik kan niet falen. Met wat voorbarig bluffen rijf ik de ene pot na de andere binnen. Minder gelukkige tegenstanders kopen zich opnieuw in en lachen groen wanneer ik hen wijs op de ijdele verwachtingen die ze langzaamaan als voor werkelijkheid hielden. Ik kan niet falen. Ik heb een winstformule uitgedacht.

Vroeger sneeuwde het nog in de winter. Ik weet het zeker. Als het erg gevroren had, zochten we veel te lang naar mutsen, sjaals en wanten en gingen we vervolgens sleeën in het park of waagden we ons op het angstaanjagend krakende ijs. Er werd al eens een traan gelaten wanneer dat ijs onzacht in aanraking kwam met een onbesjaald stuk aangezicht wanneer een roekeloze bestuurder de bocht met de slee een tikje verkeerd inschatte, maar een moederzoen later stonden we weer klaar om luidkeels onze beurt op de slee op te eisen. We beseften niet dat het de tijd van ons leven was.

Hoogmoed komt voor de val, dat wist Icarus lang voor Saruman de verkeerde partij koos. Mijn winstformule, mijn stel met was aaneengehechte vleugels moet zichzelf in stand houden in de warmte die zich ontwikkeld heeft in het vertrouwen aan de overkant van de tafel. Vier tegenspelers zijn uitgeschakeld. Dit is een gevecht van één tegen één. Dit is een gevecht waarin ik mezelf moet bewijzen tegenover de ervaren vijand: de meester die zijn leerling tegenkomt.

Ik kan me niet herinneren hoe vaak ik voor de verwarming heb gezeten met mijn voeten tegen het metaal in een poging het gevoel in mijn tenen terug te winnen. Een sneeuwman maken is leuk, maar het werd pas echt leuk wanneer we met z’n vieren achter elkaar aan liepen met zoveel mogelijk sneeuwballen in onze armen en zo weinig mogelijk sneeuw in onze nek. Meestal zaten we tien minuten later met rode wangen en een kop dampende chocomelk terug binnen om af en toe een blik te werpen door het raam waarachter de dunne vlokken in een goedgeorganiseerde poging trachtten ons het zicht te ontnemen op de sneeuwman die we net hadden gemaakt.

Ik ken mijn zwakte. In het licht van de overwinning ben ik bereid risico’s te nemen. Risico’s die in het verleden in mijn nadeel zijn beslecht. Ik denk niet meer aan verliezen wanneer het vooruitzicht van de roem en de teloorgang van schampere ervarenen binnen handbereik ligt. Ik neem kansen. Ik bluf. Ik verlies. De geschiedenis herhaalt zich.

De oranje wortel lag op de grond en probeerde zich te onderscheiden van de witte smeltende sneeuw waardoor ze werd omringd. Naar school gaan werd weer mogelijk zonder het uitglijden in het gladde zog van een voorzichtige moeder die haar kinderen een begaanbare weg baande over de straten.

Met moeite onderdruk ik het wrange gevoel dat ik voel na de wanhopige poging die leidt tot mijn zoveelste ondergang. Stoppen op mijn hoogtepunt. Dat moet ik nog leren.

Ik wou dat het sneeuwde, dan kon ik buiten spelen.

Ubuntu Playing No Sound and Speeded Video

Having trouble with audio and video playing too speedy in Ubuntu after an upgrade? Here’s a very easy solution I finally found after much of needless deleting, reinstalling and replacing software packages.

If your problem is the same as mine, meaning Youtube plays movies too fast and no sound can be heard, the problem are settings of pulseaudio. The solution is simple.

rm -r ~/.pulseaudio

Enjoy!

Rome, een dagboek

Dinsdag 19 juli
11:59

Ik ben al drie uur op de luchthaven. Vertrokken zonder veel vooruitzicht en in de veronderstelling dat ik wel in Rome zou raken, begaf ik me naar Leuven station voor een enkeltje Brussel Nationaal.

Brussels Airlines is te duur: “Wij doen niet aan last-minute.” Mij best, denk ik, en Connections redt de dag. Ik reis alleen, dus ben aan mezelf overgeleverd, wat naar alle waarschijnlijkheid de eerste keer is. Ik lieg als ik zeg dat ik het niet een beetje spannend vind. Ik kijk op mijn rechterkant uit op vier paar roltrappen die drie verdiepingen met elkaar verbinden, linksvoor zie ik een Quick, waarvan ik me afvraag of ik daar straks wél zin in zal hebben. Aan de overkant van wat men “De Luchtweg” heeft gedoopt, gooit een meisje uit verveling herhaaldelijk haar flesje omhoog, dat ze vervolgens deskundig terug opvangt. Omdat Seekers louter fictief zijn, verdenk ik haar enkel van een gezonde motoriek maar wacht ik toch geduldig tot ze haar flesje per abuis over de balustrade keilt om zo een nietsvermoedende roltrapreiziger iets sneller naar beneden te doen rollen dan gepland.

Honger begint te komen, inspiratie op. Nog lang geen tijd om te boarden, ik vlieg pas om 14:40 naar Amsterdam. Bill Bryson schreef mijn tijdverdrijf in 2003 en noemde het “A Short History of Nearly Everything”. Ik ben hem er dankbaar voor.

16:25

Een vertraagde vlucht later, ben ik in Schiphol gestrand. Geen tijd om rond te kijken, boarding start om 16:05. Dat was voordat het vliegtuig zoek was, of iets dergelijks, want waarom zou men anders plots van vliegmasjien veranderen? Gelukkig is Schiphol rijkelijk bezaaid met wegwijzers en vond ik bijgevolg gemakkelijk de grond waarop ik zit. Langs mijn zijde berispt een moeder, geheel herkenbaar en met een volmaakt Engels accent, haar oudste dochter omwille van het plagen van haar zus. Ik hoop dat ik hier goed zit, maar dat zal wel want ik hoor een taal die ik niet ken en dus waarschijnlijk Italiaans is. Wachten tot alles zichzelf oplost. Als mijn bagage meereist, vind ik het allemaal best.

16:57

De vlucht is ondertussen delayed tot 18:30, maar goed dat ik niet gehaast ben. Kinderen amuseren zich door een pingpongbal naar hun vader te ketsen, een ander zet uit verveling een roltrap in gang. De grote mensen staren wezenloos voor zich uit of gooien ongeduldige blikken op hun horloge of het informatiebord. Als ik wist hoe lang de vlucht naar Rome duurt, zou ik het me niet zitten afvragen. In de hoofdgebouwen van Schiphol klonken rustgevende muziek en natuurgeluiden. Deze vleugel vergaten ze. Ik wacht. Krijg nou wat, die roltrap werkt in beide richtingen. Niet tegelijkertijd, natuurlijk, we zijn tenslotte Schrödinger niet.

20:10

Het is koud op het vliegtuig, zo getuige een oude man met een muts. De arme dame aan het raam links van me begon de vlucht met een kruisteken, waarna het tafeltje aan de zetel voor haar los kwam en haar nagenoeg in het gezicht sloeg. Je mag daar niet mee lachen, maar het is moeilijk. God is een kleine sadist.

De piloot roept om dat het in Rome nog steeds 26 graden is. Laat me hopen dat dat over een halfuur nog steeds het geval is, want ik moet op zoek naar onderdak. De negen herbergen die ik vanuit Schiphol opbelde konden overnachting noch soelaas bieden. Het ziet er naar uit dat mijn eerste nacht er één op hotel zal worden. Gelukkig heb ik niets tegen luxe.

Wat turbulentie en een bloedneus later, (ik vermoed geen causaal verband,) zetten we de landing in, langs rechts vergezeld door een ondergaande zon die onze linkerzijde van een verticale regenboog voorziet.

00:47

Ik besef wat het nut is van een tweepersoonsbed voor een vrijgezel: je kan gaan slapen zonder eerst je bed te moeten ontruimen. Ik lig poedelnaakt op bed in een hotelkamertje dat veel te duur is, maar het mag er ook wel wezen. Ik ben in ieder geval begonnen met mijn centen er terug uit te halen: een vol bad, badschuim incluis. Morgen op zoek naar een goedkoper alternatief, maar pas na mijn uitgebreid ontbijt van 7 tot 10.

Woensdag 20 juli
10:41

De schaduw van het loof beschermt me tegen de zon. Ik zit op een bankje in Giardini del Quirinale en zit daar wel lekker. Mijn volgende vier nachten zijn verzekerd van onderdak en dat voor een prijs waarvoor ik op mijn laatste verblijfplaats nog geen twee nachten zou kunnen verblijven. Allemaal goed, allemaal wel.

Dorst, drieëntwintig graden. Dat belooft. Ik kan deze namiddag pas inchecken en socializen dus ben ik aan het solotrippen door deze mooie, vuile stad waar chauffeurs een claxonreflex hebben die sterker is dan hun remreflex. De wind steekt op en toont me waar ik heen moet.

Donderdag 21 juli
15:46

Mijn voeten laten zich voelen, ik zit in de bar van de herberg met een Duvel voor mij. Waarom iets nieuws proberen als het oude zo goed is? Dat strookt niet helemaal met de ingesteldheid die je verwacht van een alleenreiziger, maar dat kan me niet schelen. Ik geniet van een gulden slok goud.

Slecht geslapen, veel te warm, een laatste keer wakker geworden om 7u. Om halfnegen de stad ingetrokken met drie Amerikaanse europavaarders. Rome heeft zich aan me prijsgegeven hoewel ik me als een toerist gedroeg.

Of ik moe ben, wordt me gevraagd. Valt het dan zo op? Aan siësta’s doe ik principieel niet mee, maar het zou welkom zijn. Het is hier rustig. Nu wel. Vanavond verzamelt de voltallige Roomse bevolking zich hier weer om de boel op stelten te zetten. Dat is prettig, want ik doe sociaal. Het gevoel dringt zich aan me op dat ik geen zinnen meer kan vormen. Zonneslag of niet, hier laat ik het bij, maar echt niet omdat ik een siësta nodig heb.

Vrijdag 22 juli
14:44

Een schoen onder mijn stoel, de ander om mijn voet. Ook hier staken soms treinen, geen zee vandaag. Vanmorgen gewekt door een herbergmedewerkster die me aanspoorde uit te checken (omdat ik ook morgen weer in een andere kamer slaap, het gevolg van niet gereserveerd te hebben). Het was elf uur, dus ik was blij dat ze nog beleefd was. Zware avond gisteren, trage start vandaag. Twee uur rondgehangen op het station, wachtend op een trein die nooit kwam. Het duurde even voor de betekenis van soppresso tot ons doordrong. Zoals het elke gezonde jongeman betaamt, hang ik na die teleurstelling door te zakken aan de toog. Misschien zwem ik straks, als de hoofdpijn is gaan liggen.

Zaterdag 23 juli
10:44

“Thank you,” en vijf euro wisselgeld. Geen slecht ontbijt, zo’n egg and bacon sandwich, maar de capuccino heb ik beter gehad. Me afvragend wat Rome te bieden heeft als het nat en miezerig is, ga ik mijn tijdverdrijf halen dat boven naast mijn bed ligt. Opnieuw bedankt, Bill.
Ontbijt, nieuwe gezichten. Regen op straat. Mijn laatste volle dag, ik ben een plakker want de meesten verbleven hier minder lang dan ik. Nog wat socializen, de dag ietwat proberen vullen.

14:50

Van Morrison zingt Moondance, ik eet. Ieder doet waar ie goed in is. Hij neemt me terug naar vorige week, toen ik August Rush zag: een film die me door vrienden was aangeraden en mede verantwoordelijk was voor het feit dat ik me hier bevind. In het algemeen een aanrader. Inspirerend en ontroerend, ik laat graag eens een traan op die manier.

Met wat Engelsmannen (die hier trouwens dunbezaaid zijn, net zoals Belgen — ik heb nog maar één Vlaams gezin gehoord sinds ik hier ben) en twee Aussies (die daarentegen in statistisch ongebruikelijke proporties aanwezig zijn) nogmaals de Roomse binnenstad bezocht, die ik onderhand wat begin te kennen. Zonder kaart de groep rond de stad gidsen, het schept voldoening. De zon liet ons niet volledig in de steek.

Deze perzik is hard, maar toch zo goed als op, de rest gooi ik weg. Ik kom eraan, Bill.

Zondag 24 juli
09:22

Te vroeg opgestaan, uitgebreid ontbijt. Pas om 10u richting Stazione Termini, dus tijd om wat te schrijven. Een reizend drietal zit aan mijn tafel erg onreizig te wezen. Ze misten hun vroege trein en wachten drie uur op de volgende waar plaats op is. Tegenvaller, jongens. Een van hen slaapt, een ander zucht, de derde maakt de eerste wakker om naar de supermarkt te gaan. Ik wijs hen de weg.

10:00

Een jongedame schrijdt voorbij, mannenhoofden volgen haar tred. De uurregeling laat weten dat mijn trein naar de luchthaven vertrekt over 22 minuten. Geen dag te vroeg, blijkbaar: stapelwolken stapelen hun wollige wolkheid dicht opeen hoog in de lucht die ik tussen twee overdekte perrons kan zien. De temperatuur is gedaald. Rome rouwt om mijn vertrek en ik begrijp haar.

10:59

De Leonardo Express, noemen ze hem, de trein naar de luchthaven. Nu weet ik niet wat ‘express’ betekent in het Italiaans, maar ik vermoed niet dat het iets te maken heeft met traagheid of vertragingen. Mijn vlucht vertrekt over twee uur, dus het mag wel eens gaan opschieten. Ik maak me niet druk, want het is (nog steeds) vakantie.

11:55

De expresstrein blijkt ook nog eens te boemelen in elke tussenhalte. Ik sta sardienachtig in de wandelgang van de trein, mensen zweten rondom mij. Volgende halte is luchthaven. Mijn vlucht vertrekt over een uur, dit wordt lekker spannend.

12:47

Nog 7 minuten voor de vlieger vertrekt. Ik zit er juist op. Persoonlijk aangesproken en opgevangen aan gate B07 (“you are mister Hiele?”), werd me met de glimlach verteld dat ik niet eens de laatste was en mijn excuses dus overbodig waren. Scheenbeenspierpijn die ik nog lang zal voelen, ik ben nooit een loper geweest. De arme jongeman naast me is vast niet erg te spreken over de gevolgen die zo’n loopwedstrijd voor mijn lijfgeur met zich mee draagt. Het spijt me, maar niet mijn schuld. De Italiaanse spoorwegen doen er onze NMBS uitzien als een eersteklas klantvriendelijk bedrijf.

Geen tijd gehad om mijn lege flesje nog te vullen, mijn droge keel huilt. Geldt het noodzakelijke uitschakelen van de mobiele telefoon ook voor smartphones, of is vluchtmode toch voldoende? Ik steek ‘m in elk geval weg.

16:07

Terug in Nederland, en dat merk je. Het regent grijzigheid en er valt alleen Heineken te krijgen. Bedankt maar nee bedankt. Nog een dik halfuur luisteren naar de artificiële stem die om de 5 seconden met een verschrikkelijke “mind your step” waarschuwt voor het einde van de rollende voetgangersvloer en ik ben weer onderweg naar Brussel.

17:58

De trip zit erop. Zo meteen vertrekt deze trein naar Leuven en ligt de Romereis voor altijd achter me. Ik dank iedereen die ik ontmoet heb op mijn reis en ook iedereen die het onbegrijpelijke doorzettingsvermogen kon opbrengen om deze nagenoeg onafgebroken stroom van woorden van het begin tot het einde te trotseren. U was een fantastisch publiek.

Java HTTP Wrapper

Below is the source of two Java classes I wrote a while ago. The purpose of these classes is to make HTTP web requests in Java by means of a HttpWrapper object. The Cookie class is used by the HttpWrapper class to store cookies per HttpWrapper object (rather than per domain, which would be advisory). A simple example of use:

HttpWrapper wrapper = new HttpWrapper();
wrapper.get("http://www.google.com/");
assert(wrapper.getHtml().contains("Google"));

I’m aware that it is flawed, both in logic as well as in security (cookies are sent cross-domain, see above), but the goal of all this is to allow Java programs to mimic some basic browser behavior where wanted. That is also the reason that this class spoofs the Mozilla user-agent headers.

Feel free to use and modify.

Download the jar file here.
Find the documentation here.

HttpWrapper.java

package honoki.web;

import java.io.*;
import java.net.*;

/**
 * This class provides an environment to easily make web requests (both GET and POST) to a server.
 * It supports the use of cookies and proxies.
 *
 * @author Pieter Hiele
 * @version 2.0
 */
public class HttpWrapper {

  /**
   * Default constructor of a HttpWrapper.
   */
  public HttpWrapper() {
    cookie = new Cookie();
    html = "";
    this.noProxy();
  }

  /**
   * A method to get the content of a given page, with a given referer.
   *
   * @param url
   * 			The given URL.
   * @param referer
   * 			The given referer.
   * @throws IllegalStateException
   * 			Whenever an IO-related problem occurs.
   * @post
   * 			new.getHtml() will return the headers and content of the given URL.
   */
  public void get(String url, String referer) {
    try {
      URL url_ = new URL(url);
      HttpURLConnection conn;
      conn = (HttpURLConnection) url_.openConnection(getProxy());
      conn.setRequestMethod("GET");
      conn.setAllowUserInteraction(false);
      conn.setDoOutput(false);
      conn.setInstanceFollowRedirects(false);

      conn.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0");
      conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
      conn.setRequestProperty("Accept-Language", "en-us,en;q=0.5");
      //conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
      conn.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
      conn.setRequestProperty("Keep-Alive","300");
      conn.setRequestProperty("Connection", "keep-alive");
      conn.setRequestProperty("Referer", referer);
      if(!cookie.toString().isEmpty()) conn.setRequestProperty("Cookie", cookie.toString());

          // Get response-headers
          String headers = "";
          for(String key: conn.getHeaderFields().keySet())
          	headers += ((key != null)?key + ": ":"") + conn.getHeaderField(key) + "\n";

      // Get content
      BufferedReader d = new BufferedReader(new InputStreamReader(new DataInputStream(conn.getInputStream())));
      String result = "";
      String line = null;
      while ((line = d.readLine()) != null) result += line +"\n";
      d.close();

      cookie.setCookies(conn);
      setLastPage(url);

      setHtml(headers + "\n" + result);
    } catch (IOException e) {
      throw new IllegalStateException("An IOException occurred:\n"+e.getMessage());
    }
  }

  /**
   * A method to get headers and content of a given URL without specifying the referer.
   *
   * @param url
   * 			The given URL.
   * @throws IllegalStateException
   * 			Whenever an IO-related problem occurs.
   * @effect
   * 			| get(url, getLastPage())
   * @see
   * 			{@link #get(String, String)}
   */
  public void get(String url) throws IllegalStateException {
    get(url, getLastPage());
  }

  /**
   * Post the given postdata to a given url with a given referer, and return the response of the server.
   *
   * @param url
   * 			The given URL.
   * @param postdata
   * 			The given postdata.
   * @param referer
   * 			The given referer.
   * @throws IllegalStateException
   * 			Whenever a IO-related problem occurs.
   * @post
   * 			new.getHtml() will return the response of the web server on this request.
   */
  public void post(String url, String postdata, String referer) throws IllegalStateException {
    try {
      URL url_ = new URL(url);
      HttpURLConnection conn = (HttpURLConnection) url_.openConnection(getProxy());

      conn.setRequestMethod("POST");
      conn.setAllowUserInteraction(false);
      conn.setDoOutput(true);
      conn.setInstanceFollowRedirects(false);

      conn.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0");
      conn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
      conn.setRequestProperty("Accept-Language", "en-us,en;q=0.5");
      //conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
      conn.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
      conn.setRequestProperty("Keep-Alive","300");
      conn.setRequestProperty("Connection", "keep-alive");
      conn.setRequestProperty("Referer", referer);
      if(!cookie.toString().isEmpty()) conn.setRequestProperty("Cookie", cookie.toString());
      conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
      conn.setRequestProperty("Content-Length", Integer.toString(postdata.length()));

      // Write postdata
      OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
          wr.write(postdata);
          wr.flush();
          wr.close();

          // Get response-headers
          String headers = "";
          for(String key: conn.getHeaderFields().keySet())
          	headers += ((key != null)?key + ": ":"") + conn.getHeaderField(key) + "\n";

          // Get content
      String result = "";
      String line = null;
      BufferedReader d = new BufferedReader(new InputStreamReader(new DataInputStream(conn.getInputStream())));
      while ((line = d.readLine()) != null) result += line +"\n";
      d.close();

      cookie.setCookies(conn);
      setLastPage(url);

      setHtml(headers + "\n" + result);
    } catch (IOException e) {
      throw new IllegalStateException("An IOException occurred:\n"+e.getMessage());
    }
  }

  /**
   * A method to get the response of the web server after requesting the given URL without specifying the referer.
   *
   * @param url
   * 			The given URL.
   * @param postdata
   * 			The given postdata.
   * @throws IllegalStateException
   * 			Whenever a IO-related problem occurs.
   * @effect
   * 			| post(url, postdata, getLastPage())
   * @see
   * 			{@link #post(String, String, String)}
   */
  public void post(String url, String postdata) throws IllegalStateException {
    post(url, postdata, getLastPage());
  }

  /**
   * Set the value of the last page that was visited.
   *
   * @param lastpage
   * 			The new value of the last page.
   * @throws IllegalArgumentException
   * 			If the given page is null.
   * @post
   * 			| new.getLastPage().equals(lastpage);
   */
  public void setLastPage(String lastpage) {
    if(lastpage == null) throw new IllegalArgumentException("The given page must not be null.");
    this.lastpage = lastpage;
  }

  /**
   * Return the last page that was visited.
   */
  public String getLastPage() {
    return lastpage;
  }

  /**
   * Clear the last page that was visited.
   *
   * @effect	| setLastPage("");
   */
  public void clearLastPage() {
    setLastPage("");
  }

  /**
   * The page that was last visited.
   */
  private String lastpage = "";

  /**
   * Set a custom cookie for this HttpWrapper.
   *
   * @param cookie	The given cookie.
   * @post			| new.getCookie().equals(cookie);
   */
  public void setCookie(Cookie cookie) {
    if(cookie == null) throw new IllegalArgumentException("The given cookie must not be null.");
    this.cookie = cookie;
  }

  /**
   * Return a clone of the Cookie of this HttpWrapper.
   */
  public Cookie getCookie() {
    return cookie.clone();
  }

  /**
   * The cookies of this wrapper.
   * Be aware that the cookies are not stored per domain name, but per instance of a HttpWrapper.
   */
  private Cookie cookie;

  /**
   * Return the html content that this Wrapper has last retrieved from a request.
   */
  public String getHtml() {
    return this.html;
  }

  /**
   * Set the html content of this HttpWrapper.
   *
   * @param html
   * 			The new html content.
   */
  private void setHtml(String html) {
    this.html = html;
  }

  /**
   * The html content of the last page this HttpWrapper requested.
   */
  private String html;

  /**
   * Set a proxy for this HttpWrapper.
   *
   * @param host
   * 			The host of the proxy.
   * @param port
   * 			The port of the proxy.
   * @see
   * 			{@link #noProxy()} to disable the use of a proxy.
   */
  public void setProxy(String host, int port) {
    this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
  }

  /**
   * Disable the use of a proxy for this HttpWrapper.
   */
  public void noProxy() {
    this.proxy = Proxy.NO_PROXY;
  }

  /**
   * Return the proxy of this HttpWrapper.
   */
  private Proxy getProxy() {
    return this.proxy;
  }

  /**
   * The proxy to be used with this wrapper.
   */
  private Proxy proxy;

}

Cookie.java

package honoki.web;

import java.io.Serializable;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;

/**
 * This class describes a cookie. It is designed to work with {@link honoki.web.HttpWrapper}.
 * A Cookie instance of this class does not differentiate between different domains,
 * so the user is expected to handle the cookies appropriately.
 *
 * @author Pieter Hiele
 * @version 1.1
 */
public class Cookie implements Serializable {

  private static final long serialVersionUID = 1L;

  /**
   * The default constructor.
   */
  public Cookie() { }

  /**
   * This constructor allows you to provide a textual cookie that will be stored.
   *
   * @param cookie
   * 			The cookie to be stored.
   */
  public Cookie(String cookie) {
    setCookies(cookie);
  }

  /**
   * Read the cookies from the given connection and save them in this Cookie.
   *
   * @param conn
   * 			The given connection to extract the cookies from.
   */
  public void setCookies(URLConnection conn) {
    int i=1;
    String hdrKey, hdrString, aCookie;
    while ((hdrKey = conn.getHeaderFieldKey(i)) != null) {
      if (hdrKey.equals("Set-Cookie")) {
        hdrString = conn.getHeaderField(i);
        StringTokenizer st = new StringTokenizer(hdrString, ",");
        while (st.hasMoreTokens()) {
          String s = st.nextToken();
          aCookie = (s.indexOf(";") > -1) ? s.substring(0, s.indexOf(";")) : s;
          int j = aCookie.indexOf("=");
          if (j > -1)	cookies.put(aCookie.substring(0, j), aCookie.substring(j + 1));
        }
      }
      i++;
    }
  }

  /**
   * Read the cookies from a string and save them in this Cookie.
   *
   * @param cookie
   * 		A cookie formatted as a string.
   */
  public void setCookies(String cookie) {
    String cookies[] = cookie.split("; ");
    String parts[];
    for(String c: cookies) {
      if(c.contains("=")) {
        parts = c.split("=",2);
        this.cookies.put(parts[0], parts[1]);
      }
    }
  }

  /**
   * Return a textual representation of this Cookie.
   */
  public String toString() {
    String result = "";
    Enumeration keys = cookies.keys();
    while (keys.hasMoreElements()) {
      String key = (String)keys.nextElement();
      result += key + "=" + cookies.get(key);
      if (keys.hasMoreElements()) result += "; ";
    }
    return result;
  }

  /**
   * Clear the current cookie of this wrapper.
   *
   * @post
   * 			| new.getCookies().equals("")
   */
  public void clearCookies() {
    cookies.clear();
  }

  /**
   * Remove the cookie with the given name.
   *
   * @param name
   * 		The name of the cookie to remove.
   */
  public void removeCookie(String name) {
    cookies.remove(name);
  }

  /**
   * The actual data structure to store the cookie parts in.
   */
  private Hashtable cookies = new Hashtable();

  /**
   * Returns a clone of this Cookie.
   */
  @Override
  public Cookie clone() {
    Cookie clone = new Cookie();
    clone.setCookies(this.toString());
    return clone;
  }

}