Internet, this is Edoardo

cat /var/log/life

Packaging Icecast-kh

WatchPeopleCode

I recently got interested in streaming video on the web watching WatchPeopleCode and it’s been a fun experience screencasting, even if it did not work as I expected.

I did already subscribe to a youtube channel that screencasted some programming but watchpeoplecode is more entertaining and interactive, plus you can see interesting projects while being coded!

What I don’t really like is the ubiquity of Flash to stream those screencast, Youtube, Twich and the WatchPeopleCode mediaserver are Flash-only compatible. Basically if you want to stream something live on the web you use Flash.

Live streaming with HTML5

Running a search for live streaming with HTML5 returns some interesting results, like stackoverflow, github projects or wowza forum.

But the most important link is this: the Icecast project.

Icecast is a streaming media server which currently supports Ogg (Vorbis and Theora), Opus, WebM and MP3 audio streams.

Wait!? Theora? Webm? Icecast can do video?

Icecast

Turns out that Icecast can do Webm streaming from 2012 and this is very important because as you can see from theese tables with a single codec we can target the whopping 64.87% of device (both mobile and desktop). It’s a shame that another codec like Theora can do only 48.54% and is not available on Chrome for Android.

So with just one stream we can target a good share of our target, that’s good but we still know nothing about Webm streaming.

Digging in the Icecast docs reveal that is a piece of cake,

1
2
3
4
5
<mount>
        <mount-name>/arewestreamingyet.webm</mount-name>
        <username>yourusername</username>
        <password>yourpassword</password>        
</mount>

and we can do listener authentication and source authentication with another service.

The drawback is: a mount is a user streaming to Icecast, what if you want to add another mount? You must edit the config file, like every time you want to have multiple sources transmitting at the same time.

That sucks.

Maybe you can schedule streamers and just have one source or spin another icecast server when a streamer want to stream. Seems like Icecast is a no go.

Icecast-kh

Icecast is a stable project and it’s moving slow and steady but there is an experimental branch called kh that offer interesting features like wildcard mounts. This would be the end of our problem and we could just configure the serve to use authentication on wildcard mount.

This would change the config to something more like

1
2
3
4
5
6
<mount>
        <mount-name</*/live.webm</mount-name>
        <authentication type="url">
                <option name="stream_auth" value="http://auth.example.org/source.php"/>
        </authentication>
</mount>

Ok good, we are ready. Let’s install the kh branch on our machine.

No packages, just source release, mmmmmm this is not what I expected.

Debian packaging system

After installing from source and configuring and trying the configuration and the streaming part and seeing everything work as expected I had a bad feeling about doing the same work every time I wanted to spin a new Icecast server (like a relay node to serve more user).

Maybe today I can learn something new, like how to make a Debian package.

The best resource I found is this set of slides, it contains most of the information that you need to get started and keep going.

Let’s go!

Prerequisites

Add a deb-src entry to your /etc/apt/sources.list file and run

1
2
apt-get update
apt-get build-dep icecast

Luckily for us the kh branch has the same requirements as the master branch.

Get the sources

The kh branch is published as a source package on Github, download it

1
wget https://github.com/karlheyes/icecast-kh/archive/icecast-2.4.0-kh3.tar.gz

and extract it

1
tar xzfv icecast-2.4.0-kh3.tar.gz

rename the directory to icecast-kh-2.4.0 and the source release to icecast-kh_2.4.0.orig.tar.gz

Scaffold the debian direcetory

Create a directory called debian, there will be placed the files describing the release info and packaging instructions. This is difficult and a time consuming process.

There are a lot of tools available to do this work, just use them

1
2
cd icecast-kh-2.4.0
dh_make

Edit the package metadata

In the previous step the command dh_make generated a bunch of files for us and placed them in the debian directory inside the icecast-kh-2.4.0, those files are the metadata attached to the package.

  • control: the real metadata about the package; deps, name, mantainer, project page
  • rules: how to build the package
  • copyright: info about the license and copyright
  • changelog: what changed from one release to another

you should edit them and place some useful info in there.

Just build it

But what we really care is the package itself, a tool like ansible can take care of installing the packages needed by our icecast-kh package. (the post is getting long so I decided to skip the previous part)

1
debuild -us -uc

If everything has gone as it should now there is a .deb file sitting in the parent directory and you can use that instead of building from sources relaying on a custom shell script.

Conclusion

This was the rigth time to learn something like packaging for Debian and the icecast project was what I needed to get started.

The docs on the Debian wiki are not that exhaustive and the most tedious part was getting the right info and trying what could work.

Dell Inspiron Fixes

Disclaimer: this information have been found on http://forums.fedoraforum.org/archive/index.php/t-304139.html and are here because they are valuable to me.

I have a cheap laptop always with me, a Dell Inspiron 15 (3551 if you are interested) and it comes preloaded with Ubuntu and a Dell repo preconfigured.

Installing Ubuntu Gnome, Fedora or Debian from scratch always resulted in a broken touchpad but now I’m running Debian and since a week has passed without any issue I can say that I won’t change my distro pretty soon,

Switching to Debian fixed these issues:

  1. Random unresponsive lockscreen
  2. Ubuntu locked to 14.04 LTS without guaranteed updates from Dell

Drawbacks:

  1. I could top 11 hours of computing without Wifi, now I can top 6 hours.
  2. The touchpad stopped working
  3. The integrated Bluetooth is not recognised by the kernel

The issue n° 2 is pretty much what locked me to use Ubuntu and what kept exploring Xorg and the world of xorg.cong files in an attempt to fix it.

the synaptic fix

  1. Edit the default Grub file with editor /etc/default/grub. Append to GRUB_CMDLINE_LINUX the string i8042.nopnp, save and exit.
  2. Edit the i2c-hid.conf file with editor /etc/modprobe.d/i2c-hid.conf and append the line blacklist i2c_hid
  3. Edit /etc/modules, add the string synaptic_i2c, save and exit.
  4. Run depmod with depmod -a
  5. Update your system images with update-initramfs
  6. Update Grub with update-grub
  7. Reboot

the bluetooth fix

This work for the Atheros AR9565 chipset.

  1. create a file in /etc/modprobe.d/, I called it ath9k_bluetooth_coexists.conf
  2. copy this line inside the previous file options ath9k btcoex_enable=1, save and exit
  3. run depmod -a
  4. run update-initramfs
  5. reboot

references for the Bluetooth info

Another Time Git Left Me Speechless

Working with Git is an activity that requires an humble approach, everything you do can be wrong in a situation you couldn’t think of and Git pretty much tries to save you from all what may be out there.

To me this behavior resemble pretty well the Rust compiler. Whenever rustc refuses to compile my script there is a reason that is explained in a short line of output and more often than not the compiler output something on the line of “Ehi you want to do A, you can do it like these…”.

Damn compiler, always bitching that I have to change my code, you already know what I want to do and you give hint, do it yourself!

[me every time I forget a Box<T>]

This time I was rebasing the forgotten appcache branch of a project onto the master, all was good, there was a simple merge conflict; open vim, edit, save, git add and a git rebase --continue later I was ready to update the project on the remote repository.

Except that the remote refused to update!

I sit there, the pc on my lap, wondering if using git push --force was a viable way out of the mess I did or if I should really delete the remote branch and restart over when the need to learn how to properly resolve this conflict stepped over.

I know of a page that collect the solutions to this very kind of problems but this time is different, I want to know why Git refuse to do a simple push.

And here I am, lost in the help page of git push, searching for the term fast-forward when I finally see it.

--force-with-lease

--force-with-lease is an option of git push that suits my case perfectly. I have a remote branch remote/appcache and a local branch appcache, I rebased appcache on the local master and now the very same two branches, appcache and remote/appcache are considered a danger because I want to move forward a remote branch.

Basically I have to rebase something already published, something on which someone could have worked! If I update the remote branch and move it to the HEAD of my master branch someone could lose their progress.

Imagine someone was working with the very same branch and I do not have their progress, pushing my latest commit, rebased on the top of the master branch, to the remote delete all of their work!

Updating my remote history moving forward a branch can be done if I am really sure no one touched it, this can be done with --force-with-lease. Checking the tip of the two branch in question, the local rebased and the remote, assure Git that the two branches are the very same history and that we can me it forward without losing someone’s work during the process.

Hacking Authentication for Better Experience

I recently moved to Trento to begin my master in Mathematics and I got to catch a few trains to move back and forth to Firenze. A single way trip is close to 3 hours and it would quickly become boring without an internet access.

Unfortunately the railway path is not mobile friendly, Italy is definitely not flat and the train have to transit under a bunch of mountains so no mobile signal under there. Goodbye Reddit :(

Trenitalia is the operator of the high speed railway and in partnership with Telecom Italia provide internet access on the high speed trains trough a WiFi network and God knows what something that can reach a train going 300 Km/h under a mountain.

There is no fee in using this service and even if it limited in brandwith it is still a good way to save on my monthly plan limited internet.

One of the main drawbacks of the service is authentication, it has been thought for screens wider than 7" and in this world of smartphone is pretty much unusable.

The process is:

  1. request your credentials via a web interface, recieve via SMS
  2. enter your credentials via a web interface

This should be simple enough right? Unfortunately the process is more like this:

  1. load the page http://portalefrecciargento.it, there won’t be automatic redirection from other urls you may type
  2. follow the link tied to your authentication method, text message for TIM, text message for other providers, credit card (seriously?)
  3. enter your phone number
  4. receive your text message, your credentials are like this smsDDDDDDD@telecomitalia.it(D is a digit) and a four digits password, valid for 24 hours. You can request your credentials three times, after that they won’t provide them and remind you that you run out of chance to retrieve them
  5. enter the credentials in the form
  6. accept privacy, terms and conditions
  7. submit
  8. surf all the web!

This process is so difficult on a smartphone that it drove me so much crazy that I switched to my laptop and did not look back at my phone for 3 hours!

There must be a simpler way to do this, the web is simple when thought and done right!

The best chance at fixing this terrible user experience for my phone is providing my own authentication page, as a OpenWeb App!

The idea is simple, study the authentication procedure from my laptop and reverse engineer the steps needed to request credentials and to autenticate.

Starting is really simple, I need a index.html file, a manifest.webapp file and maybe a CSS and a JS.

This skeleton is a good starting point!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- index.html -->
<html>
  <head>
      <!-- encoding is important kids! -->
      <meta charset="utf-8">
      <!-- but a viewport is importanter -->
      <meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
  </head>
  <body>
      <section role="application">
          <section role="register">
          </section>
          <section role="login">
          </section>
      </section>
  </body>
</html>

and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// manifest.webapp
{
  "name": "Wifi Frecce",
      "launch_path": "/index.html",
      "description": "Accedi al servizio Wifi delle Frecce",
      "developer": {
          "name" : "Edoardo Putti",
          "url": "http://www.edoput.it"
      },
      "default_locale": "it",
      "chrome" : {
          "navigation": "true"
      }
}

Let’s study how we can request the credentials; on the page http://portalefrecciargento.it follow the link to request your credentials, once there fill the form and open the developer tools, focus the network tab and submit the form, a request will be logged and you can peek and analyse it.

I saved the request as a .har file and opened it in a text editor, there were all the details of the previous request.

The important part of the request is this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"params": [
{
  "name": "acpt_contratto",
      "value": "1"
},
{
  "name": "acpt_privacy",
  "value": "1"
},
{
  // the phone number I submitted
  "name": "mobile",
  "value": "3333333333"
},
{
  "name": "mode",
  "value": "esegui"
},
{
  "name": "info",
  "value": "Invia"
}
]

From this info we can see that there are a few parameters that we should pass with our request but only one is provided by the user, mobile.

Knowing this we can model a form that will make the request on our behalf with only one input, the phone number.

1
2
3
4
5
6
7
8
9
10
11
12
13
<section role="register">
  <formi action="http://sms.wifiarea.it/" method="post" autocomplete="true">
      <label for="phone">
          Phone number
      </label>
      <input id="phone" type="number" name="mobile">
      <input name="acpt_contratto" value="1" hidden>
      <input name="acpt_privacy" value="1" hidden>
      <input name="mode" value="esegui" hidden>   
      <input name="info" value="Invia" hidden>    
      <input type="submit" value="Request credentials via sms">
  </form>
</section>

Doing the very same process for the login request we can model another form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<section role="login">
  <form action="https://home.alicezone.it/wifiaaa/sitemap.jsp" method="post">
      <label for="username">
          Username
      </label>
      <input id="username" type="email" name="username">
      <label for="password">
          Password
      </label>
      <input id="password" type="number" name="password">
      <input name="path" value="authenticate" hidden>
      <input name="hsb" value="false" hidden>
      <input name="language" value="it" hidden>
      <input name="logType" value="null" hidden>  
      <input type="submit" value="Login">
  </form>
</section>

This very poor interface result in this screenshot on the simulator

The usual I'm not a designer disclaimer

Using this webapp to request my credentials and login make it so easy and speeds up the process so much that I specifically run a Firefox OS simulator on my laptop to stay away from the official (and cluncky) interface.

Conclusions

This project is a simple hack to enhance user experience, many passenger on the train use their devices during their trip and many of them surely struggle with a process that is so stupidly simple but so poorly implemented, this was a very low hanging fruit with a pretty big benefit and it’s a shame that no one came up with a better solution.

What I have rediscovered, and that fascinates me, is that the web is very simple, so simple that you can hack your way out of madness while you are on a train isolated from the internet in less than ten minutes.

Update

I moved the project to a specific webpage, agnostic to the browser but you can still install it as a OpenWeb App.

Here you can see a screenshot

screenshot from the responsive view

And the updated code, more accessibility friendly; some examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form action="http://sms.wifiarea.it/" method="post" target="_blank" autocomplete="true">
<label for="phone" id="phone_input_label">
Numero di telefono
</label>
<br>
<input id="phone" type="number" name="mobile">
<br>
<label for="acpt_contratto" id="acpt_contratto_label">
Accetto il contratto per il servizio
</label>
<input id="acpt_contratto" name="acpt_contratto" type="checkbox" value="1" checked>
<br>
<label for="acpt_privacy" id="acpt_privacy_label">
Accetto le condizioni della privacy
</label>
<input id="acpt_privacy" name="acpt_privacy" type="checkbox" value="1" checked>
<br>
<input name="mode" value="esegui" hidden>
<input name="info" value="Invia" hidden>
<input type="submit" value="Richiedi username e password" id="submit_credentials">
</form>

and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<form action="https://home.alicezone.it/wifiaaa/sitemap.jsp" method="post" target="_blank" autocomplete="true">
<label for="username">
Username
</label>
<br>
<input id="username" type="email" name="username">
<br>
<label for="password">
Password
</label>
<br>
<input id="password" type="number" name="password">
<br>
<input name="path" value="authenticate" hidden>
<input name="hsb" value="false" hidden>
<input name="language" value="it" hidden>
<input name="logType" value="null" hidden>
<input type="submit" value="Accedi" id="submit_login">
</form>

PirateBay Box - Torrent Generation

It’s time for another tale about my shiny new Piratebox and the world of FOSS; the last tale about my first contact with the Piratebox project and some exploration that lead to a decentralised bittorrent tracker running on my Piratebox, offloading much of the shared file server load from the Piratebox to the network itself.

Continuing on that path I enhanced the process of torrent creation and explored a little bash scripting in the last week, the results will be the argument of this post.

Last time I discovered mktorrent, a program to generate torrent files straight from the terminal with ease and that has been repackaged for OpenWRT, not the version I’m running but maybe we can have a backport, definitely the candidate to generate torrents on the fly directly from the Piratebox with a cron job.

Last time this was what we used

1
edoput@edoput:~$ mktorrent path/to/file -a http://piratebox.lan:26213/announce

the idea now is to iterate the process for every file/directory that we want to share.

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
# iterate with mktorrent over every file in the path provided
# as the first argument

# $0 is mktorrent, the program called
# $1 is the first string after mktorrent and so on with $2, $3

for f in $1/*; do
  mktorrent "$f" -a "http://piratebox.lan:26213/announce"
done

but we are missing the wonderful feature of the web seeds, we need to have the filename not the full path.

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
# iterate with mktorrent over every file in the path provided
# as the first argument

# $0 is mktorrent, the program called
# $1 is the first string after mktorrent and so on with $2, $3

# basename is another program that get the filename from the full path

for f in $1/*; do
  mktorrent "$f" -a "http://piratebox.lan:26213/announce" -w "http://piratebox.lan/Shared/$(basename $f)"
done

Ok this is good but I would like to enable web seeds as an option

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh
# iterate with mktorrent over every file in the path provided
# as the first argument

# $0 is mktorrent, the program called
# $1 is the first string after mktorrent and so on with $2, $3

# basename is another program that get the filename from the full path

for f in $1/*; do
  if $2: then
      mktorrent "$f" -a "http://piratebox.lan:26213/announce" -w "http://piratebox.lan/Shared/$(basename $f)"
  else
      mktorrent "$f" -a "http://piratebox.lan:26213/announce"
  fi
done

this is something but it break the first version, nothing to worry about this time.

Maybe we should try getopts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/sh
# iterate with mktorrent over every file in the path provided
# as the first argument

# WEBSEED is disabled on default, running with option -w enable it
WEBSEED=false

while getopts ":w" arg; do
  case "${arg}" in:
      w)
          WEBSEED=true
          ;;
      *)
          echo "Oops"
          ;;
done

for f in $1/*; do
  if $WEBSEED; then
      mktorrent "$f" -a "http://piratebox.lan:26213/announce" -w "http://piratebox.lan/Shared/$(basename $f)"
  else
      mktorrent "$f" -a "http://piratebox.lan:26213/announce"
  fi
done

Ok but running this command requires that the argument path/to/file to be before the option -w, I don’t like that much, let’s make the path argument something optional.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/sh
# iterate with mktorrent over every file in the path provided
# as the first argument

# WEBSEED is disabled on default, running with option -w enable it
WEBSEED=false
FILE_DIRECTORY=$(pwd)


while getopts ":p:w" arg; do
  case "${arg}" in:
      p)
          # optarg is the option argument that is been pointed now
          FILE_DIRECTORY=${OPTARG}
          ;;
      w)
          WEBSEED=true
          ;;
      *)
          echo "Oops"
          ;;
done

for f in $FILE_DIRECTORY/*; do
  if $WEBSEED; then
      mktorrent "$f" -a "http://piratebox.lan:26213/announce" -w "http://piratebox.lan/Shared/$(basename $f)"
  else
      mktorrent "$f" -a "http://piratebox.lan:26213/announce"
  fi
done

Nice! the work is almost done, error handling is missing and maybe this could use some more docs and comments.

The following is the final version of the magic_torrent.sh script, you can download it here

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/sh
#
# Run mktorrent on every sub-directory on the diretory provided as first argument
#
# mkrtorrent options:
#     -a, --announce: a http/upd url pointing to the announce service on the
# tracker
# -w, --web-seed: a http url pointing to location of file on some web server

usage (){
  echo "Usage:\n-d: path to destination\n-p: path to files\n-w: enable web seed\n" 1>&2; exit 1;
}


# Default options are no web seed and working directory as destinations for torrent files
WEBSEED=false
DESTINATION=$(pwd)
FILE_DIRECTORY=$(pwd)

# Check whether `basename` is installed or not, required for web seed
if $WEBSEED; then
  hash basename 2> /dev/null/ || {
      echo >&2 "Enabling webseed requires `basename` but it's not installed. Aborting."; exit 1;
  }
fi

# Options from user
while getopts ":d:p:w" arg; do
  case "${arg}" in
      d)
          DESTINATION=${OPTARG}
          ;;
      p)
          FILE_DIRECTORY=${OPTARG}
          ;;
      w)
          WEBSEED=true
          ;;
      *)
          usage
          ;;
  esac
done

# Echo the options
YELLOW="\033[1;33m" # yellow
NC="\033[0m" # no trailing color
echo "${YELLOW}Web Seed: $WEBSEED\tDestination: $DESTINATION ${NC}"

# Move to destination
cd "$DESTINATION"

# Real work being done
for f in $FILE_DIRECTORY/*
# For every directory in the first argument
do
  # Announce your work
  echo "Generating torrent file for: $f\n"
  if $WEBSEED; then
      # Web seed is requested
      NAME=$(basename $f)
          mktorrent "$f" --announce="http://piratebox.lan:26213/announce" --web-seed="http://piratebox.lan/Shared/$NAME"
  else
      # No web seed
      mktorrent "$f" --announce="http://piratebox.lan:26213/announce"
  fi
  echo "Torrent for $f created\n"
done

Remove Zoom With Touchpad From Firefox

Finally I got this “feature” to work the way I really want, page-zoom on mousewheel is done on my laptop!

The problem is: Ctrl + something is a widely used combination on keyboard shourtcut, Ctrl + Tab, Ctrl + Shift + Tab, Ctrl + +, Ctrl + -, etc etc…; another way to trigger the zoom on Firefox is Ctrl + mousewheel, unfortunately the mousewheel gets remapped on touchpads as two-finger scroll, that way you can scroll quite nicely, too bad that if I press enter the zoom comes into play and Ctrl + 0 is the only way out to normality.

So on to the solution, open about:config on your Firefox, dismiss the safety notice and search for mousewheel.with_control, a nice list of options pop out.

The default value for mousewheel.with_control.action is a string with value 3 which in some way maps to zoom, set that value to 1 and enjoy your new experience, no more unexpected zoom when hitting Ctrl for something else to be done.

A Decentralised Tracker

Recently I received a wonderful gift, a Piratebox! Now I have my own Internet! With a forum, a chat and a file sharing server! So cool!

What is not cool is that my hardware is very limited and the platform is stable only when the custom part is kept at minimum.

Definitely the best part of the box is the shared file server, connect, upload/download and go, a single download can go as up as 1Mb/s (using scp) or 30Mb/s with wget but not everyone can use a terminal or have credentials to access the box, the easiest option is definitely the HTML interface.

Under the hood the piratebox is powered by lighttp, a very resource-light webserver that fits good in the great picture of the project but we are a little late with the updates. Piratebox ships whit v0.9 and lighttp is now at v1.4, maybe we can squeeze more from the hardware with asyncronous I/O or some modern witchcraft but that’s not the point.

Before diving into the development part of this article I would like to say a few word about Droopy. Droopy is the magic that runs the piratebox most loved feature, the file sharing. It provides a dead simple upload page that you can encapsulate inside an iframe and you can use python to make more magic happen during the upload process. What I don’t really like about this approach is that it’s good only if you have small files or you are really patient.

A better approach would be using a file-sharing protocol like bittorrent, the file is avaiable to the network, not uploaded to the piratebox storage and there is virtually no size limit to what you can share.

Enough with the tecno-blabbery, let’s run our PirateBay!

Everything has been made possible by the bnbt tracker project, I think it has been abandoned a long time ago (2006) but the docs is something good and I did most of the experimentation for you.

The package we are gonna install is cbtt and you can find it in the package list for the OpenWrt attitude adjustment release. We will also need the uclibcxx package, a C++ library on diet, required to run our tracker.

Move the packages to the piratebox either with scp or upload it to the shared folder.

As in every linux distro we have a package manager, opkg; here you can find more info about the usage.

1
2
3
# sobstitute the package filename to the generic name
root@piratebox:~# opkg install uclibcxx -d ext --cache=/mnt/usb/install/cache
root@piratebox:~# opkg install cbct -d ext --cache=/mnt/usb/install/cache

This commmand will install both the library and the tracker software on a extended-root partition we have “mounted” as ext, se opkg.conf file for more info.

Link this new executable in /bin and you will be ready to go on.

1
root@piratebox:~# ln -s /usr/local/usr/bin/bnbt /bin/bnbt

Go on and give it a try, start the server with the command bnbt, a lot of log lines will come out and you can see what happens.

1
2
3
4
5
6
7
8
9
10
11
12
13
[Sun Aug  9 11:10:56 2015] config warning - unable to open bnbt.cfg for reading
[Sun Aug  9 11:10:57 2015] server - listening on port 26213 ("port")
[Sun Aug  9 11:10:57 2015] warning - unable to open dstate.bnbt for reading
[Sun Aug  9 11:10:57 2015] warning - unable to open tags.bnbt for reading
[Sun Aug  9 11:10:57 2015] warning - unable to open users.bnbt for reading
[Sun Aug  9 11:10:57 2015] warning - unable to open clientbans.bnbt for reading
[Sun Aug  9 11:10:57 2015] Client Banlist parse called
[Sun Aug  9 11:10:57 2015] warning - unable to open bans.bnbt for reading
[Sun Aug  9 11:10:57 2015] IP Banlist parse called
[Sun Aug  9 11:10:57 2015] server - start
## Ctrl + c stop the process and kill the server
[Sun Aug  9 11:11:03 2015] server warning - select error (error 4)
[Sun Aug  9 11:11:04 2015] server - exit

Good news, it works! After you killed the server you will find some files around that it has generated for you.

1
2
3
4
root@piratebox:~# ls -l
-rw-r--r--    1 root     root          3616 Aug  9 11:10 bnbt.cfg
-rw-r--r--    1 root     root            36 Aug  9 11:11 dstate.bnbt
-rw-r--r--    1 root     root             2 Aug  9 11:11 tags.bnbt

You can open them and look around, bnbt.cfg is the configuration file, dstate.bnbt is the internal file to keep some stats as number of completed torrents and tags.bnbt is the internal file to keep track of the tags you can add to torrents.

But I don’t like it this way, these files are polluting my limited memory on the root filesystem and they can grow very large, let’s move them to somewhere else.

1
2
3
4
5
6
7
8
## remove the previous files
root@piratebox:~# rm bnbt.cfg dstate.bnbt tags.bnbt
## change the working directory
root@piratebox:~# cd /mnt/ext/etc/config/
## create a new directory and step into it
root@piratebox:/mnt/ext/etc/config/# mkdir bnbt && cd bnbt
## run the tracker program inside the new directory
root@piratebox:/mnt/ext/etc/config/bnbt/# bnbt

This is definitely better, now the files are on the external memory. Let’s move on and let the tracker start at boot.

init files

I don’t know if this is the best way to handle this but I found that init files are the simplest option so I will go that way.

1
2
3
4
5
6
## change the working directory
root@piratebox:~# cd /etc/init.d/
## create a new file, will contain the tracker instructions
root@piratebox:/etc/init.d/# touch tracker
## make it executable
root@piratebox:/etc/init.d/# chmod +x tracker

Init files are all alike, so I will just post mine, just copy and paste it into the tracker file you created before.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/sh /etc/rc.common
# description: run tracker server on startup
# the new galaxy most resilient tracker

NAME="torrent tracker"

# Start order
START=99
# Stop order
# STOP=

# move to config directory

cd /mnt/ext/etc/config/bnbt

# Start the torrent tracker
start () {
  service_start /bin/bnbt &
  ## create a lock file
  ## touch /var/lock/subsys/bnbt
  ## success $"Tracker Server startup"
  ## echo
}

# Stop the torrent tracker
stop () {
  service_stop /bin/bnbt
}

Now the final touch, enable it. This will write a file, S99tracker to /etc/rc.d/ and it will start the tracker at boot.

To enable it

1
root@piratebox:~# /etc/init.d/tracker enable

Reboot the piratebox and wait until it is back online, go to http://piratebox.lan:26213/users.html, here you can create a user with every permission, it will be your administrator. Do me a favor and don’t call it admin, administrator or root and give it a decent password. The email is required but you can put anything in there, so be creative.

The next page you will see is the tracker’s index page, there you can see what your tracker is doing with the torrent network you set up.

There won’t be a lot of info but this is good, we prefer anoninmity and no logging.

configuring your tracker

You can find a lot of info on the official documentation of the bnbt project, so I won’t cover (now) what these config means, just copy and paste it to your config file (which is now /mnt/ext/etc/config/bnbt/bnbt.cfg).

I know working on the Piratebox is not that simple, so here it is the config file, upload it to the piratebox and mv it to the bnbt config folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
allowed_dir =
announce_interval = 60
bind =
bnbt_access_log_dir =
bnbt_access_log_file_pattern = %Y-%m-%d.log
bnbt_allow_comments = 0
bnbt_allow_info_link = 0
bnbt_allow_scrape = 1
bnbt_allow_scrape_global = 1
bnbt_allow_search = 0 
bnbt_allow_sort = 0
bnbt_allow_torrent_downloads = 0
bnbt_archive_dir =
bnbt_charset = iso-8859-1
bnbt_comment_length = 800
bnbt_comments_file =
bnbt_compression_level = 0
bnbt_count_unique_peers = 0
bnbt_debug = 0
bnbt_delete_invalid = 1
bnbt_delete_own_torrents = 1
bnbt_disable_html = 0
bnbt_dump_xml_file =
bnbt_dump_xml_interval = 600
bnbt_dump_xml_peers = 1
bnbt_error_log_dir =
bnbt_error_log_file_pattern = %Y-%m-%de.log
bnbt_external_torrent_dir =
bnbt_file_dir =
bnbt_file_expires = 180
bnbt_flush_interval = 100
bnbt_force_announce_on_download = 0
bnbt_force_announce_url =
bnbt_guest_access = 3
bnbt_max_conns = 64
bnbt_max_peers_display = 500
bnbt_max_recv_size = 128
bnbt_max_torrents = 0
bnbt_member_access = 79
bnbt_name_length = 32
bnbt_parse_on_upload = 1
bnbt_per_page = 100
bnbt_private_tracker_flag = 0
bnbt_realm = BNBT
bnbt_refresh_fast_cache_interval = 30
bnbt_refresh_static_interval = 10
bnbt_require_announce_key = 1
bnbt_robots_txt =
bnbt_rss_channel_copyright =
bnbt_rss_channel_description = BitTorrent RSS Feed for BNBT
bnbt_rss_channel_image_height = 0
bnbt_rss_channel_image_url =
bnbt_rss_channel_image_width = 0
bnbt_rss_channel_language = en-us
bnbt_rss_channel_link = http://localhost:26213/
bnbt_rss_channel_title = My BNBT RSS Feed
bnbt_rss_channel_ttl = 60
bnbt_rss_file =
bnbt_rss_file_mode = 0
bnbt_rss_interval = 30
bnbt_rss_limit = 25
bnbt_rss_online_dir =
bnbt_show_added = 0
bnbt_show_average_dl_rate = 0
bnbt_show_average_left = 0
bnbt_show_average_ul_rate = 0
bnbt_show_completed = 0
bnbt_show_file_comment = 0
bnbt_show_file_contents = 0
bnbt_show_gen_time = 1
bnbt_show_info_hash = 0
bnbt_show_left_as_progress = 0
bnbt_show_max_left = 0
bnbt_show_min_left = 0
bnbt_show_num_files = 0
bnbt_show_share_ratios = 0
bnbt_show_size = 0
bnbt_show_stats = 0
bnbt_show_transferred = 0
bnbt_show_uploader = 0
bnbt_static_footer =
bnbt_static_header =
bnbt_style_sheet =
bnbt_swap_torrent_link = 0
bnbt_tag_file = tags.bnbt
bnbt_tlink_bind =
bnbt_tlink_connect =
bnbt_tlink_password =
bnbt_tlink_port = 5204
bnbt_tlink_server = 0
bnbt_tracker_title = The Piratebox Bay
bnbt_upload_dir =
bnbt_use_announce_key = 1
bnbt_users_file = users.bnbt
bnbt_users_per_page = 50
cbtt_abuse_detection = 0
cbtt_abuse_hammer_limit = 10
cbtt_abuse_limit = 5
cbtt_ban_file = clientbans.bnbt
cbtt_ban_mode = 0
cbtt_blacklist_below_1024 = 0
cbtt_blacklist_common_p2p_ports = 0
cbtt_block_private_ip = 0
cbtt_dont_compress_torrents = 1
cbtt_download_link_image =
cbtt_ip_ban_mode = 0
cbtt_ipban_file = bans.bnbt
cbtt_page_number_count = 3
cbtt_require_compact = 0
cbtt_require_no_peer_id = 0
cbtt_restrict_overflow = 0
cbtt_restrict_overflow_limit = 1099511627776
cbtt_restricted_peer_spoofing = 0
cbtt_scrape_file =
cbtt_scrape_save_interval = 0
cbtt_service_name = BNBT Service
cbtt_stats_link_image =
dfile = dstate.bnbt
downloader_timeout_interval = 2700
favicon =
image_bar_fill =
image_bar_trans =
keep_dead = 0
max_give = 200
min_announce_interval = 1500
min_request_interval = 18000
mysql_cbtt_ttrader_support = 0
mysql_database = bnbt
mysql_host =
mysql_override_dstate = 0
mysql_password =
mysql_port = 0
mysql_refresh_allowed_interval = 0
mysql_refresh_stats_interval = 600
mysql_user =
only_local_override_ip = 0
parse_allowed_interval = 0
port = 26213
response_size = 50
save_dfile_interval = 300
show_names = 1
socket_timeout = 15

Now you only need a torrent to upload and we will see if the tracker is really working.

make a torrent

I will be only covering how to do this with a command line but google is your friend.

We will use mktorrent because it’s dead simple, the problem is it’s not avaiable on the current release of the piratebox so you have to use another pc to create the torrent file.

1
edoput@edoput~: mktorrent path/to/my/file -a http://piratebox.lan:26213/announce

let it run and we are done, upload the torrent file to the piratebox, download it from the Shared file folder and open it with your torrent client of choiche. When prompted where to save the file choose the current file location on your pc and Enable local peer discovery to share your file with the local net.

Web seeds

Okay but if I already have a file on the piratebox and I want it to stay there how can this help me achieving a better download speed?

Intrducing Web Seeds, just use the option --web-seed with the file url like this.

1
edoput@edoput~: mktorrent path/to/my/file -a http://piratebox.lan:26213/announce -w http://piratebox.lan/Shared/myfile

Basically this will enable the piratebox to be a seed box, serving the files both on http and torrent.

Conclusion

I had fun with this project and it all went very well, it took maybe three days of thinkering and it’s now working quite well.

What I really like about this implementation is that if the configuration of the tracker on every box is the same then you can jump across networks with your files and you can keep sharing, there is the option to have the tracker display every torrent it is tracking and that would mean that we can add a page to the piratebox where avaiable torrents are listed for everyone to go and get it.

Firefox in Enterprise

In may I have been asked about Firefox deployment in enterprise environment but I did not know much at the time. Time for a first draft of what I’ve discovered so far.

Firefox has two variants, Rapid Release and Extended Support Release aka RR and ESR, both for enterprise environment.

Rapid Release

Released every 6 weeks, just like Firefox for everyone but bugfixes are not backported to previous versions; make tests, fix your environment, move on. Here you can find the release calendar for RR

A softer approach is the Extended Support Release, just like a Ubuntu LTS edition.

Extended Support Release

Released every 42 weeks, follows the standard versioning so there is a jump of 7 Rapid Release between every ESR release.

Up to now we had the following versions: 10, 17, 24, 31 and 38.

Every ESR version has a support window of 54 weeks, an overlap of 12 weeks between the old and the new version gives you a lot of time to make changes and to be sure you can switch to a newer release without problems.

Sure, 12 weeks are a lot of time but you can get 6 more if you try the RR beta before the next ESR gets published, 18 weeks seems a lot better.

Again, ESR gets bugfixes for 54 weeks but after that it’s done, expect no more and switch to the newer version.

Download

RR download link

ESR download link

SMIL(E)

Inspired by CSS3 Animations in real life.

Working with web tech is mostly using a tool to get something done, animations are something that isn’t that simple to deal with but there are tools out there that can make your life simpler.

We can use CSS, DOM, sprites and? Obviously something no-one heard of, SVG animations in the browser!

Obviously we have an acronym SMIL, pronounced as smile, which is Synchronized Multimedia Integration Language.

Basically we can add a nested <animate> svg element within the one we want to animate, like this.

1
2
3
<myElement>
    <animate />
</myElement>

set what we want to animate

1
2
3
<myElement center="0">
    <animate attributeName="center" from="0" to="100" dur="5s" repeatCount="indefinite"/>
</myElement>

and now the center attribute will go from the value 0 to 100 in 5 seconds and cycle indefinitely.

Wow that was easy, time to level up

1
2
3
4
5
6
7
8
<myElement transform="rotation">
    <animateTransform
    type="rotate"
    from="0"
    to="360"
    dur="5s"
    repeatCount="indefinite"/>
</myElement>

this is a little tricky, we did not se the center of rotation so it is assumed to be (0,0) the top left corner, lets fix this.

1
2
3
4
5
6
7
8
<myElement transform="rotation" width="1024" height="1024">
    <animateTransform
    type="rotate"
    from="0 512 512"
    to="360 512 512"
    dur="5s"
    repeatCount="indefinite"/>
</myElement>

now the center of rotation is (512,512), the center of myElement.

And this is the effect we can get with SMIL, the example is forked from Edoardo Odorico’s one.

What is missing is the CSS animation and we could pack the three files into one.

These are the images with animation included

Zero Lag Backbone Views

It is not a secret that I have been playing Dungeon & Dragons with my friends for a long time and that I am passionate about it, I could roleplay more but we have a lot of fun.

I should not be wrong when I say that what is important in a game session is the ratio played time over total time, strategy meetings are ok but consulting manuals and other things should be kept out of the session.

One of the activities my Dungeon Master (Lorenzo) found to be time-sucking was planning the battle order: making a list of the warriors, getting everyone to throw a dice roll, throwing dice for the enemy, the NPC, order the list.

Pencils and paper could be replaced by a laptop but ordering a list is not that easy in a word processor, I do not know if other attempts have been made by Lorenzo but in the end we kept using pencils and papers.

And that is the main problem of today, to update elements in a list and order it when the update is done. Hmmm… if a character could be treated as a model and the company as a collection I would only need a view to display the information and a way to sort the list, I like this MV* approach so let’s see if something can be done with a SPA with Backbone.

Spoiler alert: this is not a working application written with Backbone.js, more or less is a guideline, what really matters is the problem of the view.

I definitely need a model for our heroes with names and values to order them

1
2
3
4
5
6
var Hero = Backbone.Model.extend({
    initialize: function (options) {
        this.name = options.name;
        this.value = options.value;
    }
});

and a collection to keep everyone toghether, with something that can compare model and order them.

We are reversing the order because it is default ascending. We could deal with this in the view, prepending model instead of appending but I would like to leave my view without any extra code.

1
2
3
4
5
6
var Team = Backbone.Collection.extend({
    model: Hero,
    comparator: function (model){
        return -parseInt(model.get('value'));
    }
});

Now we need a view,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var View = Backbone.View.extend({
    template: myTemplate,
    events: {
        'click #addHero': 'addPlayer',
        'click #sortTeam': 'sortHeroCollection'
    },
    initialize: function () {
        this.listenTo(this.collection, 'add', this.render);
    },
    render: function () {
        this.$el.empty();
        this.$el.html(this.template(
            this.collection.toJSON()
        ))
    },
    addPlayer: ...
    sortHeroCollection: ...
})

Ok this is not that performant or anything else, in my real work I used nested views so this will not definitely work as before.

What is important is the template I am using

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- this is the hero template -->
<p>
    <!-- name -->
    <span contenteditable="true" title="nome" class="nome">${nome}</span>
    <span>
        <!-- initiative -->
        <span contenteditable="true" class="single-line" title="iniziativa"> ${iniziativa} </span>
        <!-- health -->
        <span contenteditable="true" class="single-line" title="salute"> ${salute} </span>
        <!-- AC -->
        <span contenteditable="true" class="single-line" title="armatura"> ${armatura} </span>
        <!-- listen -->
        <span contenteditable="true" class="single-line" title="ascoltare"> ${ascoltare} </span>
        <!-- spot -->
        <span contenteditable="true" class="single-line" title="osservare"> ${osservare} </span>
    </span>
    <button class="fa remove fa-remove" title="rimuovi"></button>
</p>

the property contenteditable=true of the span element make it so that I don’t have to update my view when values are updated because the content of the page has already been edited, all I need is an additional event handler attached to my view.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var View = Backbone.View.extend({
    template: myTemplate,
    events: {
        'click #addHero': 'addPlayer',
        'click #sortTeam': 'sortHeroCollection',
        'input': 'update'
    },
    initialize: function () {
        this.listenTo(this.collection, 'add', this.render);
    },
    render: function () {
        this.$el.empty();
        this.$el.html(this.template(
            this.collection.toJSON()
        ))
    },
    addPlayer: ...
    sortHeroCollection: ...
    // you have to find a way to reference models from the view, hence subviews
    update: function (e) {
        this.model.save(
            takeWhatIsUpdated(e),
            {
                patch: true
            }
        )
    }
})

In the same event handler I could wait for validation from the model and the server and update the view to display errors if needed.

I think this is a good trick that we can use, it’s an enhancement both in performance (less views updates) and user experience (less view updates), the only drawback is compatibility and telling the user that even if there is no input field on the page they can submit their changes to the application but a big (dismissable) banner on the top of the page should work.