Planet freenode

July 25, 2014

RichiH's blog

Release Critical Bug report for Week 30

I have been asked to publish bug stats from time to time. Not exactly sure about the schedule yet, but I will try and stick to Fridays, as in the past; this is for the obvious reason that it makes historical data easier to compare. "Last Friday of each month" may or may not be too much. Time will tell.

The UDD bugs interface currently knows about the following release critical bugs:

  • In Total: 1511
    • Affecting Jessie: 431 That's the number we need to get down to zero before the release. They can be split in two big categories:
      • Affecting Jessie and unstable: 383 Those need someone to find a fix, or to finish the work to upload a fix to unstable:
        • 44 bugs are tagged 'patch'. Please help by reviewing the patches, and (if you are a DD) by uploading them.
        • 20 bugs are marked as done, but still affect unstable. This can happen due to missing builds on some architectures, for example. Help investigate!
        • 319 bugs are neither tagged patch, nor marked done. Help make a first step towards resolution!
      • Affecting Jessie only: 48 Those are already fixed in unstable, but the fix still needs to migrate to Jessie. You can help by submitting unblock requests for fixed packages, by investigating why packages do not migrate, or by reviewing submitted unblock requests.
        • 0 bugs are in packages that are unblocked by the release team.
        • 48 bugs are in packages that are not unblocked.

Graphical overview of bug stats thanks to azhag:

by Richard 'RichiH' Hartmann at July 25, 2014 09:58 PM

mquin's blog

Couch to 5k - the view from halfway

Back in June, inspired by the hint of some pleasant weather and the understanding that I'm perhaps not as active as I ought to be, I bought a pair of running shoes and started following the popular "Couch to 5k" training programme.

The programme starts out with short periods of jogging interspersed with slightly longer periods of walking, and over the subsequent weeks the time spent jogging is increased steadily, with the aim - as laid out in the name - of allowing a fairly sedantary person with no running experience to progress to covering a 5km run over the space of nine weeks. Being a little bit active I'm probably not in the typical couchbound start group, but I've not really done any running since high school.

Now that I've finished week 5 - more than halfway through - I figured I write a bit about the experience.

On day one I didn't manage to finish the routine as laid out and was wondering whether I'd just wasted the cost of a pair of shoes.

Looming large duirng the early weeks was thr worry that my damaged hip might not be able to deal with jogging, however despite somme initial discomfort this hasn't turned out to be a problem, and I'd go as far as to say that the level of discomfort (which has been variable, but constant, pretty much since I broke the hip) is lower now than it has been in the past.

So far, I've found the progression to be perfect - the workouts have been challenging, but I've managed, with a bit of willpower, to complete all of them so far, and I'm feeling positive about the remaining weeks.

The last week has been the hardest so far - While the first four involved doing the same routine three times, week five ramps up this distance covered at one time considerably, from 5 minutes at the start of the week to 20 at the end (and, as my luck would have it, the long run fell on the warmest day as well).

July 25, 2014 05:54 PM

July 17, 2014

erry's blog

5 Excellent developer tools features

These are five features use often (apart from the last, never used that one!) about the developer tools in Firefox and Chrome. I thought I would blog about the similar features in both browsers instead of focusing on one of them, and show how to use them in both of them.

Emulating element states (hover, focus, etc.)

One of my favourite features, which makes things much less a pain, is the ability to trick the browser to think that you’re hovering over an element until you remove the flag. You can then freely inspect any :hover css code and see how it’s (not) working.

In chrome dev tools, after you inspect an element, you can right click on the element (make sure to not do this on the body/text or it won’t work) and click on ‘force element state’, then the state you want (e.g. ‘:hover’)

Chrome dev tools - hover

Chrome dev tools – hover

Now, you should see your stylesheet for the :hover state!

Hover preview - Chrome

Hover preview – Chrome

Firefox can do the same by simply right clicking an element and clicking ‘hover’, it again allows you to see the effect of your CSS and see the new :hover code applying.

Firefox hover preview

Firefox hover preview

Device emulation

In chrome, you can toggle the bottom pane by pressing ESC on developer tools. Once open, you can choose the ‘emulation’ panel. This gives you several options: from screen sizes, to user agents, to whole device setups! Perfect for testing responsive websites.
You can also emulate touch events with your mouse there.

Chrome emulating a nexus 5

Chrome emulating a nexus 5

In firefox, there is a little icon on the element inspector that toggles responsive design mode

firefox responsive design

firefox responsive design


Once clicked, you can again see your website in a variety of sizes.
Firefox emulating a smaller screen

Firefox emulating a smaller screen


There’s a dropdown to select a screen size, and you can toggle touch event emulation again or take a screenshot. My favourite feature there though is the ability to rotate the screen with a button!

Networking

Chrome has a networking tab in its console which allows you to see all your requests and how much time each request took. Apart from being a way to see if you have any slow pages, it also helps diagnose errors such as missing assets and see AJAX requests.

All requests

All requests

It also shows details for a specific request when clicked, making it easy to see request headers and body – very useful for debugging POST requests.

Request details in chrome network panel

Request details in chrome network panel

Finally, there’s the ‘audits’ panel which offers some advice on making your page faster, sort of like YSlow and similar extensions.

Audit panel

Audit panel

Firefox offers the same functionality in its own networking panel:

Firefox networking

Firefox networking

Viewing a request

Viewing a request

And if you click the little clock on the bottom right, you can see a breakdown of the various page elements and how much time they take

My only regret about going back to student accommodation for my year in industry is that I will greatly miss my parents' fibre optic connection.

My only regret about going back to student accommodation for my year in industry is that I will greatly miss my parents’ fibre optic connection.

Style editor

If you open a CSS file in sources, chrome allows you to make changes to it on-the-fly, and see if they apply. This, of course, is in addition to being able to see and edit any styles that apply to an element back in the elements panel!

Chrome style editor

Chrome style editor

Firefox has a style editor pane that allows the same, as well as adding new stylesheets and importing stylesheets, which is even better.

Firefox style editor

Firefox style editor

Shader editor

This is a firefox feature I only found out about while writing this post! If you click the little wrench icon in the top left of firefox dev tools, you can enable the shader editor. If you go to a page that uses WebGL afterwards (such as HelloRacer) you can see and edit shader code. Pretty neat!

Shader editor

Shader editor

Are there any features you use a lot? Do leave a comment!

Thanks for reading, and see you until next time.

by Errietta Kostala at July 17, 2014 07:46 PM

June 28, 2014

erry's blog

My first activity as a STEM ambassador

A couple days ago (as of writing this, at least!) (on the 26th of June, 2014), I went out to a school with a staff member from University and supported a session on Raspberry pi. The session was part of a club taking place during a week where the students didn’t have regular classes and lasted the whole day – it was part of a whole week of sessions on Linux, Raspberry pi, computing, and programming done for that club.

My role as a STEM ambassador for the activity was to help out in the session when students had trouble doing something. I’m pleased to say, however, that that didn’t happen too much: the students were very awesome and learned so quickly!

They were first handed out a raspberry pi each with raspbian and other required software pre-installed and were shown how to remote control the pi with ssh and vnc and run an X session remotely without a monitor attached to the pi. They actually managed to run zenmap and find all the IP addresses of the raspberry pi, then pointed a browser to the IP addresses to find the one where they had written their name on apache’s default index page!

Each student remote controlled their own pi

Each student remote controlled their own pi!

They also got to log in to a single raspberry pi and run as many applications as possible to see how much it could take – I was surprised to see that even with 8 different tightvncserver sessions and several applications running, it could take the load up to a point!

A debian machine remote controlling a raspberry pi

A debian machine remote controlling a raspberry pi


Run ALL the apps!

Run ALL the apps!

Finally, there was a short session on HTML and JavaScript and building a simple application that would first just read a number from a textbox and then read 2 numbers and add them (it was supposed to be later developed into a full calculator). Again, I was pleasantly surprised to see how quickly they were able to figure out how to do this.

Overall, I was blown away by how organised the teacher and school was for the activity, and by how well-behaved and quick to learn the students were. We can definitely hope for a generation of more people interested in STEM fields if programs like this keep up!

I had an awesome time as a STEM ambassador for the activity, and I’m looking forward to supporting more activities in the future when I have time. I encourage anyone with an interest in STEM and getting more young people into the field to be an ambassador, it’s an amazing experience!


(Copyright info: You’re welcome to use the post text under CC-BY like my other posts, but please don’t use the pictures without permission as they weren’t taken by me)

by Errietta Kostala at June 28, 2014 06:30 PM

June 18, 2014

freenode staffblog

New extban: $j

We have loaded a new module on the network which provides the $j extban type:

$j:<chan> – matches users who are or are not banned from a specified channel

As an example…

/mode #here +b $j:#timbuktu

…would ban users from #here that are banned (+b) in #timbuktu.

Please note that there are a couple of gotchas:

  • Only matching +b list entries are checked. Quiets (+q) Exemptions (+e) & invexes (+I) are NOT then considered. As such, the following mode change would not alter the behaviour of the first example:

/mode #timbuktu +e *!*@*

  • Quiets and the quieting effect of bans may not immediately take effect on #here when #timbuktu’s ban list changes due to caching by the ircd.
  • $j isn’t recursive. Any $j extbans set in #timbuktu are ignored when matching in #here.

We imagine you’ll have some more useful use cases than the above.

Thanks for flying freenode!

by Pricey at June 18, 2014 09:34 PM

June 07, 2014

erry's blog

CSS sibling selectors

I recently discovered CSS sibling selectors, and an awesome way to take advantage of them. Needless to say, mind was blown so I had to blog about it!

First of all, about sibling selectors themselves: they match an element that’s on the same level as another element, either directly after it (adjacent sibling selector) or anywhere in the same level (general sibling selector).

For example, given the following CSS code:

h1 + p { color: red; }

And the following HTML:

<div>
  <h1>Hello!</h1>
  <p>This is red</p>
  <p>This isn't</p>
</div>

The first paragraph will be red because it’s right after an h1.

Likewise, if we use the general sibling selector:

h1 ~ p { color: red; }
<div>
  <h1>Hello!</h1>
  <div>We can even have another element in between.</div>
  <p>This is red.</p>
  <span>It doesn't matter if they're right next to each other as long as they're on the same level</span>
  <p>This is also red!</p>
  <div><p>However, this isn't red</p></div>
</div>
<p>this isn't red</p>

As you can see, any <p> tag that follows an <h1> tag in the same level of the DOM will match the CSS rule.

So, what’s so cool about this? How can you take advantage of it? Imagine the following scenario:

<div>
    <h1>Supernatural Directory</h1>
    <p>Match only:</p>

    <input type="radio" name="supernatural" id="all" checked/> <label for="all">All</label>
    <input type="radio" name="supernatural" id="werewolves" /> <label for="werewolves">Werewolves</label>
    <input type="radio" name="supernatural" id="fairies" /> <label for="fairies">Fairies</label>
    <input type="radio" name="supernatural" id="vampires" /> <label for="vampires">Vampires</label>

    <h1 class="directory werewolves all">Werewolves</h1>
    <ul class="directory werewolves all">
        <li>Rodney Robinson</li>
        <li>Matthew Phillips</li>
        <li>Rebecca Vasquez</li>
    </ul>
    <h1 class="directory fairies all">Fairies</h1>
    <ul class="directory fairies all">
        <li>Deborah Wong</li>
        <li>Sara Douglas</li>
        <li>Jean Gutierrez</li>
    </ul>

    <h1 class="directory vampires all">Vampires</h1>
    <ul class="directory vampires all">
        <li>Jesse Peters</li>
        <li>Michael Hunter</li>
        <li>Willie Washington</li>
    </ul>
</div>

We obviously want to show only one of the categories by pressing one of the radio buttons. Obviously, this is easy enough to do with javascript, but did you know you can do it with just CSS? By taking advantage of sibling selectors, you can!

First, let’s hide all the .directory elements:

.directory {
    display: none;
}

Now, if we do this:

#all:checked ~ .all {
    display: block;
}

This means that all elements with the ‘all’ class after the checked ‘all’ radio button will be visible! If you use this code, your whole directory will be visible again since ‘all’ is checked by default, and when you select another category nothing should be visible.

You probably know what to do from here, but if you just change that CSS to:

#all:checked ~ .all, #werewolves:checked ~ .werewolves, #fairies:checked ~ .fairies, #vampires:checked ~ .vampires {
    display: block;
}

Now, by toggling one of the checkboxes, the right category will toggle! This is perfect for making some effects without javascript.

In closing I’d like to give credit to the book that taught me this trick, “Responsive Web Design by Example” by Thoriq Firdaus. I think it’s a decent book for people like me that are technical but not that good at design, it teaches you to make some sexy-looking websites, and I hope that it’ll help me improve my design skills!

Until next time,

by Errietta Kostala at June 07, 2014 11:25 AM

May 17, 2014

RichiH's blog

git-annex corner case: Reviving a dead remote

Quoth joeyh:

Note that recent versions of git-annex have a reinit command that makes this much simpler:

git clone /existing/repo.annex

git annex info # to refresh your memory about uuid or description of lost repo

git annex reinit uuid|description

Old Post:

Another half a blog post, half a reminder for my future self.

Turns out that pulling an external 3.5" disk off of your nightstand with the help of a tangled USB cable is a surprisingly efficient way to kill a it. On the plus side, this yields instant results and complete success.

On the other hand, I kinda lost well over one TiB of personal photographs and other data which I didn't really appreciate a whole lot.

Thankfully, all data was annexed and I always maintain at least three copies of all files at all times, so the fix is as easy as getting two new disks and running

badblocks /dev/foo -swo $manufacturer-$model-$(date '+%F--%H-%M-%S-%Z').badblocks-swo # assuming you keep a git repo of this data

followed by partitioning, mkfs etc and

cd /existing/repo.annex
git annex info # not the UUID you need
cd /new/disk
git clone /existing/repo.annex
cd repo.annex
vim .git/config # add the [annex] block and _copy over the UUID of the lost repo_
git annex get # assuming you want a full copy, which I always do for data archival repos
git annex sync

and done. By running git annex get before git annex sync, I managed to avoid (potentially) saving information about missing data; I simply made sure it was all in place before synching again.

The second external disk allows me to always get one local disk up to speed and another one off-site. I am able to learn from experience ;)

by Richard &#x27;RichiH&#x27; Hartmann at May 17, 2014 07:23 PM

May 15, 2014

RichiH's blog

Interesting debate

It's nice to see that the overall tone of some key debates is slowly changing. The stance of not securing the whole stack piece by piece because some unrelated part in the stack is leaking information as well is steadily losing ground.

Another impact of this whole mess is that you can't really be certain why some parties are arguing against pervasive encryption any more. On the plus side, that makes defaulting to safety simpler.

by Richard &#x27;RichiH&#x27; Hartmann at May 15, 2014 10:55 AM

April 26, 2014

freenode staffblog

April 1st 2014, Followup

It’s been almost too long for this blog post to arrive here after the April Fools quiz this year. Thanks to everyone who participated!

The first ten people who completed the challenges are, in descending order of aprilness:

(times are listed in UTC)

  1. 2014-04-02T18:25:17 booto
  2. 2014-04-02T23:36:53 Fuchs *

  3. 2014-04-03T00:29:29 furry
  4. 2014-04-03T01:34:18 mniip
  5. 2014-04-03T09:41:38 jojo
  6. 2014-04-03T16:29:51 redi
  7. 2014-04-03T18:57:21 BlueShark
  8. 2014-04-04T15:33:24 larinadavid
  9. 2014-04-04T22:27:20 Omniflux
  10. 2014-04-04T23:02:19 apoc
  11. 2014-04-04T23:13:02 thommey

(*) user opted out of any prizes
There were 25 additional nicks who completed the quiz and made it to the winner’s circle but weren’t fast enough to place in the top 10.

The prizes were cloaks for those in the top-10. In addition to the top-10 cloaks everyone else who finished the challenge that ‘opted-in’ were eligible for the cloak lottery. This was a lottery for 3 runnerup cloaks.

Out of the 25 additional people that completed the challenge, the following 3 won a cloak through the cloak lottery:

  • skasturi
  • danielg4
  • jojoa1997

Here are the riddles and their solutions, in the original order:

  • Level 0
    • The clue was given in the April 1st blog post: IyMjI3hrY2Q=
    • That is the string "####xkcd" encoded using base64.
    • The answer: ####xkcd, which was the first channel in the quiz.
  • Level 1
    • Clue: Tnl2cHItbmFxLU9iby1qbnl4LXZhZ2Itbi1vbmU=
    • This is a rot13‘ed and base64′ed string.
    • In Python: "Tnl2cHItbmFxLU9iby1qbnl4LXZhZ2Itbi1vbmU=".decode('base64').decode('rot13')
    • The answer: ####Alice-and-Bob-walk-into-a-bar
  • Level 2
    • Clue: MKWkpKMa
    • This is another string that is encoded with a series of base64 and rot13 transformations.
    • In Python: "MKWkpKMa".decode('rot13').decode('base64').decode('rot13')
    • The answer: ####reddit
  • Level 3
    • Clue: SHg5RkR4SUpIeHFGSnlXVUlJSVFJeHFKCg== | Save this for a later level: https://i.imgur.com/87cX9y4.jpg | 4 decodes needed
    • Yet another string encoded with a series of base64 and rot13 transformations.
    • In Python: "SHg5RkR4SUpIeHFGSnlXVUlJSVFJeHFKCg==".decode('base64').decode('rot13').decode('base64').decode('rot13')
    • This yields: EBEORIETEMETHHPITI
    • Contestants were expected to do a web search for this and find out it is the end of the Zodiac Killer’s infamous message.
    • The answer: ####zodiac
  • Level 4
    • Clue: https://i.imgur.com/x4nejBh.png | LaTeX right direction | Google! | No maths needed
    • The topic changed several times as contestants seemed pretty stumped on this level, the topic line above was its final form.
    • The answer: ####exner – this was expected from figuring out what the equation is. Simply put, the equation in the image is Exner’s Equation.
  • Level 5
  • Level 6
    • Clue: https://www.dropbox.com/s/emz7xy3p9r2ivxe/wat.unknown (verify the file, sha256sum: 0efade1bb29d1b7fdd65e5612159e262cbd41a2e27ed89a0144701a5556da68f)
    • This file is more stenography:
      • Use ‘file‘ to determine what the file type is.
      • Un-7zip the .unknown file
      • Base64 decode the output
      • Use ‘file’ to determine that the output is a .jpg
      • Unzip the .jpg
      • Untar two.tar.gz
      • Open the surprised.txt file.
    • The content of surprised.txt is: ####ImSoMetaEvenThisAcronym
    • The answer: ####ImSoMetaEvenThisAcronym
  • Level 7
    • Clue: AQwPfPN1ZBXNfvNj4bPmVR4fVQYPfPNlZBXNfvNkAP4jZhXNflOS and “Da Vinci” | Jules Verne | s/.02/.03/ in the decrypted text
    • The clue is base64′ed and rot13′ed. To decode it in Python: print "AQwPfPN1ZBXNfvNj4bPmVR4fVQYPfPNlZBXNfvNkAP4jZhXNflOS".decode('rot13').decode('base64')
    • This yields: 48° 50′ 0″ N, 2° 20′ 14.02″ E
    • These are GPS coordinates for the Paris meridian.
    • From this and the “Da Vinci” clue contestants were expected to find the Wikipedia page about the Rose Line.
    • The specific quote that contestants were suppose to find:
      "Dan Brown simply invented the 'Rose Line' linking Rosslyn and Glastonbury. The name 'Roslin' definitely does not derive from any 'hallowed Rose Line'. It has nothing to do with a 'Rose Bloodline' or a 'Rose Line meridian'. There are many medieval spellings of 'Rosslyn'. 'Roslin' is certainly not the 'original spelling': it is now the most common spelling for the village."[18]

      Source

    • The “Jules Verne” clue is suppose to reaffirm to contestants that they were on the right track:
      The competition between the Paris and Greenwich meridians is a plot element in Jules Verne's "Twenty Thousand Leagues Under the Sea", published just before the international decision in favor of the British one.

      Source

    • The answer: ####roslin
  • Level 8
  • Level 9
    • Clue: ZCLVLLCOIUTKKJSCEKHHHSMKTOOPBA | OGUCSSGAPVGVLUMBTVOGICUNJDHSTB | RUTJJGNXUNTY | Letters that would repeat in a typical word do not repeat in the key(s), example ‘freenode’ would be ‘frenod’ | https://i.imgur.com/pGIBjEE.png | http://is.gd/TgNsvm
    • Alright this one is really really really tricky. The topic changed several times.
    • The three strings are encoded with Four-square from the previous level with the same keys.
    • Contestants were expected to use ‘UVB’ and ‘RUSSIA’ as keys for the Four-square cipher.
    • It was expected that contestants arrive at ‘UVB’ from the channel name, ####POVAROVOSOLNECHNOGORSKRUSSIA
    • The former transmitter[27] was located near Povarovo, Russia[28] at 56°5′0″N 37°6′37″E which is about halfway between Zelenograd and Solnechnogorsk and 40 kilometres (25 mi) northwest of Moscow, near the village of Lozhki.

      Source

    • The is.gd link points to a file that has the “No Q” image from a previous level hidden in it.
    • The “RUTJJGNXUNTY” decrypts to AaronHSwartz
    • The answer: ####AaronHSwartz
  • Level 10
    • Clue: HKGJSUOJVRLGSBELAUHOUIGLVRURWMGTUGJGWTKN
    • Originally this channel (####AaronHSwartz) was suppose to be the winner’s circle, however due to too many people leaking answers and channel names, one more challenge was added.
    • Same cipher as before, this time the keys were ‘DEMAND’ and ‘PROGRESS’
    • Demand Progress is an Internet activist-related organization specializing in petitions to help gain traction for legal movements against Internet censorship and related subjects, started by Aaron Swartz, source.
    • The clue decrypts to JOINUSNOWANDSHARETHESOFTWAREWRITTENBYRMS
    • RMS is Richard Matthew Stallman, and ‘Join Us Now and Share the Software’ is an openly licensed song by Richard Stallman.
    • The answer: ####JOINUSNOWANDSHARETHESOFTWAREWRITTENBYRMS

The topic in ####JOINUSNOWANDSHARETHESOFTWAREWRITTENBYRMS was: Congratulations on solving the freenode’s April Fools 2014 Crypto Challenge | Want MOAR? #ircpuzzles

Congratulations to those who participated this year!

The 25 additional people that completed the challenge:

  • 2014-04-05T04:06:53 knivey
  • 2014-04-05T10:00:12 Tordek
  • 2014-04-05T15:40:50 jacob1 *
  • 2014-04-05T15:48:48 stac
  • 2014-04-05T16:24:01 Changaco *
  • 2014-04-05T17:30:01 Arch-TK *
  • 2014-04-05T17:35:05 ar *
  • 2014-04-05T18:16:20 Weetos *
  • 2014-04-05T18:38:39 nyuszika7h
  • 2014-04-05T18:56:26 vi[NLR]
  • 2014-04-05T19:06:38 tkd *
  • 2014-04-05T21:54:56 Chiyo
  • 2014-04-05T22:46:01 slidercrank
  • 2014-04-05T22:54:10 jojoa1997
  • 2014-04-06T00:55:51 Pixelz *
  • 2014-04-06T02:53:25 Transfusion
  • 2014-04-06T02:58:15 DonkeyHotei
  • 2014-04-06T03:04:01 sdamashek *
  • 2014-04-06T03:07:49 Cypi *
  • 2014-04-06T03:36:03 FXOR
  • 2014-04-06T13:44:35 pad
  • 2014-04-06T19:22:06 skasturi
  • 2014-04-06T19:37:13 Bloodhound
  • 2014-04-07T08:16:22 molly *
  • 2014-04-07T14:42:32 Bijan-E

(*) user opted out of the cloak lottery

by yano at April 26, 2014 06:05 PM

April 18, 2014

RichiH's blog

higher security

Instant classic

Trusted:

NO, there were errors:
The certificate does not apply to the given host
The certificate authority's certificate is invalid
The root certificate authority's certificate is not trusted for this purpose
The certificate cannot be verified for internal reasons

Signature Algorithm: md5WithRSAEncryption
    Issuer: C=XY, ST=Snake Desert, L=Snake Town, O=Snake Oil, Ltd, OU=Certificate Authority, CN=Snake Oil CA/emailAddress=[email protected]
    Validity
        Not Before: Oct 21 18:21:51 1999 GMT
        Not After : Oct 20 18:21:51 2001 GMT
    Subject: C=XY, ST=Snake Desert, L=Snake Town, O=Snake Oil, Ltd, OU=Webserver Team, CN=www.snakeoil.dom/emailAddress=[email protected]
...
            X509v3 Subject Alternative Name: 
            email:[email protected]

For your own pleasure:

openssl s_client -connect www.walton.com.tw:443 -showcerts

or just run

echo '
-----BEGIN CERTIFICATE-----
MIIDNjCCAp+gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBqTELMAkGA1UEBhMCWFkx
FTATBgNVBAgTDFNuYWtlIERlc2VydDETMBEGA1UEBxMKU25ha2UgVG93bjEXMBUG
A1UEChMOU25ha2UgT2lsLCBMdGQxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhv
cml0eTEVMBMGA1UEAxMMU25ha2UgT2lsIENBMR4wHAYJKoZIhvcNAQkBFg9jYUBz
bmFrZW9pbC5kb20wHhcNOTkxMDIxMTgyMTUxWhcNMDExMDIwMTgyMTUxWjCBpzEL
MAkGA1UEBhMCWFkxFTATBgNVBAgTDFNuYWtlIERlc2VydDETMBEGA1UEBxMKU25h
a2UgVG93bjEXMBUGA1UEChMOU25ha2UgT2lsLCBMdGQxFzAVBgNVBAsTDldlYnNl
cnZlciBUZWFtMRkwFwYDVQQDExB3d3cuc25ha2VvaWwuZG9tMR8wHQYJKoZIhvcN
AQkBFhB3d3dAc25ha2VvaWwuZG9tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQC554Ro+VH0dJONqljPBW+C72MDNGNy9eXnzejXrczsHs3Pc92Vaat6CpIEEGue
yG29xagb1o7Gj2KRgpVYcmdx6tHd2JkFW5BcFVfWXL42PV4rf9ziYon8jWsbK2aE
+L6hCtcbxdbHOGZdSIWZJwc/1Vs70S/7ImW+Zds8YEFiAwIDAQABo24wbDAbBgNV
HREEFDASgRB3d3dAc25ha2VvaWwuZG9tMDoGCWCGSAGG+EIBDQQtFittb2Rfc3Ns
IGdlbmVyYXRlZCBjdXN0b20gc2VydmVyIGNlcnRpZmljYXRlMBEGCWCGSAGG+EIB
AQQEAwIGQDANBgkqhkiG9w0BAQQFAAOBgQB6MRsYGTXUR53/nTkRDQlBdgCcnhy3
hErfmPNl/Or5jWOmuufeIXqCvM6dK7kW/KBboui4pffIKUVafLUMdARVV6BpIGMI
5LmVFK3sgwuJ01v/90hCt4kTWoT8YHbBLtQh7PzWgJoBAY7MJmjSguYCRt91sU4K
s0dfWsdItkw4uQ==
-----END CERTIFICATE-----
' | openssl x509 -noout -text

At least they're secure against heartbleed.

by Richard &#x27;RichiH&#x27; Hartmann at April 18, 2014 10:22 AM

April 17, 2014

erry's blog

Fallback for HTML5 date input

HTML5 is awesome. It gives us so many things that we previously had to do manually! Unfortunately, not all browsers support it yet.
Personally, I’m eager to use all the new features, but I don’t want to sacrifice browser support. For example, I don’t want to use jquery ui to render a date picker if a browser supports html5 <input type=”date”>.
Fortunately, you can check if a browser supports the HTML5 way, and use Jquery UI even if it doesn’t! I used Modernizr for this, and it’s quite awesome. As you can see, there are many many things it can detect, but for this particular instance, you can build a bundle with only “input types” (I might do a more extensive post on Modernizr later… hmmm).

First of all, build a Modernizr bundle with the options you want (or just “input types” like me) and download the resulting .js file. Assuming you already have jquery ui installed, your code should look like this

<html>
    <head>
        <title></title>
        <!-- The real path to the jquery ui css... !-->
        <link href="css/jquery-ui.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <input type="text" class="date" />
        <!-- Path to jquery & jquery ui !-->
        <script src="js/jquery.js"></script>
        <script src="js/jquery-ui.js"></script>
        <script>
            $( ".date" ).datepicker();
        </script>
    </body>
</html>

Which will work fine, but I still prefer the native datepicker where available. This is where Modernizr comes in handy! First of all, load the Modernizr script as well.

        <script src="js/modernizr.js"></script>

You can now use the date input type like you usually would:

        <input type="date" />

As long as you also add this somewhere in your javascript code:

<script>
if (!Modernizr.inputtypes.date) {
    $( "input[type=date]" ).datepicker();
}
</script>

That easy! Now if the date input type isn’t supported, all your <input type=”date”> will automatically use the jquery ui datepicker. Additionally, you will automatically have the native solution when these browsers start supporting it, without you having to change anything else in the future.

(altogether, the code should look like this):

<html>
    <head>
        <title></title>
        <!-- The real path to the jquery ui css... !-->
        <link href="css/jquery-ui.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <input type="date" />
        <!-- Path to jquery & jquery ui !-->
        <script src="js/jquery.js"></script>
        <script src="js/jquery-ui.js"></script>
         <script>
         if (!Modernizr.inputtypes.date) {
             $( "input[type=date]" ).datepicker();
         }
         </script>
    </body>
</html>



Native datepicker in chrome:

native date input


Jquery ui datepicker in firefox:

jquery ui date picker

That’s all for now! (Should I blog about modernizr more…? Let me know on twitter (@errietta) :p)

‘Till next time!

by Errietta Kostala at April 17, 2014 05:08 PM

mrmist's blog

Telephoney Rant


This article is tagged with: ,

2 Months after submitting our “home move” order and almost 2 months since moving in to our new home, we have no phone line from BT. We’re incredibly lucky that the area is serviced by virgin cable, so we have managed to obtain alternative Internets, otherwise I dare say I would be apocalyptic with rage. As it is, I’m just on “simmer”, instead. I was actually moved to write a real letter to the company yesterday, after what was probably my fifth or sixth “update” – updates that is that don’t really update anything other than the next time that we’ll be called with an update. Pathetic.

by Mrmist at April 17, 2014 08:38 AM

April 16, 2014

RichiH's blog

secure password storage

Dear lazyweb,

for obvious reaons I am in the process of cycling out a lot of passwords.

For the last decade or so, I have been using openssl.vim to store less-frequently-used passwords and it's still working fine. Yet, it requires some manual work, not least of which manually adding random garbage at the start of the plain text (and in other places) every time I save my passwords. In the context of changing a lot of passwords at once, this has started to become tedious. Plus, I am not sure if a tool of the complexity and feature-set of Vim is the best choice for security-critical work on encrypted files.

Long story short, I am looking for alternatives. I did some research but couldn't come up with anything I truly liked; as there's bound to be tools which fit the requirements of like-minded people, I decided to ask around a bit.

My personal short-list of requirements is:

  • Strong crypto
  • CLI-based
  • Must add random padding at the front of the plain text and ideally in other places as well
  • Should ideally pad the stored file to a few kB so size-based attacks are foiled
  • Must not allow itself to be swapped out, etc
  • Must not be hosted, cloud-based, as-a-service, or otherwise compromised-by-default
  • Should offer a way to search in the decrypted plain text, nano- or vi-level of comfort are fine
  • Both key-value storage or just a large free-form text area would be fine with a slight preference for free-form text

Any and all feedback appreciated. Depending on the level of feedback, I may summarize my own findings and suggestions into a follow-up post.

by Richard &#x27;RichiH&#x27; Hartmann at April 16, 2014 06:47 AM

April 15, 2014

freenode staffblog

Heartbleed

The recently exposed heartbleed bug in the OpenSSL library has surprised everyone with a catastrophic vulnerability in many of the world’s secure systems.

In common with many other SSL-exposed services, some freenode servers were running vulnerable versions of OpenSSL, exposing us to this exploit. Consequently, all of our affected services have been patched to mitigate the vulnerability, and we have also regenerated our private SSL keys and certificates.

In an unrelated event, due to service disruption & the misconfiguration of a single server on our network, an unauthorised user was allowed to use the ‘NickServ’ nickname for a short period Sunday morning. Unfortunately there is a possibility that your client sent data (including your freenode services password) to this unauthorised client. Identification via SASL, certfp or server password were not affected, but any password sent directly to the “NickServ” user might have been.

Because of these two recent issues, we would like to make the following recommendations to all of our users. It would also be good practice to follow them at regular intervals.

  • Though we are not aware of any evidence that we have been targeted, or our private key compromised, this is inevitably a possibility. SSL sessions established prior to 2014/04/12 may be vulnerable. If your current connection was established prior to this date via ssl then you should consider reconnecting to the network.
  • We would advise that users reset their password (after reconnecting) using instructions returned by the following command:

/msg nickserv help set password

This should help ensure that if your password was compromised through an exploitation of the Heartbleed vulnerability, the damage is limited.

  • In line with general best practice, we would always recommend using separate passwords on separate systems – if you shared your freenode services password with other systems, you should change your password on all of these systems; preferably into individual ones.
  • If you use CertFP, you should regenerate your client certificate (instructionsand ensure that you update NickServ with the new certificate hash. You can find out how to do this using the following command:

/msg nickserv help cert

  • Having changed passwords and/or certificate hashes, it cannot hurt to verify your other authentication methods (such as email, ACCESS or CERT). It is possible you have additional access methods configured either from past use or (less likely) due to an account compromise.
  • Finally, it is worth noting that although probably the least likely attack vector, Heartbleed can also be used as client-side attack, i.e. if you are still running a vulnerable client a server could attack you. This could be a viable attack if, for instance, you connect to a malicious IRC server and freenode at the same time; hypothetically the malicious IRC server could then attack your client and steal your IRC password or other data. If affected, you should ensure your OpenSSL install is updated and not vulnerable then restart your client.

As ever, staff are available in #freenode to respond to any questions or concerns.

by Pricey at April 15, 2014 07:35 PM

April 14, 2014

RichiH's blog

git-annex corner case: Changing commit messages retroactively and after syncing

This is half a blog post and half a reminder for my future self.

So let's say you used the following commands:

git add foo
git annex add bar
git annex sync
# move to different location with different remotes available
git add quux
git annex add quuux
git annex sync

what I wanted to happen was to simply sync the already committed stuff to the other remotes. What happened instead was git annex sync's automagic commit feature (which you can not disable, it seems) doing its job: Commit what was added earlier and use "git-annex automatic sync" as commit message.

This is not a problem in and as of itself, but as this is my my master annex and as I managed to maintain clean commit messages for the last few years, I felt the need to clean this mess up.

Changing old commit messages is easy:

git rebase --interactive HEAD~3

pick the r option for "reword" and amend the two commit messages. I did the same on my remote and all the branches I could find with git branch -a. Problem is, git-annex pulls in changes from refs which are not shown as branches; run git annex sync and back are the old commits along with a merge commit like an ugly cherry on top. Blegh.

I decided to leave my comfort zone and ended up with the following:

# always back up before poking refs
git clone --mirror repo backup

git reset --hard 1234
git show-ref | grep master
# for every ref returned, do:
  git update-ref $ref 1234

rinse repeat for every remote, git annex sync, et voilà. And yes, I avoided using an actual loop on purpose; sometimes, doing things slowly and by hand just feels safer.

For good measure, I am running

git fsck && git annex fsck

on all my remotes now, but everything looks good up to now.

by Richard &#x27;RichiH&#x27; Hartmann at April 14, 2014 10:46 PM

April 13, 2014

erry's blog

Using jquery to make all forms ajax-powered

Hi,

I recently wrote a piece of hacky javascript to automatically make all my forms powered by AJAX + JSON. I still had to write functions to handle this JSON data, but it saved me time over retrieving values from the form and performing an AJAX request for every form. Plus, if I didn’t need to use JSON and just used DOM to change the innerHTML of something, I’d have saved more time yet.

This is my (commented!) javascript code (note that it requires jquery):

function getSubmits(form) {
    return $(form).children("input[type=submit],button[type=submit]");
}

//default json handler: just return a div with the result as its innerHTML
function default_json_handler(json) {
    var div = $('<div/>');
    div.html (json);
    return div;
}

function ajaxify() {
    var forms = $('form'); //ALL the forms!
    var sidx  = 0;

    forms.unbind();

    forms.each (function(idx, form) {
        //We'll hold the handler function in the form's data-json-handler
        //attribute. window['function_name'] will tell us if the function
        //exists
        var jsonh   = window[$(form).attr('data-json-handler')]
                        || default_json_handler;
        var submits = getSubmits(form);
        submits.unbind();

        var method  = $(form).attr('method');
        var url     = $(form).attr('action');

        submits.click(
            function(e) {
                //the data that we'll submit through ajax
                var params     = [];

                //This may be useful to let your script
                //know that the data is being posted through
                //AJAX; for example I use this to return JSON
                //instead of XML.
             
                params['ajax'] = [1];

                if(!$(this).attr('data-sidx')) {
                    $(this).attr('data-sidx', sidx++);
                }

                //since we clicked a submit button, we'll be submitting
                //its value if it has a name...
                if (name = $(this).attr('name')) {
                    params[name] = [encodeURIComponent($(this).val())];
                }

                //get the rest of the input values
                params  = getInputValues($(this), params);
                //get the request string
                params  = getRequestStr(params);

                //We use this ID so that we have a specific result div for
                //each of our submit buttons
                var div = $('#result' + $(this).attr('data-sidx'));

                if (!div[0]) {
                    div = $('<div/>',{id: 'result' + $(this).attr('sidx')})
                    .appendTo(form);
                }
               
                $.ajax(url, {
                  dataType  :'json',
                  method    :method,
                  data      :params,
                  success   :function(data) {
                    div.empty();
                    div.hide();
                    //call the json handler and append the result.
                    div.append(jsonh(data));
                    div.fadeIn(800);
                    div.show(500, function() {
                        $('html, body').animate({
                            scrollTop: $(div).offset().top
                        }, 1000);
                    });
                  },
                  beforeSend :function() {
                    div.empty()
                    div.append(
                        $("<img src='static/img/loading.gif' />")
                    );
                  }
                });

                e.stopPropagation();
                e.preventDefault();
                return false;
            }
        );
    });
}

//gets a form's input values

function getInputValues(btn, params) {
    //get all the input values, but only checked
    //radios and checkboxes!
    var inputs = btn.parent().find(
        //all inputs that match the criteria
        'input[type!=submit]' +  // We've already handled this
            '[type!=radio]' +    // we handle this below 
            '[type!=checkbox]' + // likewise
            '[type!=button],' +  // their value isn't submitted...
                                 // unless they're submit buttons which
                                 // we've handled.

        ':checked,' +           // elements with the :checked state.
        'select,' +             // selects
        'textarea'              // textareas
    );

    inputs.each(function(idx, input) {
        var name  = $(input).attr('name');
        var value = $(input).val();

        if (name) {
            if (!params[name]) {
                params[name] = []; // We use an array here so that we can handle
                                   // Multiple elements with the same name (checkboxes?)
            }

            params[name].push(encodeURIComponent(value));
        }
    });

    return params;
}

// gets the request string from an array of parameters.
function getRequestStr(params) {
    paramstr ='';

    for (var key in params) {
        for (var i in params[key]) {
            paramstr += (paramstr?'&':'') + key + '=' + params[key][i];
        }
    }

    return paramstr;
}

//calls the ajaxify function on document load
$(document).ready(ajaxify);

This is how our form looks like:

<!-- Pay attention to data-json-handler="result"! It's what we'll use
to handle the json result from ajax! !-->
<form method="post" action="script.php" data-json-handler="result">
<label>
    Text input:<br/>
    <input type="text" name="text" />
</label><br/>

<label>
    Password input:<br/>
    <input type="password" name="password" />
</label><br/>

<label>
    Textarea:<br/>
    <textarea name="textarea"></textarea>
</label><br/>

<label>
    Email input:<br/>
    <input type="email" name="email" />
</label><br/>

<label>
    Date input:<br/>
    <input type="date" name="date" />
</label><br/>


<label>
   <input type="radio" name="radio" value="1" /> Radio option 1
</label><br/>

<label>
   <input type="radio" name="radio" value="2" /> Radio option 2
</label><br/>

<label>
   <input type="radio" name="radio" value="3" /> Radio option 3
</label><br/>


Checkboxes:<br/>

<label>
   <input type="checkbox" name="check[]" value="1" /> Checkbox option 1
</label><br/>

<label>
   <input type="checkbox" name="check[]" value="2" /> Checkbox option 2
</label><br/>

<label>
   <input type="checkbox" name="check[]" value="3" /> Checkbox option 3
</label><br/>

Select:<br/>
<select name="select">
    <option value="">Please select...</option>
    <option value="Cow">Moo!</option>
    <option value="Pig">Oing oing!</option>
    <option value="Cat">Meow!</option>
    <option value="Dog">Woof!</option>
</select>
<br>
<input type="submit" name="submit" value="Submit with name!" />
<button type="submit" name="submit2" type="button" value="submit 2!">Submit &lt;button&gt; with name!</button>
<input type="submit" value="Submit without name!" />

</form>

<script src="jquery.js"></script>
<script src="ajaxify.js"></script>
<script src="jsonhandlers.js"></script>

And our jsonhandlers.js script will have the functions that actually handle the json result. For example:

function result(data) {

    var table = $("<table><tr><th>Name</th><th>Email</th>");
    for (var i = 0; i < data.length; i++) {
        table.append($("<tr><td>" + data[i].name + "</td><td>" + data[i].email + "</td></tr>"));
    }
    
    return table;
}

Of course, script.php will have to return appropriate data. For this example, I just used:

<?php
     echo json_encode(
        array (
            (object)
            array (
                'name' => 'User 1',
                'email' => '[email protected]'
            ),
            (object)
            array (
                'name' => 'User 2',
                'email' => '[email protected]'
            ),
        )
    );
?>

But in the real world you’d use the input to get data.

You can now see how the form works. You’ll notice when you submit it you get your results in a table, and if you inspect the HTTP request you should see all the data that is sent:

form

You can verify that all the input values are sent to the PHP page.

tl;dr: What our javascript code does is go through every form, and add a handler to the submit buttons. When they are clicked, it uses jquery to find the right elements and get their value. It then constructs a string to perform an AJAX request, including ajax=1 so that our server script knows it was submitted with ajax and act accordingly. After the result is acquired, the function set on the form’s data-json-handler attribute is called and it’s up to you how you handle the ajax.

If you want to change the JSON result behaviour just change the data-json-handler of each form!

That’s all for now. Hopefully this was helpful and not too confusing!

by Errietta Kostala at April 13, 2014 05:14 PM

April 02, 2014

erry's blog

GIMP tutorial – Designing freenode’s April fool’s blog post

Note: This is a (late) April fool’s post. It’s not to be taken seriously.

Hi,

So I’m a member of freenode‘s volunteer staff. I mostly deal with user support tasks and development, but I sometimes volunteer on other tasks as well. In this case, I took on the task of helping write our April fool’s blog post. I created the image that you see in the blog post.

I will now teach you how to create an image so horrible it can be used in freenode’s April fools blog post, too!
Open GIMP and create a 2648 x 852 image. The hugeness of it contributes to it looking like a scanned newspaper release.

We will start with the easy elements. Grab the text tool and type ‘For immediate release: Tuesday, April 1, 2014′ in 20pt Comic Sans MS. put the text on the top left of the image, with some margin. Then, drag a guide from the left ruler to where the text is, so that the rest of our elements will be aligned well.

You should now get something like this:

step1

Next, create the freenode logo in Google font. Luckily, there are web tools that do this; we will be using http://myog.searchmyway.com/?name=freenode

Take a screenshot of that logo, and open the screenshot in GIMP. If necessary, crop the logo to size so it has no extra white space; you can do this with Image -> Autocrop image. Now, copy your logo and go back to your original image. Make sure you have the ‘layers’ panel up; if not it’s in Window -> Dockable Dialogues -> Layers, or ctrl + L. On that window, the bottom left button creates a new layer. Click on it, then paste your logo. Then go go layer -> Anchor Layer, or press ctrl + H. If you did this right, you should have your logo in its own layer. You can give it a name, such as ‘logo’ to make things easier. Also, go to layer -> autocrop layer so that the layer is the right size.

You should have something like this at this point.

2

Now here’s the tricky part: we want to insert our crypto’s challenge first clue, ‘IyMjI3hrY2Q=’ into the ‘o’ in ‘freenode’. To do that, we need to stretch our logo out enough. First, use the text tool to actually write ‘IyMjI3hrY2Q=’ in monospace font and size 18. Place it anywhere for now. Now, drag your logo layer near the date, with some margin, and start scaling it proportionately (select the scale tool, and make sure the little chain icon is pressed) until it fills the whole image width. After that, select the scale tool again, but this time make sure the icon is not active (so press it again). Finally, reduce the layer height until it’s around 240 pixels. It’s okay if it’s slightly more or slightly less.
You can now drag your text layer (the IyMjI3hrY2Q= one) to be inside of the ‘o’.

At this point, you should have something like this:

3

Now the hard part is done. For the next step, we will add the main text. Select the text tool, and click on near your guide (and with some margin under the logo) and start dragging the text tool until it takes almost full width. Copy and paste the following text:

It is with great pleasure that we announce the integration between our network services and Google+. We have been working very closely with Google over the past year trying to implement Google+ into our network and would like to thank them for partnering with us on this initiative as they seek to drive adoption of Google+. We feel our respective missions are synergistically aligned and we look forward to collaborating further. Email verification will shortly be deprecated and will be phased out in the near future, to be replaced by a requirement for a Google+ profile.

To apply for the beta test of the integration you can simply send:
/msg NickServ SET PROPERTY GOOGLE+ ON

Once accepted into the beta, a welcome email will appear in your inbox hyperlinking to a +freenode page in your google account settings. Following the easy prompts will grant you the ability to turn on authentication passthrough. A dedicated GooServ will be loaded shortly as features mature, including the inevitable amalgamation into hangouts. Please stay tuned!

Please note, we have yet to load a new version of the help documentation that contains the Google+ integration steps into NickServ.

If you need any further assistance please don’t hesitate to join #freenode-google+ and ask your questions.

If the text is not in the right place, feel free to move the layer with the arrow keys. Hold shift for bigger movements. What you’re aiming for, is something like this.

4

Use the text tool again, but this time right under the freenode logo. Write ‘Official staff newspaper’ in size 19 bold Comic Sans MS Bold. You can drag a guide across to where the logo is to make sure you have placed your text in the right place. Once again, do feel free to move the text after you’ve created it, if needed.

5

Now for the last part, the pony image. We will steal this from our April 2012 blog post image, http://blog.freenode.net/wp-content/uploads/2012/04/ITucplOwnTShozIfVT1cM2u0VTWyVPZwp3EupaD.jpg. Simply bring in that image to GIMP and crop out the pony logo. Then, copy that image, and go back to your original. Make a new layer once again, paste the pony, and anchor the layer, like we did a while ago. You can also layer->autocrop layer if you want. Now, move the layer so that it touches the first guide, and leave some margin from the bottom, like so:

6

Congratulations, the hard work is done! Save your work, and export it as a png. (File->Export as…)

Now you may have noticed that the blog post image looks more like a scanned document. It’s ok, I’ll show you how to do this, too. It’s actually very easy, thanks to http://tex.stackexchange.com/questions/94523/simulate-a-scanned-paper.

Simply replace the command there to suit your filenames:

convert freenode.png \( +clone -blur 0×1 \) +swap -compose divide -composite -gamma 0.1 -linear-stretch 5%x0% -rotate 1.5 as-scanned.png

Note: the ‘convert’ command is part of the ‘imagemagick’ package.
And you should have your final result:

as-scanned

However, it seems that that image was too big for the blog.
Oh dear! But you can probably tell how I made the final image out of that: simply increase the canvas height as much as needed, and drag the text box to take over that area. Afterwards, use guides to move elements around as needed (you made layers, right!?). Because we need to resize the logo, I decided to move the hint, too.
You’ll easily get this:

10

That’s it, you’ve created an official page of the freenode staff newspaper, and made it look like it was scanned. Congratulations!

(Once again, this post, and of course the original one on blog.freenode.net is a bit of an April fool’s joke. My design skills aren’t normally THAT bad :D)

by Errietta Kostala at April 02, 2014 10:00 AM

freenode staffblog

+freenode

UPDATE: This was of course an April Fool… you can “/msg nickserv set property GOOGLE+” to remove the property from your account. There might still be other secrets within the message though…

freenode4

Edit: Previous versions of the post contained an incorrect NickServ command. We have corrected this and apologise for the inconvenience.

by Pricey at April 02, 2014 08:15 AM

April 01, 2014

Md's blog

Real out of band connectivity with network namespaces

This post explains how to configure on a Linux server a second and totally independent network interface with its own connectivity. It can be useful to access the server when the regular connectivity is broken.

This can happen thanks to network namespaces, a virtualization feature available in recent kernels.

We need to create a simple script to be run at boot time which will create and configure the namespace. First, move in the new namespace the network interface which will be dedicated to it:

ip netns add oob
ip link set eth2 netns oob

And then configure it as usual with iproute, by executing it in the new namespace with ip netns exec:

ip netns exec oob ip link set lo up
ip netns exec oob ip link set eth2 up
ip netns exec oob ip addr add 192.168.1.2/24 dev eth2
ip netns exec oob ip route add default via 192.168.1.1

The interface must be configured manually because ifupdown does not support namespaces yet, and it would use the same /run/network/ifstate file which tracks the interfaces of the main namespace (this is also a good argument in favour of something persistent like Network Manager...).

Now we can start any daemon in the namespace, just make sure that they will not interfere with the on-disk state of other instances:

ip netns exec oob /usr/sbin/sshd -o PidFile=/run/sshd-oob.pid

Netfilter is virtualized as well, so we can load a firewall configuration which will be applied only to the new namespace:

ip netns exec oob iptables-restore < /etc/network/firewall-oob-v4

As documented in ip-netns(8), iproute netns add will also create a mount namespace and bind mount in it the files in /etc/netns/$NAMESPACE/: this is very useful since some details of the configuration, like the name server IP, will be different in the new namespace:

mkdir -p /etc/netns/oob/
echo 'nameserver 8.8.8.8' > /etc/netns/oob/resolv.conf

If we connect to the second SSH daemon, it will create a shell in the second namespace. To enter the main one, i.e. the one used by PID 1, we can use a simple script like:

#!/bin/sh -e
exec nsenter --net --mount --target 1 "$@"

To reach the out of band namespace from the main one we can use instead:

#!/bin/sh -e
exec nsenter --net --mount --target $(cat /var/run/sshd-oob.pid) "$@"

Scripts like these can also be used in fun ssh configurations like:

Host 10.2.1.*
 ProxyCommand ssh -q -a -x -N -T server-oob.example.net 'nsenter-main nc %h %p'

April 01, 2014 04:26 PM

March 25, 2014

RichiH's blog

Train them to submit

And today from our ever-growing collection of what the fuck is wrong with you people?!...

This is wrong on so many levels, I can't even begin to describe it. Sadly, it seems that this will get funded. And if it does not, technology will only become cheaper over time...

by Richard &#x27;RichiH&#x27; Hartmann at March 25, 2014 02:11 PM

March 22, 2014

RichiH's blog

Lenovo X1 Carbon

Christine's accidential blog spam on planet.d.o just now gave me the chance to re-read the comments in her post.

The state from back then is still the current state on up-to-date Debian unstable:

  • Microphone button does nothing.
  • USB 3.0 docking station's display does not work thanks to the content mafiaa with an admittedly small petition trying to fix this. Obviously it is going to be ignored.
  • No one using Debian seems to care about the fingerprint reader.
  • UMTS/WWAN modem works fine on Windows, but Linux loses connection to the USB device all the time. As a result, UMTS does not work

The last item has the most impact on me. The need to tether when you have a dedicated SIM card, built-in modem, and good antennas in your laptop is... infuriating. Especially as it's working as intended on Windows.

As an added benefit, even though I saved the PIN in network manager, it asks me for the PIN every time I log in and every time after hibernating. For a device which I can't use in the first place. Argh!

by Richard &#x27;RichiH&#x27; Hartmann at March 22, 2014 07:04 AM

March 14, 2014

RichiH's blog

Git prize: Outstanding Contribution to Open Source/Linux/Free Software

In February, Linux Magazine contacted me, asking if I would be willing to accept the Linux New Media Award 2014 in the main category "Outstanding Contribution to Open Source/Linux/Free Software" on behalf of the Git community due to my involvement with evangelizing and vcsh. Needless to say, I was thrilled.

I managed to poke Junio via someone at Google and he agreed. We also reached out within the German Git community and two maintainers of git submodule, Jens Lehmann and Heiko Voigt, joined in as well. While we didn't manage to hammer out interoperability issues of vcsh and git submodule due to time constraints and too much beer, we are planning to follow up on that.

Git beat OpenStack, Python, and Ubuntu by a huge margin; sadly I don't have exact numbers (yet).

More details and a rather crummy photo can be found in Linux Magazine's article. A video of the whole thing will uploaded to this page soonish. If it appears that we kept our "speech" very short, that was deliberate after the somewhat prolonged speeches beforehand ;)

The aftershow event was nice even though the DJ refused to turn down the music down to tolerable levels; his reaction to people moving father away, and asking him to turn down the volume a bit, was to turn it up... Anyway, given the mix of people present during the award ceremony, very interesting discussions ensued. While I failed to convert Klaus Knopper to zsh and git, at least there's a chance that Cornelius Schuhmacher will start using vcsh and maybe even push for a separation of configuration and state in KDE.

The most interesting tidbits of the evening were shared by Abhisek Devkota of cyanogenmod fame. Without spilling any secrets it's safe to say that the future of cyanogenmod is looking extremely bright and that there are surprises in the works which will have quite the impact.

Last but not least, here's the physical prize:

Glass trophy held by your's truly

by Richard &#x27;RichiH&#x27; Hartmann at March 14, 2014 11:19 PM

March 08, 2014

RichiH's blog

Panem et congressūs

There's a German joke about Germans:

Q: How can you find Germans abroad?
A: You stand outside a bakery and wait until someone starts cussing.

I am constantly reminded of this joke, abroad and back home, as we do tend to take our bread seriously...

As an additional data point supporting this, the German local team just spent 20 minutes discussing bread. I somehow doubt you could make an off-hand comment about bread and garner much interest in most communities; in a German one, this works.

In mildly related news, this is what bits.debian.org will pick up soon:

During the DebConf committee meeting, it has been decided that the 16th annual Debian Conference, DebConf15, will be held in Germany next year.

Thanks to the Belgian and Swedish teams; we are looking forward to their renewed bids for future DebConfs!

Specifics as to location and date are still being nailed down and we will keep Debian as a whole informed about our progress.

A dedicated (English-language) mailing list has been created for the organization and we welcome interested people to subscribe and join the discussion.

by Richard &#x27;RichiH&#x27; Hartmann at March 08, 2014 05:26 PM

March 06, 2014

RichiH's blog

DC15.de

Here's to a happy, successful, and overall quite awesome DebConf15 in Germany.

Details to follow :)

by Richard &#x27;RichiH&#x27; Hartmann at March 06, 2014 10:04 PM

March 03, 2014

RichiH's blog

Facial recognition

Dear crazyweb,

while there seem to be good GUI tools to enable facial recognition with FLOSS, they all fall short of my requirements. And while there seem to be a lot of research projects with open code, they seem to be lacking in the "usable in real life" department.

It seems as if there should be something to scratch that itch, but I couldn't find it...

Thus, my wishlist for facial recognition software

  • MUST NOT send any data to any third parties!
  • Must run on Linux
  • GUI and CLI are both fine as long as the rest of the specs are met, but good CLI-integration would be a huge plus
  • Should offer batch-verification of detected faces like so
  • Must not rely on duplicating files in its own data structure/DB/directory; symlinks are fine
  • Should cope with source files disappearing
  • Should be able to list/diff files which are new or not yet tagged
  • Must not require being able to write to any picture files
  • Must be able to store data outside of the original pictures
  • Should not modify the source directories without being told to; temp files, face DB, and similar should all be located in a place I decide upon
  • Should offer batch-processing
  • Must be able to trigger a command or script for all verified identifications i.e. the ones I manually set to matching the person; alternatively, at least be able to export data in a way I can build scripts upon
  • Should be able to cope with faces changing over time, people growing older, getting a beard, etc
  • Should be FLOSS if at all possible
  • MUST NOT send any data to any third parties!
  • I consider tags to be permanent, the DB for the program should ideally be ephemeral but I am aware that this may not be possible
  • If the DB needs to be retained, it should ideally be in a merge-friendly text format not binary but that may be asking too much ;)
  • MUST NOT send any data to any third parties!
  • Ponies.

I will gladly follow up with a workflow blog post assuming I end up with useful feedback.

by Richard &#x27;RichiH&#x27; Hartmann at March 03, 2014 08:20 PM

February 25, 2014

erry's blog

Displaying an HTML slider’s value

HTML5 sliders, or <input type=”range”> are a cool new input type, but he problem is, the user has no indication of which value they have selected. Luckily, this is easy to fix

Firstly you can create your slider and give it a class name, and a unique id. For example:

<input type="range" class="slider" id="slider1" /><br/>
<input type="range" class="slider" value="20" id="slider2" />

Then we will see where its value will go. You can create a span whose id is the same as the slider + ‘_value’ for example. This will help us be able to access the elements with javascript later:

<input type="range" class="slider" id="slider1" />
<span id="slider1_value"></span><br/>
<input type="range" class="slider" value="20" id="slider2" />
<span id="slider2_value"></span>

Now all we have to do is loop through our sliders, and update the innerHTML of the value span as they change:

<script>
    var sliders = document.getElementsByClassName('slider');
    // class='slider' elements :p
    var len     = sliders.length;

    for ( var i = 0; i < len; i++ ) {
        var slider = sliders[i];

        slider.addEventListener('change', function() {
            updateValue(this);
        });

        updateValue(slider);
    }

    function updateValue(slider) {
        var id        = slider.id;

        if (!id) {
            return;
        }

        var val       = document.getElementById(id + '_value');
        // Find the span whose id is the id of the slider + _value..
        
        if (val) {
            val.innerHTML = slider.value; // And update it!
        }
    }
</script>

Now your sliders should have values next to them (or wherever you place your _value element) that auto-update as you change them =)

Example!


<script>function updateValue(e){var t=e.id;if(!t){return}var n=document.getElementById(t+"_value");if(n){n.innerHTML=e.value}}var sliders=document.getElementsByClassName("slider");var len=sliders.length;for(var i=0;i<len></script>
That's all for now! See you next time =)

by Errietta Kostala at February 25, 2014 09:35 PM

CodeIgniter core classes

One of the things I liked about codeigniter is it lets you create and extend core classes. For example, say I want to extend the functionality of the controller class so that it does not allow me to access certain pages unless I am logged in:

Simply create application/core/MY_Controller.php to extend the core controller class. Then you can do this:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

   class MY_Controller extends CI_Controller {
    protected $user;

    public function __construct() {
        parent::__construct();

        $this->load->helper('url');
        $this->load->library('session');

        $this->user     = $this->session->userdata('user');
        # or your own way to check if the user is logged in
        $page           = uri_string();

        if (
            !$this->user
            && strpos ( $page, 'login' ) === false
            && strpos ( $page, 'register' ) === false
            && $page != ''
        ) { # this allows you to access pages matching 'login', 'register',
            # and the index page.
            # you can configure this of course.
            $this->session->set_flashdata(
                'error',
                'You must log in to see this page.'
            );

            redirect ('/login');
        }
    }
}
?>

Now when you write your own controller you would extend MY_Controller instead of CI_Controller, for example:

<?php
class Profile extends MY_Controller {

And all your pages will have this functionality. As an added bonus, every controller that extends MY_Controller will have the user session in $this->user.

Another cool thing you can do by extending core controllers is writing functions you want every controller to have available:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

   class MY_Controller extends CI_Controller {
    protected $user;

    public function __construct() {
        parent::__construct();

        $this->load->helper('url');
        $this->load->library('session');

        $this->user     = $this->session->userdata('user');
        # or your own way to check if the user is logged in
        $page           = uri_string();

        if (
            !$this->user
            && strpos ( $page, 'login' ) === false
            && strpos ( $page, 'register' ) === false
            && $page != ''
        ) { # this allows you to access pages matching 'login', 'register',
            # and the index page.
            # you can configure this of course.
            $this->session->set_flashdata(
                'error',
                'You must log in to see this page.'
            );

            redirect ('/login');
        }
    }

    protected function stash_input() {
        foreach ($this->input->post() as $key => $value) {
            $this->session->set_flashdata($key, $value);
        }
    }
}
?>

stash_input here, for example, will store all of $_POST in your flash data.
this is useful for postback forms.
For example, if an error happens when submitting the form, you can store the input in the session data and then use it in your view:

//...
if ($error) {
$this->stash_input();
$this->session->set_flashdata("error", $error);
redirect(site_url('/your_form'));
return;
}
//...

Overall, extending core classes can be very helpful and is one of my favourite features of codeigniter!

That’s all for now. Until next time, see ya!

by Errietta Kostala at February 25, 2014 10:32 AM

February 20, 2014

Md's blog

Automatically unlocking xscreensaver in some locations

When I am at home I do not want to be bothered by the screensaver locking my laptop. To solve this, I use a custom PAM configuration which checks if I am authenticated to the local access point.

Add this to the top of /etc/pam.d/xscreensaver:

auth sufficient pam_exec.so quiet /usr/local/sbin/pam_auth_xscreensaver

And then use a script like this one to decide when you want the display to be automatically unlocked:

#!/bin/sh -e

# return the ESSID of this interface
current_essid() {
  /sbin/iwconfig $1 | sed -nre '/ESSID/s/.*ESSID:"([^"]+)".*/\1/p'
}

# automatically unlock only for these users
case "$PAM_USER" in
  "")   echo "This program must be run by pam_auth.so!"
        exit 1
        ;;
  md)   ;;

  *)    exit 1
        ;;
esac

CURRENT_ESSID=$(current_essid wlan0)

# automatically unlock when connected to these networks
case "$CURRENT_ESSID" in
  MYOWNESSID) exit 0 ;;
esac

exit 6

February 20, 2014 05:49 AM

February 15, 2014

RichiH's blog

On init system debates

Qouth Rogério Brito:

Russ, thank you for your exemplary role.

And that's all that was left to say.

by Richard &#x27;RichiH&#x27; Hartmann at February 15, 2014 09:02 AM

February 04, 2014

freenode staffblog

Turbulence

As many of you will be aware, freenode has been experiencing intermittent instability today, as the network has been under attack. Whilst we have network services back online, the network continues to be a little unreliable and users are continuing to report issues in connecting to the network.

We appreciate the patience of our many wonderful users whilst we continue to work to mitigate the effects this has on the network.

We also greatly appreciate our many sponsors who work with us to help minimise the impact and who are themselves affected by attacks against the network.

We’ve posted on this subject before, and what we said then remains as true as ever – and for those of you who didn’t read the earlier blogpost first time round, it’s definitely worth perusing it now if this subject interests or affects you.

Thank you all for your patience as we continue to work to restore normal service!

[UPDATE 04/02/2014]

At the moment SASL authentication works only on PLAINTEXT, *not* BLOWFISH. We’ve checked and TOR should be working too. Sadly wolfe.freenode.net will be taken off the rotation, so those users who’ve connected specifically to it, please make sure that your client points to our recommended roundrobin of chat.freenode.net!

by njan at February 04, 2014 04:53 PM

January 30, 2014

Md's blog

On people totally opposed to systemd

Do you remember the very vocal people who, a decade ago, would endlessly argue that udev was broken and that they would never use it?

Percentage over time of systems on which udev is installed

Sometimes you can either embrace change or be dragged along by it. We are beyond the inflection point, and the systemd haters should choose their place.

January 30, 2014 05:15 AM

January 19, 2014

erry's blog

Things to do while Microsoft Visual Studio installs

Microsoft visual studio (We’re forced to use it for uni, ugh) seems to take forever and a day to install. Here’s a few fun things you can do while it does that:
(Note that this is a joke, don’t take it /too/ seriously ;))

  • Watch the lord of the rings trilogy and all the harry potter movies 10 times
  • Play through every game in the Zelda series, aim for 100% completion
  • Install Linux in 1000 machines
  • Learn a new language
  • Watch paint dry
  • Travel around the world
  • Become an expert in martial arts
  • Count to infinity
  • Solve the P VS NP Problem
  • Go to university and graduate
  • Write this blog post :p

    by Errietta Kostala at January 19, 2014 09:49 PM

    January 13, 2014

    erry's blog

    A look at codeigniter

    We’re using Codeigniter at university in our second term, so naturally I had to look at it during the holiday break before it started. Codeigniter is a PHP MVC framework. I prefer it to writing PHP from scratch, but my personal opinion is that I still like Catalyst more. Yet again, Catalyst is Perl based, while Codeigniter is PHP.

    Anyway, I’m going to look into making a simple registration and login system from scratch. The first step is, of course, to download codeigniter. You can get it from its website. You unzip the file somewhere where your webserver will service it, and check it out on your browser. Congratulations, you have the most basic setup ready!

    Looking at the contents of your new folder, application/ has all the things you should care about. Specifically, config/ has all the configuration-related stuff, controllers/ is where the controllers go, models/ is where the models go, and views/ is where the views go.

    The best place to start is the config. Having a look at the application/config folder we first have config.php which has some global settings. There are a few things you may want to change there, but nothing that will prevent the application from working. If accepting user input, however, I like to enable CSRF protection and XSS protection. In that file you can find

    $config['csrf_protection'] = FALSE;
    $config['csrf_token_name'] = 'csrf_test_name';
    $config['csrf_cookie_name'] = 'csrf_cookie_name';
    $config['csrf_expire'] = 7200;
    

    You can change that to something like:

    $config['csrf_protection'] = true; //You need to set this to true,
    $config['csrf_token_name'] = 'token'; //but these settings can be
    $config['csrf_cookie_name'] = 'token'; //whatever you want.
    $config['csrf_expire'] = 7200;
    

    Then, codeigniter will take care of CSRF protection for you! There’s also this:

    $config['global_xss_filtering'] = FALSE;
    

    If you change that to true, you will get global XSS protection. There are other settings in the file you may want to change, so look around.

    Next important file is application/config/database.php. If you’re doing anything database related, then you need to edit that. It’s pretty self explanatory, but

    $db['default']['hostname'] = 'localhost';
    $db['default']['username'] = '';
    $db['default']['password'] = '';
    $db['default']['database'] = ''; //You may want to create a database for your app
                                     //And put its name here.
    $db['default']['dbdriver'] = 'mysql';
    

    Is what you absolutely need to change. For the driver, I suggest mysqli and not the default value of mysql, since it has been deprecated for ages.

    There’s also application/config/routing.php which determines how routing works in your app. We’ll look at that in a bit though.

    Now that the config part is done with for now, we can take a first look at controllers. If you look at application/controllers/welcome.php You have a very basic controller that basically does this:

    public function index()
    {
          $this->load->view('welcome_message');
    }
    

    index() is called when a user goes to http://your_website/index.php/welcome/, but because the routing config (see above) chooses ‘welcome’ as the default controller, http://your_website/ will load the same page.
    In fact, you could do this:

    public function hello()
    {
          $this->load->view('welcome_message');
    }
    

    And then http://your_website/index.php/welcome/hello will display the same message.
    You might guess where that page comes from: application/views/welcome_message.php – change that page and the page displayed when you load the welcome page will be loaded.

    Of course the whole point is to make your own views, controllers and models. Since we want to make a registration system, it’s probably good to start from the database. We’ll make a users table:

    CREATE TABLE IF NOT EXISTS `users` (
      `user_name` varchar(255) NOT NULL,
      `user_id` bigint(255) unsigned NOT NULL AUTO_INCREMENT,
      `password` varchar(255) NOT NULL,
      `email` varchar(255) NOT NULL,
      PRIMARY KEY (`user_id`),
      UNIQUE KEY `unique_user` (`user_name`,`email`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=latin1;
    

    We can then make a model to reflect this database table. You can create all your models in application/models, so this would be application/models/user_model.php or similar

    <?php
    
        if ( !defined('BASEPATH') ) {
            exit('No direct script access allowed');
        }
    
        class user_model extends CI_Model {
            public function __construct() {
                $this->load->database(); //load the database class
            }
    
            public function login() {
                $username = $this->input->post('username');
                $password = $this->input->post('password');
    
                $query    = $this->db->get_where (
                    'users',
                    array (
                        'user_name' => $username
                    )
                );
    
                $user     = $query->row();
    
                if ( !$user ) {
                    return -1; //no such username
                }
    
                if ( $password != $user->password ) {
                    return 0; //wrong password
                }
    
                $_SESSION['user'] = $user->user_id;
                return 1; //correct
            }
    
            public function register() {
                $password   = $this->input->post('password');
    
                $data = array (
                    'user_name'    => $this->input->post('username'),
                    'password'     => $password,
                    'email'        => $this->input->post('email')
                );
    
                $this->db->insert ('users', $data);
    
                $_SESSION['user'] = $this->db->insert_id();
            }
        }
    ?>
    

    This is pretty self explanatory. It loads the database class, and defines a login function that checks if the database data matches the input data and a register one that inserts to the database. get_where allows you to get results for a where query, and insert inserts an array of keys and values to the specific database fields. You may see that we don’t check the input here. This is ok, we’ll do this in our controller.

    Now that we have our model we need a couple views. application/views/register.php:

    <?php
        $this->load->helper('form');
        echo form_open('register/submit');
    ?>
            <fieldset>
                <label>
                    <legend>Register</legend>
                    <label>
                        User name:<br/>
                        <input type="text" name="username" value="" />
                    </label><br/>
                    <label>
                        Email:<br/>
                        <input type="email" name="email" value="" />
                    </label><br/>
                    <label>
                        Password:<br/>
                        <input type="password" name="password" value="" />
                    </label><br/>
                    <input type="submit" name="" value="register" />
                </label>
            </fieldset>
        </form>
    

    You may notice that instead of a >form< tag we use form_open. The reason for this is that this method also takes care of inserting the counter-CSRF fields if we enabled this in the config.

    Similarly, application/views/login.php:

    <?php
        $this->load->helper('form'); //this form helper gives us form_open
        echo form_open('login/submit');
    ?>
            <fieldset>
                <label>
                    <legend>Login</legend>
                    <label>
                        User name:<br/>
                        <input type="text" name="username" value="" />
                    </label><br/>
                    <label>
                        Password:<br/>
                        <input type="password" name="password" value="" />
                    </label><br/>
                    <input type="submit" name="" value="login" />
                </label>
            </fieldset>
        </form>
    

    And now we can actually make our controllers, which will be simple since we have our user model ready.
    This is our register controller – application/controllers/register.php

    <?php
        session_start();
    
        class register extends CI_Controller {
            public function __construct() {
                parent::__construct();
                $this->load->model('user_model'); //load our user model
                //When you load it, you can use all its methods through
                //$this->user_model
                $this->load->helper('form'); //and the form helper
            }
    
            public function index() {
                $this->load->view('register'); //just display the form
            }
    
            public function submit() {
                $this->load->library('form_validation');//load the form validation lib
    
                $this->form_validation->set_rules(
                'username',
                'Username',
                'required|max_length[255]|is_unique[users.user_name]'
                ); //this will ensure the username isn't already in the database,
                   //that it's been provided,
                   //and that it's no more than 255 chars long
                $this->form_validation->set_rules('password',
                'Password', 'required|max_length[55]');
                $this->form_validation->set_rules('email', 'Email',
                'required|valid_email|is_unique[users.email]');
                 //Similarly, this will ensure
                 //The email is unique and valid.
    
                if ( $this->form_validation->run() === false ) {
                    $this->index(); //if the form validation fails show the index
                } else {
                    $this->user_model->register();
                    //this will create the db record from input
                    //do something else
                    //If you want to redirect a user to a page after this:
                    $this->load->helper('url');
                    redirect('/user/show_info');
                }
            }
        }
    ?>
    

    We can see how codeigniter made our lives easier here. Instead of writing the same boring validation methods again and again we get them provided to us, plus the database helper doesn’t hurt either.

    the login controller will be like this:

    <?php
        session_start();
    
        class login extends CI_Controller {
    
            public function __construct() {
                parent::__construct();
                $this->load->model('user_model');
                $this->load->helper('form');
    
            }
    
            public function index($error="") {
                $this->load->view('login', array ('error' => $error));
            }
    
            public function submit() {
                $this->load->library('form_validation');
    
                $this->form_validation->set_rules('username', 'Username', 'required');
                $this->form_validation->set_rules('password', 'Password', 'required');
    
                if ( $this->form_validation->run() === false ) {
                    $error = 'Please provide a username and password';
                    $this->index($error);
                } else {
                    $result = $this->user_model->login();
    
                    if ($result == -1) {
                        $error = "No such username";
                        $this->index($error);
                    } elseif ($result == 0) {
                        $error = 'Wrong password!';
                        $this->index($error);
                    } else {
                        //Success!
                        //Again, you can now...
                        $this->load->helper('url');
                        redirect('/user/show_info');
                    }
                }
            }
        }
    ?>
    

    You might have noticed two things in the above. 1: Where do validation errors go? 2: What does

       $this->load->view('login', array ('error' => $error));
    

    do?

    As for the first question, the answer is validation_errors(). It’s a method of our form helper and you can use it in your view:

    <?php
            if (validation_errors()) {
    ?>
                <div class="error">
                    <?php
                        echo validation_errors();
                    ?>
                </div>
    <?php
            }
    ?>
    

    And for the second question, this just passes a variable into the view. This means we can do:

    <?php
    if (isset($error)) {
    ?>
        <div class="error">
            <?php
                echo $error;
            ?>
        </div>
    <?php
    }
    ?>
    

    You can use this to pass any required data to your model, such as database query results, etc.

    Congratulations, you should now have a working login system! You can of course see this in action at http://your_app/index.php/register/ and
    http://your_app/index.php/login respectively.

    If you want to be able to view a user’s information after that, you can add a function to get a user object from their id to your model:

    <?php
    
        if ( !defined('BASEPATH') ) {
            exit('No direct script access allowed');
        }
    
        class user_model extends CI_Model {
            public function __construct() {
                $this->load->database(); //load the database class
            }
    
            public function by_id($user_id) {
                //does a SELECT query WHERE user_id = $user_id
                $query    = $this->db->get_where (
                    'users',
                    array (
                        'user_id' => $user_id
                    )
                );
    
                //returns an object represening the resulting user row in the database
                return $query->row();
            }        
    
            public function login() {
                $username = $this->input->post('username');
                $password = $this->input->post('password');
    
                $query    = $this->db->get_where (
                    'users',
                    array (
                        'user_name' => $username
                    )
                );
    
                $user     = $query->row();
    
                if ( !$user ) {
                    return -1; //no such username
                }
    
                if ( $password != $user->password ) {
                    return 0; //wrong password
                }
    
                $_SESSION['user'] = $user->user_id;
                return 1; //correct
            }
    
            public function register() {
                $password   = $this->input->post('password');
    
                $data = array (
                    'user_name'    => $this->input->post('username'),
                    'password'     => $password,
                    'email'        => $this->input->post('email')
                );
    
                $this->db->insert ('users', $data);
    
                $_SESSION['user'] = $this->db->insert_id();
            }
        }
    ?>
    

    And then you can use this from a controller:

    <?php
        //application/controllers/user.php
        session_start();
    
        class user extends CI_Controller {
            public function __construct() {
                parent::__construct();
                $this->load->model('user_model');
            }
    
            public function show_info($user_id=false) {
                if ($user_id == false) { //default to our logged in user
                    $user_id = $_SESSION['user'];
                }
               
                $user = $this->user_model->by_id($user_id);
                //this will make $user available to your view
                $this->load->view('info', array('user' => $user));
            }
        }
    ?>
    

    the view could be just:

    <?php
    //application/views/info.php
    echo "Hello " . $user->user_name . ".";
    ?>
    

    or anything else that uses the $user object! It should have all the values from your database row available.

    now link_to_your_site/user/show_info/{user_id} will display the user’s name.

    The next thing to do from here is to look at The manual to look at how helpers work and other awesome things you can do.

    Until next time,

    by Errietta Kostala at January 13, 2014 05:01 PM

    December 31, 2013

    erry's blog

    The struggle with year 2 project!

    Hi,

    This is a bit of a more personal post (in that it talks about how I approached a project rather than being a tutorial) but I thought it’d be interesting to share anyway.

    Our year 2 project in University is actually really open — we can work on pretty much whatever we want. I wanted to work with something new and exciting, so I chose to do a project built around WebRTC. People would be able to make accounts and build teams then arrange and hold audio and video meetings that worked using WebRTC. There would also be a reminder before joining a meeting etc.

    I had no experience with WebRTC, so I knew this would be challenging — and I wasn’t wrong. The first problem was the actual signalling to get the WebRTC conversation to start. You can use about anything for that, but the best way is socket.io because it supports multiplexing, and you can have signalling with more than two clients. Problem is, I hadn’t realised this at first. Some form of signalling worked even with AJAX, and I switched to websockets soon afterwards, and it worked as long as I had two clients. I couldn’t get it to work with more than two though, and after spending countless hours with this article I finally got a three-way chat to work using a socket.io server (and changing my code to match the one in that tutorial).

    After that it got a bit quieter, tidying the GUI app and adding some of the more simple functionality such as log in, register, reminder emails etc. But it’s about to get busy again as I’ll attempt to do file transfer and desktop sharing too, to offer a fully-featured meeting suite ^_^
    Of course I played it safe by not including these parts in the developed Minimum Viable Product, so if I don’t succeed I won’t fail the assignment :p

    It’s overall a great project and I’m learning a lot for it, although it can get challenging with the WebRTC api and all!

    And I think that’s it from me in 2013 ;) see you next year!

    PS: If you want to know more about WebRTC, I’d read this, this, and this article, watch the video and check out the code.

    by Errietta Kostala at December 31, 2013 08:00 PM

    December 01, 2013

    Md's blog

    Easily installing Debian on a Cubieboard

    I recently bought a Cubieboard to replace my old Sheevaplug which has finally blown a power supply capacitor (this appears to be a common defect of Sheevaplugs), so I am publishing these instructions which show how to install Debian on sunxi systems (i.e. based on the Allwinner A10 SoC or one of its newer versions) with no need for cross compilers, emulators or ugly FAT partitions.

    This should work on any sunxi system as long as U-Boot is at least version 2012.10.

    The first step is to erase the beginning of SD card to remove anything in the unpartitioned space which may confuse U-Boot, partition and format it as desired. The first partition must begin at 1MB (1024*1024/512=2048 sectors) because the leading unpartitioned space is used by the boot loaders.

    dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=1
    parted /dev/mmcblk0
    
      mklabel msdos
      mkpart primary ext4 2048s 15G
      unit s
      print
      mkpart primary linux-swap ... -1
    
    mkfs.ext4 -L root /dev/mmcblk0p1
    mkswap --label swap /dev/mmcblk0p2
    

    Download the boot loaders and an initial kernel and install them:

    tar xf cubieboard_hwpack.tar.xz
    dd if=bootloader/sunxi-spl.bin of=/dev/mmcblk0 bs=1024 seek=8
    dd if=bootloader/u-boot.bin of=/dev/mmcblk0 bs=1024 seek=32
    
    mount /dev/mmcblk0p1 /mnt
    mkdir /mnt/boot/
    
    cp kernel/script.bin kernel/uImage /mnt/boot/
    

    script.bin is Allwinner's proprietary equivalent of the device tree: it will be needed until sunxi support will be fully merged in mainline kernels.

    U-Boot needs to be configured to load the kernel from the ext4 file system (join the lines at \\, this is not a supported syntax!):

    cat << END > /mnt/boot/uEnv.txt
    # kernel=uImage
    root=/dev/mmcblk0p1 rootwait
    boot_mmc=ext4load mmc 0:1 0x43000000 boot/script.bin && ext4load mmc 0:1 0x48000000 boot/${kernel} \\
      && watchdog 0 && bootm 0x48000000
    END
    

    Now the system is bootable: add your own root file system or build one with debootstrap. My old Sheevaplug tutorial shows how to do this without a working ARM system or emulator (beware: the other parts are quite obsolete and should not be trusted blindly).

    If you have an old armel install around it will work as well, and you can easily cross-grade it to armhf as long as it is up to date to at least wheezy (the newer, the better).

    You can also just use busybox for a quick test:

    mkdir /mnt/bin/
    dpkg-deb -x .../busybox-static_1.21.0-1_armhf.deb .
    cp bin/busybox /mnt/bin/
    ln -s busybox /mnt/bin/sh
    

    After booting the busybox root file system you can run busybox --install /bin/ to install links for all the supported commands.

    Until Debian kernels will support sunxi (do not hold your breath: there are still many parts which are not yet in mainline) I recommend to install one of Roman's kernels:

    dpkg -i linux-image-3.4.67-r0-s-rm2+_3.4.67-r0-s-rm2+-10.00.Custom_armhf.deb
    mkimage -A arm -O linux -T kernel -C none -a 40008000 -e 40008000 \
      -n uImage -d /boot/vmlinuz-3.4.67-r0-s-rm2+ /boot/uImage-3.4.67-r0-s-rm2+
    

    It is not needed with these kernels for most setups, but an initramfs can be created with:

    update-initramfs -c -k 3.4.67-r0-s-rm2+
    mkimage -A arm -T ramdisk -C none -n uInitrd \
      -d /boot/initrd.img-3.4.67-r0-s-rm2+ /boot/uInitrd-3.4.67-r0-s-rm2+
    

    /boot/uEnv.txt will have to be updated to load the initramfs.

    Since the Cubieboard lacks a factory-burned MAC address you should either configure one in script.bin or (much easier) add it to /etc/network/interfaces:

    iface eth0 inet dhcp
            hwaddress ether xx:xx:xx:xx:xx:xx
    

    To learn more about the Allwinner SoCs boot process you can consult 1 and 2.

    December 01, 2013 12:40 PM

    November 08, 2013

    mrmist's blog

    Broadband Rant


    This article is tagged with:

    It’s hard to see how targets of 90% + coverage are going to be met in this country, when we can’t get fibre broadband even on our new housing estate in a redeveloping area.

    The next street along has an upgraded cabinet, and one further down has an upgraded cabinet, but our cabinet remains outdated. I am told that our cabinet does not meet the “financial criteria” due to having too few houses connected. To me, it seems that hardly any similar cabinets would ever make the criteria – in other words, unless you happen to be extremely lucky, or you’re living in a city centre, you can forget it, regardless of promises to cover 90% of the UK.

    I guess what that 90% figure means is that 90% of exchanges will be capable of providing fibre broadband – even if only 1/3 of the connected cabinets can.

    It seems to me that having financial criteria from a single provider makes the aims of the project incompatible with the implementation.

    by Mrmist at November 08, 2013 08:20 AM

    November 02, 2013

    Md's blog

    New PGP key

    Since my current PGP key is a 1024 bits DSA key generated in 1998, I decided that it is time to replace it with a stronger one: there are legitimate concerns that breaking 1024 bits DSA is well within the reach of major governments.

    I have been holding out for the last year waiting for GnuPG 2.1, which will support elliptic curves cryptography, but I recently concluded that adopting ECC now would not be a good idea: Red Hat still does not fully support it due to unspecified patent concerns and there is no consensus in the cryptanalists community about the continued strength of (some?) ECC algorithms.

    So I created three fancy keys: a 4096 bits main key for offline storage, which hopefully will be strong enough for a long time, and two 3072 bits subkeys for everyday use.

    I have published a formal key transition statement and I will appreciate if people who have signed my old key will also sign the new one.

    What follows are the instructions that I used to generate these PGP keys. They follow the current best practices and only reference modern software.

    While the GnuPG defaults are usually appropriate, I think that it is a good idea to use a stronger hash for the key signatures of very long-lived keys. I could not find a simple way to "upgrade" the algorithm of key self signatures.

    echo 'cert-digest-algo SHA256' >> ~/.gnupg/gpg.conf

    First, generate a RSA/4096 sign only key, which will be your master key and may be stored offline. Then add to it two RSA/3072 subkeys (one sign only and one encrypt only):

    # generate a RSA/4096 sign only key
    gpg2 --gen-key
    # add two RSA/3072 subkeys (sign only and encrypt only)
    gpg2 --edit-key 8DC968B0

    Since GnuPG lacks a command to remove the master secret key while keeping its secret subkeys, you need to delete the complete secret keys and then re-import only the subkeys:

    gpg2 --export-secret-keys 8DC968B0 > backup.secret
    gpg2 --export-secret-subkeys 8DC968B0 > backup.subkeys
    gpg2 --delete-secret-key 8DC968B0
    gpg2 --import backup.subkeys

    Then you can import again the complete keys in a different secret keyring, which can be stored offline:

    mkdir ~/.gnupg/master/
    gpg2 --no-default-keyring \
      --keyring ~/.gnupg/pubring.gpg \
      --secret-keyring ~/.gnupg/master/secring.gpg \
      --import backup.secret

    Now you can move ~/.gnupg/master/ to a USB stick. You are supposed to protect the master secret key with a strong passphrase, so there is no point in using block level encryption on the removable media.

    Since you are only using the master key to sign other keys, it only needs to be configured as the second keyring in ~/.caffrc:

    $CONFIG{'secret-keyring'} = $ENV{HOME} . '/.gnupg/master/secring.gpg';

    It is also a good idea to have an hard copy backup of your keys, since the lifetime of USB sticks should not be trusted too much:

    paperkey -v --output printable.txt --secret-key backup.secret
    a2ps -2 --no-header -o printable.ps printable.txt

    Some references that I used:

    November 02, 2013 11:03 PM

    erry's blog

    Object-Oriented Programming (OOP) in PHP

    Hello and welcome to a (late) Halloween post! We’re going to discuss PHP OOP by making classes for Ghosts, Zombies, and Vampires.
    But before that, a little introduction. If you’ve read my javascript OOP post you may have an idea of what to expect. If not, don’t worry, you don’t need to read it. In a nutshell, objects are structures can hold other variables (properties) or functions (methods), and interact with other objects.

    The first step towards OOP in PHP is to make a class. A class defines the methods and properties of objects. Classes can also inherit methods from other classes, but we’ll look at that in a bit. You can think of a class as a template or design for every object of that class.

    Imagine a ghost. It has a colour (white, red, blue), and maybe a name. Additionally, it can attack humans by scaring them. You could make a class similar to this:

    <?php
    class Ghost { //use class to define classes!
        private $colour;
        private $name;
    
        public function attack ($human_name) {
            print $this->name . ' attacked ' . $human_name . ' by scaring them!';
        }
    }
    ?>
    

    Let’s explain a few things.

        private $colour;
    

    To define object properties and methods, you can use the private, public, or protected keywords. ‘private’ will prevent any code outside that class* from accessing the property, ‘protected’ will allow classes that extend the class (children) to access the property as well, and ‘public’ will allow any piece of code to access the property

    * Parents can always access children’s properties.

    public function attack ($human_name) {
        print $this->name . ' attacked ' . $human_name . ' by scaring them!';
    }
    

    $this works only inside object methods, and always refers to the object you’re working with.

    Now we can create our objects.

    <?php
    $ghost = new Ghost();
    ?>
    

    Of course that’s not very useful as it is. We can’t change the name or colour here, because it’s private. We may not want to make our properties public and accessible to the whole world, either. So we could make ‘getters’ and ‘setters’.
    Basically, these are public methods that allow us to indirectly access private properties. One advantage of them is that we could then deny certain values if necessary, or call other required code.

    <?php
    class Ghost { //use class to define classes!
        private $colour;
        private $name;
    
        public function attack ($human_name) {
            print $this->name . ' attacked ' . $human_name . ' by scaring them!';
        }
    
        public function get_colour () {
            return $this->colour;
        }
    
        public function get_name () {
            return $this->name;
        }
    
        public function set_colour ($new_colour) {
            $this->colour = $new_colour;
        }
    
        public function set_name ($new_name) {
            $this->name = $new_name;
        }
    }
    ?>
    

    Now we can do this:

    <?php
    $ghost = new Ghost();
    $ghost->set_name('erry');
    $ghost->set_colour('pink');
    $ghost->attack('reader');
    ?>
    

    All these properties and methods will be shared by every single ghost object. Convenient and tidy, right?

    You can take this a bit further with a bit of magic. Every time you create an object with new Ghost(); a constructor function is called. You can define this constructor to do something you want it to:

    <?php
    class Ghost { //use class to define classes!
        private $colour;
        private $name;
    
        public function __construct ($name, $colour) { //Constructor!
            $this->name = $name;
            $this->colour = $colour;
        }
    
        public function attack ($human_name) {
            print $this->name . ' attacked ' . $human_name . ' by scaring them!';
        }
    
        public function get_colour () {
            return $this->colour;
        }
    
        public function get_name () {
            return $this->name;
        }
    
        public function set_colour ($new_colour) {
            $this->colour = $new_colour;
        }
    
        public function set_name ($new_name) {
            $this->name = $new_name;
        }
    }
    ?>
    

    You can then use it like this:

    <?php
    $ghost1 = new Ghost('erry','pink');
    $ghost1->attack('reader');
    $ghost2 = new Ghost('reader','blue');
    $ghost2->attack('erry');
    ?>
    

    We talked about extending classes earlier. Now we can demonstrate this. Imagine you have similar objects that aren’t quite the same: Ghosts, Zombies, and Vampires. They’re all monsters, and they all have colours and names, but they attack people in a different way. If you wanted to write classes for all of them, you’d be copy-pasting code a lot. Instead, you could write a Monster class with the common methods and properties, and extend it for methods and properties that change.

    <?php
    class Monster { //use class to define classes!
        protected $colour;
        protected $name;
    
        public function __construct ($name, $colour) { //Constructor!
            $this->name = $name;
            $this->colour = $colour;
        }
    
        public function get_colour () {
            return $this->colour;
        }
    
        public function get_name () {
            return $this->name;
        }
    
        public function set_colour ($new_colour) {
            $this->colour = $new_colour;
        }
    
        public function set_name ($new_name) {
            $this->name = $new_name;
        }
    }
    
    class Ghost extends Monster { //Child classes 'extend' the parent.
        public function attack ($human_name) {
            print $this->name . ' attacks ' . $human_name . ' by scaring them!';
        }
    }
    
    class Zombie extends Monster {
        public function attack ($human_name) {
            print $this->name . ' attacks ' . $human_name . ' by eating their brains!';
        }
    }
    
    class Vampire extends Monster {
        public function attack ($human_name) {
            print $this->name . ' attacks ' . $human_name . ' by drinking their blood!';
        }
    }
    ?>
    

    As you can see, we just changed the Ghost class to Monster, and removed attack. We then made three child classes that extend it, and just implement the attack method.
    Note that we also made the two private properties protected, so that the children inherit them. They won’t be inherited when extending if they’re private.

    <?php
    $ghost = new Ghost('erry','pink');
    $ghost->attack('reader');
    $zombie = new Zombie('erry','blue');
    $zombie->attack('reader');
    $vampire = new Vampire('erry','white');
    $vampire->attack('reader');
    ?>
    

    Will then work as expected :D

    You could also declare the attack method in the Monster class without defining it. By making it abstract, you could force all children of the class to implement it

    abstract class Monster {
    //...
        abstract public function attack ($human_name);
    }
    

    Note how that function is declared without being implemented. Classes that extend Monster are now forced to implement the attack method. abstract also prevents you from directly creating Monster objects, forcing you to use children classes, which is also good for our design here.

    Well, I think that’s all for now… Boo!

    by Errietta Kostala at November 02, 2013 09:30 PM

    October 27, 2013

    erry's blog

    Protected: On Gaining Confidence

    This content is password protected. To view it please enter your password below:

    by Errietta Kostala at October 27, 2013 02:40 PM

    September 30, 2013

    erry's blog

    Experimenting with jquery mobile

    I recently checked out jquery mobile, and I was quite impressed. It’s there to help you make a mobile user experience, but what impressed me more was a few things it does in the background, and how easy it is to use.

    To begin with, if you just load the jquery mobile CSS and script:

    <link rel="stylesheet" 
    href="//code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
    <script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
    <script src="//code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
    

    You’re already getting some of its functionality on your website, without doing anything else. For example, if you use links in your website, you will now notice that they will all automatically use AJAX and give the user a sleek loading animation.
    Also, some of your elements will be styled, naturally. For example, checkboxes will automagically become mobile-friendly:

    <label>
        <input type="checkbox" /> I'm a checkbox!
     </label>
    

    As mentioned earlier, the jquery mobile syntax was surprisingly easy to use. Let’s look at some examples:

    Dialogs

    You can designate any other page to open as a dialog, by giving your link a data-rel=”dialog” attribute. Personally, I think having it in its own page is cleaner, anyway.

    <a href="/page" data-rel="dialog">Open dialog</a>
    

    What isn’t immediately obvious, though, is that your target page needs some specific HTML markup to work, too. It should look like this:

    <div data-role="dialog">
        <div data-role="header" data-theme="d">
            <h1>Dialog Header</h1>
        </div>
    
        <div data-role="content">
            Dialog content.
            <a href="dialog/index.html" data-role="button" data-rel="back" data-theme="b">
            Ok
            </a>
            <a href="dialog/index.html" data-role="button" data-rel="back" data-theme="c">
            Cancel
            </a>
        </div>
    </div>
    

    So, your container has to be data-role=”dialog”, then data-role=”header” and data-role=”content” designate the dialog header and content. data-rel=”back” on a link will make it a close button.
    The data-theme=”” attribute is optional, but it can be used to style various components. The themeroller lets you create several “swatches” for your theme, with different colors, but the defaults are mentioned in the documentation.

    Collapsibles/Accordions

    The way to make a collapsible is straight forward:

    <div data-role="collapsible">
        <h1>Click on me!</h1>
        <p>
            Collapsible content.
        </p>
    </div>
    

    The only special markup there is data-role=”collapsible”. The heading (which can be any heading from h1 to h6) automatically becomes the collapsible toggle.
    If you want to make an accordion instead, just put your collapsible in a parent with data-role=”collapsible-set” like so:

    <div data-role="collapsible-set">
            <div data-role="collapsible">
                <h1>Collapsible 1</h1>
                <p>
                    Collapsible 1 content.
                </p>
            </div>
    
            <div data-role="collapsible">
                <h1>Collapsible 2</h1>
                <p>
                    Collapsible 2 content.
                </p>
            </div>
        </div>
    </div>
    

    That’s all it takes!

    Switches

    Another thing that you may want to do, is provide switches instead of regular checkboxes. To do this, just make a select with the attribute data-role=”slider”, and give it two options.

    <label>
        <p>
            Turn notifications:
        </p>
        <select  data-role="slider">
            <option value="off">Off</option>
            <option value="on" selected>On</option>
        </select>
    </label>
    

    I made the default state on, by just giving my on option the selected attribute here.

    Headers/footers

    You can make a cool header/footer for your page, by using data-role:

    <div data-role="header" >
      <h1>Header!</h1>
    </div>
    
    <div data-role="footer" >
        <h1>Fixed Footer!</h1>
        Blah blah
    </div>
    

    And you can also use data-position=’fixed’ to make them stick in place.

    Tables

    Tables can either ‘reflow’ or change the number of columns when the screen size changes.

    Reflow

    Reflow is the default mode for a responsive table. and it means that the table columns will change to a stacked presentation on a small screen.

    <table data-role="table" id="movie-table" 
    data-mode="reflow" class="ui-responsive table-stroke">
      <thead>
        <tr>
          <th data-priority="1">Student ID</th>
          <th data-priority="persist">Student Name</th>
          <th data-priority="2">Year</th>
          <th data-priority="3">Mark</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>1</th>
          <td>Errietta Kostala</td>
          <td>2</td>
          <td>100%</td>
        </tr>
        <tr>
          <th>1</th>
          <td>Errietta Kostala</td>
          <td>2</td>
          <td>100%</td>
        </tr>
      </tbody>
    </table>
    

    data-mode=”reflow” is the key here. ui-responsive changes the table to a normal horizontal column view when the screen beocmes big again, and table-stroke just adds a border in the table.

    Column Toggle

    Column toggle means certain columns get hidden when the screen becomes small. This is where htis data-priority and ‘persist’ that you saw above is actually used. The one with data-priority=’persist’ will always stay, then columns will stay depending on how big their priority is. So the one with priority = 4 will be the first to go, and the one with 1 will be the last. Just change data-mode in the table above to be columntoggle to do this; Your page will also automatically acquire a button which allows the user to select which columns are displayed in this mode.

    <table data-role="table" id="movie-table"
    data-mode="columntoggle" class="ui-responsive table-stroke">
      <thead>
        <tr>
          <th data-priority="2">Student ID</th>
          <th data-priority="persist">Student Name</th>
          <th data-priority="3">Year</th>
          <th data-priority="1">Mark</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th>1</th>
          <td>Errietta Kostala</td>
          <td>2</td>
          <td>100%</td>
        </tr>
        <tr>
          <th>1</th>
          <td>Errietta Kostala</td>
          <td>2</td>
          <td>100%</td>
        </tr>
      </tbody>
    </table>
    

    Sliders

    Just use the normal <input type=”range”> syntax.

    <label >Slider:
      <input type="range" data-highlight="true" min="0" max="10" step=".1" value="5">
    </label>
    

    data-highlight can give it a highlight:

    <label >Slider:
        <input type="range" data-highlight="true" min="0" max="10" step=".1" value="5">
    </label>
    

    Put two sliders within data-role=’rangeslider’ for a range slider.

    <div data-role="rangeslider">
        <label for="range-1a">Rangeslider:</label>
        <input type="range" name="range-1a" id="range-1a" min="0" max="100" value="40">
        <label for="range-1b">Rangeslider:</label>
        <input type="range" name="range-1b" id="range-1b" min="0" max="100" value="80">
    </div>
    

    Overall, jquery mobile is at least a good start for building a mobile gui, if it’s not suitable for a full solution. Also, as said earlier, it can be themed to your application’s needs, so you’re not stuck with the default look!

    Until next post, bye bye!

    by Errietta Kostala at September 30, 2013 04:42 PM

    August 09, 2013

    freenode staffblog

    Reminder: Keep your NickServ email up to date.

    If you’ve registered with NickServ within the last few years then you’ll have used an email address and we’ll have sent you a mail to verify it. That will probably be the last time you heard from us…

    …until you forget your password and find yourself unable to identify to your account. When that happens we can send an email (only to that same address) to verify your identify and reset your password.

    You aren’t stuck with the email you originally used though! We’d very strongly recommend you take 5 minutes to double check the set email address is current, especially in light of recent service closures. You don’t need access to your old inbox to change your registered email, just your NickServ password.

    To view the current state of your account, while identified type:

    /msg nickserv info

    If you’d like to then change the registered email address, first…

    /msg nickserv set email [email protected]

    … then check your email inbox. We’ll have sent you another email with instructions to verify this new address.

    Your email address is hidden from other users by default. You can ensure this by setting:

    /msg nickserv set hidemail on

    Thanks for using freenode!

    by Pricey at August 09, 2013 09:18 AM

    July 22, 2013

    freenode staffblog

    Server hosting and trust

    For the purpose of disclosure we have had to make the difficult decision to discontinue a long-standing relationship with a server sponsor.

    As a freenode user you may be aware that our set-up is somewhat untraditional and differs from that of many other IRC networks; servers are sponsored by various companies and educational institutions across the globe and all our infrastructure is centrally managed by the freenode infrastructure team. Generally speaking we do not provide o:lines or other privileges to server sponsors. Whilst it is possible for a sponsor contact to also volunteer as a staffer on the network such recruitment is independent of any server hosting.

    Our staff are expected to work together closely and communication is key in any freenode relationship, be that with users, among staff or with sponsor contacts. It is important to us to be consistent in the way we provide support and apply policy and we expect all volunteers to be intimately familiar with our policies, procedures and philosophies — which in turn means that senior staff invest a lot of time in ensuring that any new recruits are given adequate support when getting to know the ins and outs of the network and what being a freenode volunteer entails.

    Unfortunately one of our server sponsors added an o:line for themselves on the server they sponsored and whilst we do not believe that this was done with any malicious intent, more through thoughtlessness/negligence and having forgotten the expectations set out on our “Hosting a Server” page we feel that we are unable to comfortably and confidently continue the relationship.

    Our number one priority has to be our target communities, the Free and Open Source Software communities that have chosen to make use of freenode in their internet activities.

    Whilst we do not believe and have no evidence to indicate that any user traffic or data has been compromised, we would of course encourage you to change your passwords if you feel that this would make you more comfortable in continuing to use our services.

    We can only apologise for this happening and we’d like to assure you that trust is incredibly important to us and that we are incredibly embarassed that this situation arose in the first place.

    As a result of this we have just replaced our SSL certificates, so if you notice that these have changed then this is the reason why.

    We will of course take this opportunity to remind all our sponsors of our expectations when it comes to providing services to freenode and our target communities.

    Again, we apologise for any inconvenience and we hope that any loss of trust in the network that may have resulted from this incidence can be restored and that your projects will continue to feel comfortable using the network in future.

     

     

    by christel at July 22, 2013 07:19 PM

    mrmist's blog

    Filter in the name of protection

    I think it’s shocking that one of the central pillars of the concept of the Internet, free access to all things, is casually eroded by David Cameron in the name of “protecting the children”. This is appalling. Whilst I’m sure that this will give some poor quality parents an illusion of online safety, saving them from what must surely be a terrible chore of actually having to care about what their children are doing for themselves, filtering traffic by default is a massive blow to online freedoms. This will not make things better. This paves the way for the government to more fully dictate how and what we should view on the Internet in the future – after all, if the technical filters are already in place, why not just increase them a nudge to filter out more content that the government deems “unsuitable”? And, of course, the elephant in the room is that those people who do not have these filters activated, who choose instead to maintain real access to the Internet, will have suspicion cast upon them.

    by Mrmist at July 22, 2013 07:27 AM

    July 17, 2013

    freenode staffblog

    Fosscon, an open source conference in Philadelphia PA, Saturday August 10th

    FOSSCON 2013 will be held on August 10th, 2013.  Several of our very own staff here at freenode will be attending this year and we are really looking forward to it.

    FOSSCON was spawned from the depths of freenode and this will be the 4th event so far.

    We are very excited about this year’s keynote speaker, Philadelphia’s own Jordan Miller, who leads a research team at The University of Pennsylvania. Jordan makes heavy use of open source software and is doing amazing work with 3D printing as it pertains to transplant organs.  http://www.upenn.edu/pennnews/news/penn-researchers-improve-living-tissues-3d- printed-vascular-networks-made-sugar.

    Listed below is a just a quick peek at some of our confirmed speakers and their topics:

    • Bhavani Shankar will be speaking on how to bring in new developers to open source projects.
    • Elizabeth Krumbach Joseph will be speaking on Open Source Systems Administration.
    • Corey Quinn will be speaking on configuration management with Salt.
    • Brent Saner will be speaking on Project.Phree, a wireless mesh project.
    • Dru Lavigne will be speaking on FreeNAS 9.1.
    • Jérôme Jacovella-St-Louis will be hosting a workshop on cross-platform development with the Ecere SDK.
    • John Ashmead will be speaking on the math and science of invisibility.
    • John Stumpo will be offering a workshop on the Challenges facing FOSS game projects.
    • Walt Mankowski will be speaking on Scientific Programming with NumPy and SciPy.
    • Chris Nehren will be speaking on bridging the gap between development and operations.
    • Christina Simmons will be speaking on starting and managing open source events/projects.
    • Hector Castro will be offering a hands-on workshop on the Riak database engine.
    • Dan Langille will be hosting a workshop on Bacula: The Networked Backup Open Source Solution

    If you haven’t registered yet, please do so here: https://www.wepay.com/events/fosscon-2013!  We’ve had such an awesome response so far and are so excited to see how far we can go this year! Invite your friends, your partners, your business associates, and everyone else you know!  We’ll see you soon!

    by JonathanD at July 17, 2013 09:51 PM

    July 15, 2013

    freenode staffblog

    New TLS/SSL Channel Modes & Webchat Features

    We’ve recently enabled some new functionality in our ircd to further help you manage your channels:

    Channel mode +S

    This ensures only users that have connected via TLS/SSL (and so have user mode +Z) are able to join; you can not /invite them through it. It will not prevent the use of the channel by any non-TLS/SSL users already present.

    Extended ban $z

    Documented in ‘/help extban’ for some time, this has also been enabled and matches all TLS/SSL users. Usage is similar to the ‘$a’ type (which matches all identified users) and could for example be set as ‘+q $~z’ to to quiet any users not connected over an ssl connection.

    Webchat

    WEBIRC has been enabled so that behind their hostmask, users can now be considered to be connecting from their real address. This means that a single ban format can apply to both direct connections and webchat connections.

    For example, a user connecting from 171.205.18.52 will still appear as ‘nickname!abcd1234@gateway/web/freenode/ip.171.205.18.52′ but ban masks of the form ‘*!*@171.205.18.52′ will match! This is now the most effective method of matching users using webchat but the realname and hexip username are still available.

    Although freenode’s webchat is available over SSL, the webchat’s localhost connection to the ircd is not SSL, so webchat users do not get user mode +Z. Webchat users will not be able to join a +S channel and will not match the $z extban, even if they are using webchat over SSL.

    Security considerations

    These channel modes can not guarantee secure communication in all cases; if you choose to rely on them, please understand what they can and can’t do, and what other security considerations there are.

    There are a variety of known security problems with SSL, and reasons why the +S mode may not guarantee transport security on freenode. Some of these are:

    • These modes may be unset by channel operators at any time, allowing non-TLS/SSL users to join, and the mode may subsequently be reapplied;
    • If network splits occur it may also be possible for users to bypass +S intentionally or by chance;
    • Clients may be compromised or malicious, or using a malicious shared host;
    • Clients may have traffic intercepted as part of a Man In The Middle (MITM) attack and then transparently forwarded via SSL, invisibly to channel users;
    • There may be issues with TLS/SSL itself in server or client configuration or architecture which compromise its ability to provide effective transport security at the network level (there have been several published attacks against SSL recently – see here).

    This is not an authoritative list, so before using +S as part of any channel which requires strong anonymity, please ensure you understand what it does and its drawbacks.

    There are other security tools you may want to look at – you may want to consider using client plugins that provide additional encryption or route your connection through Tor. Tor also allows you to create spurious traffic to hide real traffic patterns. freenode provides its own hidden Tor node which means you can trust this connection as much as you trust freenode. Your IRC traffic with freenode via Tor is end-to-end encrypted from your Tor client to our Tor node. It does not pass through any third party nodes in unencrypted form.

    Finally, unless you can trust everyone in a channel and are sure it is configured properly and you understand the other technical risks, do not rely on these channel modes exclusively. Security is generally layered; ensure you have good defense in depth and don’t rely on individual controls which may be a single point of failure.

    Using other websites or services via Tor

    Remember to always encrypt your traffic when using Tor as you have no control over who is running exit nodes and if they are doing traffic analysis on them. While your traffic to the exit node is encrypted and the ingress node can not read it, the exit node will always need to be able to remove Tor encryption. If your traffic is clear-text said exit node will be able to read it.

    by Pricey at July 15, 2013 06:34 PM

    June 07, 2013

    Md's blog

    Torre Telecom Italia, Rozzano

    Today I was lucky enough to be able to visit the Telecom Italia telecommunications tower located in Rozzano, just south of Milano, and took some photos.

    This tower, with its 187 meters, is one of the tallest man-made structures in Italy.

    It was built by Telecom Italia in 1990 to create high capacity radio links to Genova and Torino and nowadays it contains radio transmitters for a TV station and many kinds of radio networks.

    It is an impressive monument to an age when telcos had no optical fibers, but plenty of money.

    June 07, 2013 02:09 AM

    May 24, 2013

    freenode staffblog

    Over 9000 * 10

    freenode has been growing slowly and steadily, breaking the next practically-useless-but-still-kinda-neat barrier of more than 90,000 concurrent connections at the same time. It’s very nice, and humbling, to know that we are able to enable so many people to communicate with each other.

    I shouldn’t have added a month of leeway at the last second to my last prediction so the scary scary 100,000 is officially targeted for May 2014. Yes, the pace at which freenode is growing seems to be increasing ever so slightly.

    Historic posts for those of you keeping track:

    http://blog.freenode.net/2007/08/freenode-has-reached-40-000-users/

    http://blog.freenode.net/2008/09/50000-active-users/

    http://blog.freenode.net/2009/12/happy-new-year-2010/

    http://blog.freenode.net/2011/01/freenode-70k/

    http://blog.freenode.net/2012/04/80k/

    by RichiH at May 24, 2013 09:11 PM

    May 14, 2013

    freenode staffblog

    The good, the bad, and the ugly…

    Firstly, I would like to apologise for the interruptions the network has experienced in the last week (and continues to experience as we speak). I would also like to thank our incredible server sponsors for the time and dedication they have shown in helping us attempt to deal with the situation.

    Sponsors — sponsors are the lifeblood of the network; without sponsors there would be no freenode. Unfortunately, the recent attacks have been significant enough for some of our sponsors to pull the plug as they were unable to continue providing the same level of assistance to the network as they had in the past. These kind of attacks can be costly for our sponsors; the disruptions soon have a financial impact for sponsors and their paying clients when service is disrupted. They are also costly in time and resources spent trying to alleviate the issues caused within their networks. To those of our sponsors who have had to discontinue sponsorship, in part or in full, I would like to thank you for the years of support. Not just for freenode but for the Free and Open Source Software Communities and we can only apologise for the difficulties your organisations have experienced as a result of these recent attacks.

    Free and Open Source Software Communities — whilst sponsors may be the lifeblood of the network, the FOSS communities are our reason for being. Unfortunately they, along with our sponsors, are the ones suffering at the hands of the attacker(s) — it is their projects that are disrupted and affected and we can only apologise for the instability and disruption experienced by projects on the network in this last week.

    freenode — ironically freenode is the puzzle piece that gets off lightly. We’re just a bunch of people passionate about FOSS — the network itself is devoid of feeling and whilst our volunteers do their best dealing with the aftermath of the attacks and try to keep the network up and running the reality is that in the grand scheme of things freenode is nothing. freenode is just a means to an end; the projects that have chosen to use freenode could easily go elsewhere, the volunteers who staff the network… well, they could easily go wherever their projects went — we volunteer for freenode because we’re passionate about FOSS, and the majority of us also contribute to one or several FOSS projects or have done in the past. For us it has never been about “freenode” — it has been about FOSS; and the projects we, as individuals, care about. We are all freenode users first, and staffers second.

    If there was no freenode, there would be other alternatives — perhaps similar alternatives, perhaps very different alternatives. The FOSS communities are full of talented, passionate people and I have no doubt that we’d all find different ways to stay in touch and work on our projects even if there was no freenode.

    That’s not to say we’re about to throw in the towel — we’ve all invested a lot of time and effort in the network and I am sure we will continue to do so for as long as there are projects wanting to use it and sponsors willing to help us.

    I wish I could provide you with detailed information about the attacks and the cause of them — but these details are but a mystery to us and with nearly 90,000 users I’d be loathe to speculate as to who we might have annoyed… or how. For the time being, we intend to continue mitigating attacks where possible and continuing to endeavour to provide service as usual!

    Once more, thank you for the support and the faith in the project — and thank you for the patience whilst our infrastructure team desperately tries to juggle our infrastructure around to bring back as much of our normal services as is possible at this point in time.

    by christel at May 14, 2013 09:28 PM

    January 11, 2013

    mquin's blog

    Notes and iCloud

    One of the tools included as part of iOS6 and OS 10.8 is a simple but useful note taking app, unsurprisingly named 'Notes', which is also part of the iCould service and can be synced between devices over the internet.

    I've used Notes lightly since it appears, primary to jot things down when I'm travelling (I used it a lot at Worldcon), and as a shared clipboard to move URLs and small snippets of text between my Mac and the iPad.

    What isn't readily obvious when using the application in either guise, or the iCloud web service, is how the notes are stored.

    It becomes apparent, however, if you use a third-party mail client to access your iCloud email account. There is a 'Notes' folder, hidden when viewed in Mail.app, which contains, as you might expect, your notes in standard e-mail message form.

    Okay, so we can read notes over IMAP, can we write them? Editing existing ones works as expected, but just saving a new email message into the folder doesn't - the message is visible to the IMAP client, but note to the Notes app.

    So lets look a little closer at the headers on the notes from Notes:

    Subject: An uninteresting note
    From: Me <[email protected]>
    X-Universally-Unique-Identifier: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
    Content-Type: text/html;
            charset=utf-8
    X-Uniform-Type-Identifier: com.apple.mail-note
    Message-Id: <[email protected]>
    Date: Wed, 25 Jul 2012 23:43:09 +0100
    X-Mail-Created-Date: Wed, 25 Jul 2012 23:43:09 +0100
    Content-Transfer-Encoding: quoted-printable
    Mime-Version: 1.0 (1.0)
    

    Mostly what I would expect, apart from the X-Universally-Unique-Identifier and X-Uniform-Type-Identifier headers, which turn out to be the magic trick. Create a new message with those headers (with a new UUID in the unique-identifier one), and hey presto, it appears on iCloud and in Notes.

    With a little bit of help from offlineimap and some shell glue it is not particularly hard to use this mechanism to create new notes, or edit existing ones, from the command line.

    January 11, 2013 06:17 PM

    October 31, 2012

    mquin's other blog

    FIN

    With the increasing amount of tumbleweed around here I've decided to draw a line under my journal. My infrequent ramblings will likely go to http://www.elite.uk.com/mike/ or the usual social media places in future.

    October 31, 2012 04:33 PM

    mquin's blog

    A small wireless sensor network

    Back in the dim and distant I wrote about collecting electricity consumption data. In the intervening time and due to some hardware changes and failures I had stopped doing this.

    Recently, Graeme Thomson gave a talk at ScotLUG about the system he is using to monitor temperatures around his house, using a 1-wire sensor network. Inspired by this, I decided to revisit my monitoring efforts.

    Graeme's system took advantage of the fact that his house had recently been rewired and at that time he had laid in a number of twisted pair drops into each room, which could easily be patched onto his 1-wire bus.

    Not wanting to run more cable around my own flat I decided to look at the possibility of doing the same thing wirelessly, and I remembered, from my Arduino tinkering, about the JeeNode project.

    JeeNodes are compact, low-power Arduino-compatible AVR microcontroller boards with onboard wireless modules. They are very versatile and ideal as a basis for all sorts of wireless sensor nodes. They are also relatively inexpensive, particularly when purchased in kit form.

    After a bit of tinkering around I settled on a sensor node design consisting of a JeeNode with an AA battery based power supply, and a DS18B20 digital temperature sensor.

    Assembled sensor node

    I now have four of these scattered around the flat, running a simple sketch that takes a sample from the temperature sensor every minute or so and transmits it back to my central server. The 878Mhz radio system seems to comfortably cover the entire building.

    Using one JeeNode per sensor is not the cheapest way of doing this, but saves me pulling cable and leaves me with a lot of flexibility should I wish to expand this system or monitor additional parameters in future.

    The end result: pretty graphs, and a better understanding of how the temperature in my flat changes over time.

    24 hour temperature graph

    Ideas that I have for the future include replacing the node near my server with a Arduino Nano, rather than using wireless to span half a metre, and reuse the JeeNode in another room.

    October 31, 2012 04:14 PM

    Moving On

    With the current goings on over at LiveJournal, and the realization that I'm not really paying much attention to it any more I've decided to move my meagre creative output over to ikiwiki here. My old LJ posts will remain where they are, but I won't be updating it further.

    October 31, 2012 02:40 PM

    June 28, 2012

    mrmist's blog

    Think of the children!


    This article is tagged with:

    I’m highly amused that the government are selling the latest bundle of internet censorship as a child protection measure. Apparently all our Internets should be filtered by default because that makes it safer for children. Notice that this is billed as some kind of anti-porn filter, but the actual block is against “harmful content”, which could essentially be “whatever the government wants to block by default.” The reason that we need this seems to be that “Growing numbers of parents do not feel in control of what their families are exposed to online”. Apparently actually watching what your kids are doing and engaging with them so that they don’t need a nannying internet filter is too much effort for the modern family.
    Sigh.

    by Mrmist at June 28, 2012 07:13 AM

    May 24, 2012

    mquin's other blog

    Being Digital

    Something I've not really spoken about here yet but which has been taking up a lot of my time over these last months is study.

    I decided last year to start working towards a degree part-time with the Open University. The first stage of this has been a six-month computing and information technology module entitled "My Digital Life".

    Going back to school, as it were, well over a decade since I finished college has been a bit of a change. The course itself has been very engaging, doing a good job of mixing a fairly broad range of topics from the early history of computing to more modern areas such as social networking and wireless sensor networks, with research and study skills.

    As you might expect, the OU - established as a distance learning university - has embraced technology for education, and the tools used to deliver the course were a great help.

    Coming to the end of this module I'm fairly confident that I have done well, and although I feel I'm still developing in terms of being able to think and write like an academic I have found it to be very enjoyable.

    May 24, 2012 09:30 AM

    90 days

    A little late, and this should be the last of these posts for a while - don't want to bore you all.

    85kgs seems to be the awkward point, with my weight floating around that mark for the last month or so. I'm fairly happy with the results and I've been starting with a bit of strength work in the last few weeks.

    As the weather is getting better I'm hoping to spend more time on the bike as well.

    Small moves, but it seems to be working.

    May 24, 2012 09:19 AM

    April 13, 2012

    mquin's other blog

    60 days

    Another 30 days done, and perhaps surprisingly I'm managing to stick with it.
    As of this morning I'm 10.3kg lighter than I was in February, a shallower drop (3.6kg) than in the first 30 but I was expecting it to tail off as my overall weight dropped.
    Aside from the obvious fact of being quite a bit lighter than I was, and visibly slimmer (not exactly a chiselled Adonis, but Rome wasn't built in two months) this exercise has had the positive effect of getting me into a routine with regards to eating, which I hope will stick. I'm still far from a morning person but having a decent breakfast every days feels like a good thing.

    April 13, 2012 09:24 AM

    April 09, 2012

    Pricey's blog

    identifying to the freenode testnet with certfp


    freenode will be upgrading their services very soon. One of the major new features that this upgrade will bring is the ability to identify using ssl certificates. Here's a very quick guide on how to get started.

    I used atoponce's guide for oftc when writing this up.

    You can connect to freenode using ssl without using certfp to identify.

    Generating your own certificate

    You will need openssl installed. Check your operating systems documentation for this. Once done, the following commands will create a certificate and set sensible permissions:
    mkdir -p ~/.irssi/certs
    cd .irssi/certs/
    openssl req -nodes -newkey rsa:2048 -keyout mynick.key -x509 -days 365 -out mynick.crt
    cat mynick.crt mynick.key > mynick.pem
    chmod 0400 mynick.key mynick.pem

    Needless to say, don't give anyone these files!

    Connecting with SSL

    The testnet is available at irc://testnet.freenode.net:9003 on ssl so make sure you are connecting to that!

    After starting irssi, that means something like:
    /network add freenodetest
    /server add -auto -ssl -ssl_cert ~/.irssi/certs/mynick.pem -network freenodetest testnet.freenode.net 9003
    /save
    /connect freenodetest

    Or if modifying an existing server config:
    use_ssl = "yes";
    ssl_verify = "no";
    ssl_cert = " ~/.irssi/certs/mynick.pem ";

    Once you launch irssi, you should see that you are given usermode +Z:
    13:41:49 -!- Mode change [+Z] for user Pricey


    If you /whois yourself, you should also see your certificate fingerprint:
    14:04:43 -!- Pricey [[email protected]]
    14:04:43 -!- ircname : pricechilde
    14:04:43 -!- server : barjavel.freenode.net [Paris, FR]
    14:04:43 -!- : is using a secure connection
    14:04:43 -!- : has client certificate fingerprint aaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbb0000
    14:04:43 -!- hostname : 76.10.213.24 76.10.213.24
    14:04:43 -!- idle : 0 days 0 hours 0 mins 3 secs [signon: Fri Apr 6 14:04:40 2012]
    14:04:43 -!- End of WHOIS

    If you don't see the fingerprint line, you need to go back and figure out what you've done wrong.

    Giving Services your certificate fingerprint

    Finally, we need to tell services about our certificate fingerprint. (If you haven't specified your account password as your server password, sasl'd or had a script take care of it, identify first!)
    /msg nickserv cert add aaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbb0000
    (using the fingerprint from your whois.)

    One final thing of note is that the testnet is using a self signed certificate. You can not simply use the ssl_capath option to point to your distributions existing ssl certificates. Irssi will warn you that this is the case and not connect.

    by Joseph Price ([email protected]) at April 09, 2012 02:23 PM

    March 31, 2012

    mrmist's blog

    Skyrim mod error 16


    This article is tagged with: , ,

    This ate up time today, so I though I’d note it down here. All attempts to download Skyrim mods using the Steam Workshop were create error 16 unable to download file when starting the launcher. There’s barely anything on the internets about what causes this error, or how to fix it, so here’s my findings. To fix it in my case, I tried a number of things, but the only ones that ultimately produced a working download were to grant my user full control on the skyrim data directory, and to completely exit and restart steam. Which one of those actually did anything, I’m not sure. Probably this is as simple as “restart the steam client”…

    by Mrmist at March 31, 2012 10:58 AM

    March 14, 2012

    mquin's other blog

    low-content management

    In what is perhaps conclusive proof that I've been spending way too much time around RichiH, I've taken a partial dive into ikiwiki and applied it to my dust-strewn homepage - www.elite.uk.com/mike.

    For the moment this is just adding a nice form of content management to the handful of static pages which live there - my weblog is still being pulled from LJ for the time being - but it's proving to be enough of an experience to get my head around what ikiwiki can do without being too much of a time sink.

    March 14, 2012 05:06 PM

    30 Days

    Towards the middle of February it was becoming apparent to me that I'd put on quite an insulating layer of fat over the winter, most likely due to having slipped back into the old bad habits of snacking and drinking soda at work.

    My height hides it to a certain extent but at a little over 95kg I was getting very close to being at least numerically overweight, and more pertinently (or should the be vainly), I wasn't particularly happy with that I could see looking in the mirror in the morning.

    So I decided to do something about it. I'm not one for fads or gimmicks so I've not been taken in by Atkins or any similar 'cheat' diets, just keeping a very close count of what I'm eating, and being disciplined about how and when I eat.

    As it turns out, without snacking what I do tend to eat on a day-to-day basis works out neatly in the 1200-1500 kCal range which is suggested for weight loss, so I've not needed to make a lot of adjustments and despite the inevitable bouts of hunger I've been managing to stick to it.

    So, here I am, 30 days in, and 6.7kg lighter than I was when I started, a very encouraging start.

    March 14, 2012 08:10 AM