Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/limbo/appl/cmd/Blogger.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


implement Blogger;

include "sys.m";
	sys : Sys;
include "draw.m";
	Context: import Draw;

include "string.m";
	str : String;

include "factotum.m";
	auth : Factotum;

include "arg.m";
	arg : Arg;

include "xmhell.m";
	xml : Xml;
	Parser, Item, Attributes : import xml;

include "sslsession.m";
include "keyring.m";
include "asn1.m";
include "pkcs.m";
include "x509.m";
include "ssl3.m";
	ssl3: SSL3;

include "httpc.m";
	httpc : Httpc;
	Request, Response, Connection : import httpc;

include "Blogger.m";

googleaddr : con "tcp!64.233.183.104!443";
bloggeraddr : con "tcp!72.14.207.191!80";

log_in(email, password : string) : ref User
{
	if(email =="" || password =="") raise "username or password blank";
	c := httpc->new_ssl_connection(googleaddr);
	if(c == nil) raise "Connection (SSL) to " + googleaddr + " failed";

	data := "Email=" + httpc->urlencode(email) + "&Passwd=" + httpc->urlencode(password) + "&source=GroundZero-BloggerModule-1&service=blogger";

	r := c.send_request(ref Request("www.google.com", "POST", "/accounts/ClientLogin", "1.1", "Inferno: Maht's Blogger Module", "Content-Type: application/x-www-form-urlencoded" :: nil, array of byte data));

	(nil, bits) := sys->tokenize(r.body_string(), "\n");
	while(bits != nil) {
		(i,b) := sys->tokenize(hd bits, "=");
		if(i > 0)
			if(hd b == "Auth")
				return ref User(hd tl b, nil);
		bits = tl bits;
	}
	return nil;
}

mime(mimetype, body : string) : ref Mime
{
	case(mimetype) {
	"text/plain" or "text" or "" =>
		return ref Mime.text(body);
	"text/html" =>
		return ref Mime.html(body);
	"text/xhtml" =>
		return ref Mime.xhtml(body);
	"image/png" =>
		return ref Mime.png(body);
	}
	return nil;
}

extract_blog(psr : ref Parser) : ref Blog
{
	blog : ref Blog;
	i := psr.next();
	tag, mtype : string;
	while(i != nil) {
		pick k := i {
			Tag =>
				tag = k.name;
				case(tag) {
				"id" =>
					blog = ref Blog;
					psr.down();
				"published" =>
					psr.down();
				"updated" =>
					psr.down();
				"category" =>
					blog.tags = k.attrs.get("term") :: blog.tags;
				"title" =>
					mtype = k.attrs.get("type");
					psr.down();
				"summary" =>
					mtype = k.attrs.get("type");
					psr.down();
				}
			Text =>
				case(tag) {
				"id" =>
					(nbits, bits) := sys->tokenize(k.ch, "-");
					if(nbits == 3)
						blog.id = hd tl tl bits;
				"title" =>
					blog.title = mime(mtype, k.ch);
				"published" =>
					blog.published = k.ch;
				"updated" =>
					blog.updated = k.ch;
				"summary" =>
					blog.summary = mime("", k.ch);
				}
				psr.up();
		}
		i = psr.next();
	}
	return blog;
}

extract_blog_list(r : ref Response) : list of ref Blog
{
	blogs : list of ref Blog;
	blogs = nil;

	psr := xml->init_io(r.body_string(), nil, "");
	i := psr.next();
	while(i != nil) {
		pick k := i {
			Tag =>
				case(k.name) {
				"feed" =>
					psr.down();
				"entry" =>
					psr.down();
					blogs = extract_blog(psr) :: blogs;
					psr.up();
				}
		}
		i = psr.next();
	}
	return blogs;
}

User.fill_blog_list(u : self ref User)
{
	c := httpc->new_connection(bloggeraddr);
	if(c == nil) raise "Connection to " + bloggeraddr + " failed";
	r := c.send_request(ref Request("www.blogger.com", "GET", "/feeds/default/blogs", "1.1", "Inferno: Maht's Blogger Module", "Authorization: GoogleLogin auth=" + u.authtoken :: nil, nil));
	if(r != nil)
		u.blogs = extract_blog_list(r);
}

User.blog_by_id(u : self ref User, id : string) : ref Blog
{
	b := u.blogs;
	while(b != nil) {
		if((hd b).id == id)
			return hd b;
		b = tl b;
	}
	return nil;
}

User.blog_by_name(u : self ref User, name : string) : ref Blog
{
	b := u.blogs;
	while(b != nil) {
		pick bb := (hd b).title {
			text or html or xhtml =>
				if(bb.data == name)
					return hd b;
		}
		b = tl b;
	}
	return nil;
}


Blog.new_post(b : self ref Blog, u : ref User, e : ref Entry, draft : int) : string
{
	c := httpc->new_connection(bloggeraddr);
	if(c == nil) raise "Connection to " + bloggeraddr + " failed";

	hdrs := "Authorization: GoogleLogin auth=" + u.authtoken :: "Content-Type: application/atom+xml" :: nil;

	if(e.slug != "");
		hdrs = "Slug: " + e.slug :: hdrs;

	r := c.send_request(ref Request("www.blogger.com", "POST", "/feeds/" + b.id + "/posts/default", "1.1", "Inferno: Maht's Blogger Module", hdrs, array of byte e.xml(draft)));

# temporary
	if(r != nil)
		return r.to_string();
	return "";
}

Mime.xml(mp : self ref Mime, tag : string) : string
{
	pick m := mp {
		png =>
			return "<" + tag + " src='" + m.src + "' />";
		html=>
			return "<" + tag + " type='html'>" + m.data + "</" + tag + ">";
		xhtml=>
			return "<" + tag + " type='xhtml'>" + m.data + "</" + tag + ">";
		text=>
			return "<" + tag + ">" + m.data + "</" + tag + ">";
	}
	return "";
}

Mime.plain(mp : self ref Mime) : string
{
	pick m := mp {
		png =>
			return m.src;
		html or xhtml or text =>
			return m.data;
	}
	return "";
}

Entry.xml(e : self ref Entry, draft : int) : string
{
	x := "<entry xmlns='http://www.w3.org/2005/Atom'>";
	if(e.title != nil)
		x +=  e.title.xml("title");

	if(draft)
		x += "<app:control xmlns:app=\"http://purl.org/atom/app#\"><app:draft>yes</app:draft></app:control>";

	if(e.summary != nil)
	 	x += e.summary.xml("summary");
	if(e.content != nil)
		x += e.content.xml("content");

	if(e.author_name != "" || e.author_email != "") {
		x +=  "<author>";
		if(e.author_name != "")
			x += "<name>" + e.author_name + "</name>";
		if(e.author_email != "")
			x += "<email>" + e.author_email + "</email>";
		x += "</author>";
	}

	if(e.updated != "")
		x += "<updated>" + e.updated + "</updated>";
	if(e.id != "")
		x += "<id>" + e.id + "</id>";

	if(e.tags != nil) {
		tags := e.tags;
		while(hd tags != nil) {
			x += "";
			tags = tl tags;
		}
	}

	x += "</entry>";
	return x;
}

User.list_blogs(u : self ref User)
{
	numblogs : int;
	b := u.blogs;
	for(numblogs = 0; b != nil; b = tl b) {
		sys->print("blog: %s, %s\n", (hd b).id, (hd b).title.plain());
		numblogs++;
	}
	if(numblogs == 0)
		sys->print("No blogs\n");
}

User.new_post(u : self ref User, subject, blogid : string, draft : int) : string
{
	blog := u.blog_by_id(blogid);
	if(blog == nil) raise "Blog ID not found";
	e := ref Entry;
	e.title = ref Mime.text(subject);
	data := array[1024] of byte;
	stdin := sys->fildes(0);
	read := sys->read(stdin, data, len data);
	content := "";
	while(read > 0) {
		content += string data[0:read];
		read = sys->read(stdin, data, len data);
	}
	e.content = ref Mime.text(content);

	return blog.new_post(u, e, draft);
}



init(nil: ref Context, args: list of string) {
	sys = load Sys Sys->PATH;
	str = load String String->PATH;
	xml = load Xml Xml->PATH;
	xml->init();
	arg = load Arg Arg->PATH;
	arg->init(args);
	arg->setusage("Blogger: [-l] [-n blogid -s subject -d]");
	auth = load Factotum Factotum->PATH;
	auth->init();

	httpc = load Httpc Httpc->PATH;

	user := pass := action := blogid := subject := "";
	draft := 0;

	while((c := arg->opt()) != 0)
		case c {
		'u' => user = arg->arg();
		'p' => pass = arg->arg();
		'l' => action = "list";
		'n' => 
			action = "new";
			blogid = arg->arg();
		's' =>
			subject = arg->arg();
		'd' =>
			draft = 1;
		}

	case user {
	"" =>
		(user, pass) = auth->getuserpasswd("proto=pass dom=www.google.com");
	* =>
		if(pass != "")
			(user, pass) = auth->getuserpasswd("proto=pass dom=www.google.com user=" + user);
	}		
			

	{
		u := log_in(user, pass);
		u.fill_blog_list();
		case action {
		"list" =>
			u.list_blogs();
		"new" =>
			if(subject != "")
				sys->print("%s", u.new_post(subject, blogid, draft));
			else
				raise "Subject required";
		}
	} exception e {
		"*" =>
			sys->fprint(sys->fildes(2), "%s\n", e);
	}	

}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].