Updates again:

write about a simple fs for trello.
write about tls certificates on 9front.
change page sub-title.
M sites/pmikkelsen.com/_werc/config +1 -1
@@ 1,3 1,3 @@ 
 masterSite=pmikkelsen.com
 siteTitle='Peter's website'
-siteSubTitle='A blog'
+siteSubTitle='random notes'

          
A => sites/pmikkelsen.com/plan9/lets_encrypt.md +55 -0
@@ 0,0 1,55 @@ 
+## How I get tls certificates for 9front
+
+First of all, I use linux and drawterm for this for now, but
+I would like to be able to do it all from 9front at some point.
+
+## Generate the certificate
+
+Install certbot on linux and run the following command
+
+	certbot certonly --manual -d pmikkelsen.com -d vps1.pmikkelsen.com
+
+and do the challenges, they should be easy.
+
+## Importing the cert and private key
+
+Start drawterm and login as the hostowner. After this, the filesystem of the linux
+system is available at `/mnt/term`. Run the following:
+
+	cd /sys/lib/tls/
+	cp /mnt/term/etc/letsencrypt/live/pmikkelsen.com/privkey.pem ./
+	cp /mnt/term/etc/letsencrypt/live/pmikkelsen.com/fullchain.pem ./cert
+	
+Now the private key must be converted to one that can be loaded into factotum
+
+	auth/pemdecode 'PRIVATE KEY' privkey.pem | auth/asn12rsa -t 'service=tls role=client' > key
+	rm privkey.pem
+	chmod 400 key
+
+Add the following to `/cfg/$sysname/cpurc` to load the private key on boot.
+
+	cat /sys/lib/tls/key >> /mnt/factotum/ctl
+
+Done.
+
+## SMTP over TLS
+
+I have the following in `/bin/service.auth/tcp25`
+
+	#!/bin/rc
+
+	user=`{cat /dev/user}
+	exec upas/smtpd -c /sys/lib/tls/cert -n $3
+
+Notice I had to put it in the `/bin/service.auth` folder so that it could find the private key.
+
+## Https with rc-httpd
+
+I have the following in `/bin/service.auth/tcp443`
+
+	#!/bin/rc
+	
+	exec tlssrv -c /sys/lib/tls/cert -l /sys/log/https /bin/service/tcp80 $*
+
+Again, in the `/bin/service.auth` folder. It simply wraps the plain http service
+in a tls wrapper.

          
A => sites/pmikkelsen.com/plan9/trellofs.md +347 -0
@@ 0,0 1,347 @@ 
+## A filesystem for viewing trello
+
+Since my last note [here](http://pmikkelsen.com/plan9/basic_9p_server)
+was very simple, here comes
+a small program (around 250 lines of C), which makes it
+possible to view trello as a read-only filesystem.
+
+## Usage example
+
+First set the `trellokey` and `trellotoken` environment
+variables to the API key and token from the trello website.
+
+	term% trellokey=1298791723987937123.....
+	term% trellotoken=923748237497ab798798999999.....
+
+Then `webfs` must be started so the program can make
+requests to the trello REST api.
+
+	term% webfs
+
+Then `trellofs` itself must be started. It mounts the filesystem under `/mnt/trello`.
+
+	term% trellofs
+
+If I now go and see which boards I have, I get the following
+
+	term% cd /mnt/trello
+	term% ls
+	Beaglebone_Black
+	Fly
+	Fysik_Rapport
+	Guld_kobber
+	Ideer_til_2._produkt
+	Lektier
+	P0
+	P1
+	P2
+	Projekt_Materialer
+	Spil
+	Welcome_Board
+
+The names of the folders are almost like the original board names, except
+that that some characters have been replaced by `_`. Lets look at `P1`
+
+	term% cd P1
+	term% ls
+	Backlog
+	Done
+	Estimat
+	In-progress
+	Info
+	Master.C_Top_Down_Ting
+	Sprint_1
+	Test
+	Test_sprint_1
+	Todo
+
+Todo must have something worth looking at
+
+	term% ls
+	Færdig_rapport_aflevering
+	Korrekturlæsning
+
+Those two items are the actual trello "cards", which I think of as tasks. The first
+one means "Final report hand in", so let's have a look at that.
+
+	term% cd Færdig_rapport_aflevering
+	term% ls
+	description
+	duedate
+	url
+
+There are always three files inside each card folder, but since the information in them
+is not mandatory on trello, some of them will be empty.
+
+	term% cat description
+
+	term% cat duedate
+	2019-12-18T13:00:00.000Z
+	term% cat url
+	https://trello.com/c/u23ZcNAm/9-f%C3%A6rdig-rapport-aflevering
+
+## Limitations
+
+The key and token are right now in environment variables, which is somewhat
+awkward. Also I would like to be able to actually create and move cards directly
+from the filesystem just by creating files, but that is not something I will implement
+right now.
+
+## The code
+
+The code for the `trellofs` program is listed below.
+
+	#include <u.h>
+	#include <libc.h>
+	#include <fcall.h>
+	#include <thread.h>
+	#include <9p.h>
+	#include <json.h>
+	
+	#define MAX_RESPONSE_SIZE (1024 * 1024 * 8)
+	
+	char *key;
+	char *token;
+	
+	void
+	fsread(Req *r)
+	{
+		char *str;
+	
+		if (r->fid->file->aux == nil) {
+			readstr(r, "");
+		} else {
+			str = malloc(strlen(r->fid->file->aux) + 2);
+			sprint(str, "%s\n", r->fid->file->aux);
+			readstr(r, str);
+		}
+		respond(r, nil);
+	}
+	
+	Srv fs = {
+		.read = fsread,
+	};
+	
+	JSON *
+	trelloget(char *endpoint, char *params)
+	{
+		int ctlfd, bodyfd;
+		int n;
+		int err;
+		char *buf;
+		JSON *res;
+	
+		res = nil;
+		bodyfd = -1;
+	
+		buf = malloc(MAX_RESPONSE_SIZE + 1);
+		if(buf == nil) {
+			perror("malloc");
+			return nil;
+		}
+	
+		ctlfd = open("/mnt/web/clone", ORDWR);
+		if(ctlfd < 0) {
+			perror("open");
+			goto fail;
+		}
+	
+		if(read(ctlfd, buf, 32) < 0) {
+			perror("read");
+			goto fail;
+		}
+		
+		n = atoi(buf);
+	
+		sprint(buf, "url https://api.trello.com/%s?key=%s&token=%s", endpoint, key, token);
+		if(params)
+			sprint(buf + strlen(buf), "\&%s\n", params);
+		else
+			sprint(buf + strlen(buf), "\n");
+	
+		err = write(ctlfd, buf, strlen(buf));
+		if(err < 0){
+			perror("write");
+			goto fail;
+		}
+		
+		sprint(buf, "/mnt/web/%d/body", n);
+		bodyfd = open(buf, OREAD);
+		if(bodyfd < 0){
+			perror("open");
+			goto fail;
+		}
+	
+		err = readn(bodyfd, buf, MAX_RESPONSE_SIZE);
+		if (err < 0) {
+			perror("read");
+			goto fail;
+		}
+	
+		res = jsonparse(buf);
+	
+	fail:
+		close(ctlfd);
+		close(bodyfd);
+		free(buf);
+	
+		return res;
+	}
+	
+	char *
+	escapename(char *str)
+	{
+		char *new;
+		int i, len;
+	
+		len = strlen(str);
+	
+		new = malloc(len + 1);
+		for(i = 0; i <= len; i++){
+			switch(str[i]){
+			case '/':
+			case ' ':
+			case ',':
+			case ':':
+			case '"':
+			case '\'':
+				new[i] = '_';
+				break;
+			default:
+				new[i] = str[i];
+			}
+		}
+		return new;
+	}
+	
+	void
+	addcard(File *dir, JSON *card)
+	{
+		JSON *element;
+		char *filename;
+		File *carddir;
+		char *description;
+		char *url;
+		char *duedate;
+	
+		element = jsonbyname(card, "name");
+		filename = escapename(element->s);
+		carddir = createfile(dir, filename, nil, DMDIR|0555, nil);
+		if(carddir == nil){
+			perror("createfile");
+			return;
+		} else {
+			element = jsonbyname(card, "desc");
+			description = strdup(element->s);
+			element = jsonbyname(card, "url");
+			url = strdup(element->s);
+			element = jsonbyname(card, "due");
+			if (element->t == JSONString)
+				duedate = strdup(element->s);
+			else
+				duedate = nil;
+			createfile(carddir, "description", nil, 0444, description);
+			createfile(carddir, "url", nil, 0444, url);
+			createfile(carddir, "duedate", nil, 0444, duedate);
+		}
+		free(filename);
+	}
+	
+	void
+	addlist(File *dir, JSON *list)
+	{
+		JSON *element;
+		char *filename;
+		JSON *cards;
+		JSONEl *card;
+		char cardsEndpoint[128];
+		File *listdir;
+	
+		element = jsonbyname(list, "name");
+		
+		filename = escapename(element->s);
+		listdir = createfile(dir, filename, nil, DMDIR|0555, nil);
+		if(listdir == nil){
+			perror("createfile");
+			return;
+		} else {
+			element = jsonbyname(list, "id");
+			sprint(cardsEndpoint, "1/lists/%s/cards", element->s);
+			cards = trelloget(cardsEndpoint, "fields=desc,name,url,due");
+			if(cards == nil)
+				return;
+	
+			for(card = cards->first; card != nil; card = card->next){
+				addcard(listdir, card->val);
+			}
+		}
+		jsonfree(cards);
+		free(filename);
+	}
+	
+	void
+	addboard(File *root, JSON *board)
+	{
+		JSON *element;
+		JSONEl *list;
+		char *filename;
+		File *boarddir;
+	
+		element = jsonbyname(board, "name");
+		filename = escapename(element->s);
+		boarddir = createfile(root, filename, nil, DMDIR|0555, nil);
+		if(boarddir == nil) {
+			perror("createfile");
+			return;
+		}
+	
+		element = jsonbyname(board, "lists");
+		for(list = element->first; list != nil; list = list->next){
+			addlist(boarddir, list->val);
+		}
+		
+		free(filename);
+	}
+	
+	void
+	trelloinit(File *root)
+	{
+		JSON *result;
+		JSONEl *board;
+	
+		result = trelloget("1/members/me/boards", "fields=name,lists&lists=open");
+	
+		for(board = result->first; board != nil; board = board->next){
+			addboard(root, board->val);
+		}
+		jsonfree(result);
+	}
+	
+	void
+	main(void)
+	{
+		JSONfmtinstall();
+	
+		key = getenv("trellokey");
+		token = getenv("trellotoken");
+	
+		Tree *tree;
+	
+		tree = alloctree(nil, nil, DMDIR|0555, nil);
+	
+		fs.tree = tree;
+		trelloinit(tree->root);
+	
+		postmountsrv(&fs, nil, "/mnt/trello", MREPL | MCREATE);
+	}
+
+The program can be compiled by hand or by using the following `mkfile`.
+
+	BIN=/usr/glenda/bin/amd64
+	
+	TARG=trellofs
+	
+	OFILES=\
+		main.$O\
+	
+	</sys/src/cmd/mkone