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!

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.

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!

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

}