implement Flickr; include "sys.m"; sys: Sys; include "draw.m"; draw : Draw; include "keyring.m"; keyring: Keyring; include "httpc.m"; httpc : Httpc; Request, Connection, Response : import httpc; include "xmhell.m"; xml: Xml; Parser, Item, Attributes, Mark: import xml; include "mime.m"; mime : Mime; Document, Disposition : import mime; include "sslsession.m"; include "asn1.m"; include "pkcs.m"; include "x509.m"; include "ssl3.m"; ssl3: SSL3; include "Flickr.m"; init() { sys = load Sys Sys->PATH; keyring = load Keyring Keyring->PATH; httpc = load Httpc Httpc->PATH; httpc->init(); xml = load Xml Xml->PATH; xml->init(); mime = load Mime Mime->PATH; } debug(b : array of byte) { t := array of byte sys->sprint("%s\n", string b); sys->write(sys->fildes(2), t, len(t)); } query_string(a: ref Auth, method: string, parameters : list of ref Parameter) : string { p : ref Parameter; query_string := ""; if(a != nil) { if (a.token != "") query_string += "auth_token=" + a.token + "&"; query_string += "api_key=" + a.apikey + "&"; } query_string += "method=flickr." + method; for(ps:=parameters; ps != nil; ps = tl ps) { p = hd ps; query_string += "&" + httpc->urlencode(p.name) + "=" + httpc->urlencode(p.value) ; } return query_string; } sign_parameters(a: ref Auth, method : string, parameters : list of ref Parameter) : string { digest := array[16] of byte; p : ref Parameter; unhashed := a.secret; if (a != nil) { unhashed += "api_key" + a.apikey; if(a.token != "") unhashed += "auth_token" + a.token; } if(method != "") unhashed += "methodflickr." + method; for(ps:=parameters; ps != nil; ps = tl ps) { p = hd ps; unhashed += p.name + p.value; } keyring->md5(array of byte unhashed, len array of byte unhashed, digest, nil); digest_text := ""; for(i:=0; i<16; i++) { digest_text += sys->sprint("%02x", int(digest[i])); } sys->print("%s unhashed\n%s hashed\n", unhashed, digest_text); return digest_text; } send_request(a: ref Auth, method : string, parameters : list of ref Parameter) : ref Response { return httpc->new_connection("tcp!" + IP + "!80").send_request(ref Request(Host, "POST", RESTForm, "1.1", "Maht Flickr", "Content-Type: application/x-www-form-urlencoded" :: nil, array of byte (query_string(a, method, parameters) + "&api_sig=" + sign_parameters(a, method, parameters)))); } read_rsp_node(xtxt: string) : string { if(xtxt == nil) return ""; x := xml->init_io(xtxt, nil, ""); for(n := x.next(); n != nil; n = x.next()) { pick e := n { Tag => case e.name { "rsp" => if(e.attrs.get("stat") == "ok") x.down(); break; * => x.down(); f := x.next(); pick g := f { Text => return g.ch; } } } } return ""; } Auth.get_frob(a : self ref Auth) : string { r := send_request(a, "auth.getFrob", nil); if(r == nil) return ""; return read_rsp_node(r.body_string()); } Auth.get_token(a: self ref Auth) : string { r := send_request(a, "auth.getToken", ref Parameter("frob", a.get_frob()) :: nil); if(r == nil) return ""; return r.body_string(); } Auth.check_token(a: self ref Auth) { r := send_request(a, "auth.checkToken", nil); if(r == nil) raise "auth.checkToken failed"; sys->print("%s\n", r.to_string()); } Photo.to_string(p : self ref Photo) : string { return sys->sprint("Photo id=%s\n\towner: %s\n\tsecret: %s\n\tserver: %s\n\tfarm: %s\n\ttitle: %s\n\tispublic: %d\n\tisfriend: %d\n\tisfamily: %d\n", p.id, p.owner, p.secret, p.server, p.farm, p.title, p.ispublic, p.isfriend, p.isfamily); } response_to_photopage(r : ref Response) : ref Photopage { if(r == nil) return nil; photopage := ref Photopage; p : ref Photo; x := xml->init_io(r.body_string(), nil, ""); for(n := x.next(); n != nil; n = x.next()) { pick e := n { Tag => case e.name { "rsp" => if(e.attrs.get("stat") == "ok") x.down(); break; "photos" => photopage.page = int e.attrs.get("page"); photopage.pages = int e.attrs.get("pages"); photopage.perpage = int e.attrs.get("perpage"); photopage.total = int e.attrs.get("total"); x.down(); "photo" => p = ref Photo; p.id = e.attrs.get("id"); p.owner = e.attrs.get("owner"); p.secret = e.attrs.get("secret"); p.server = e.attrs.get("server"); p.farm = e.attrs.get("farm"); p.title = e.attrs.get("title"); p.ispublic = int e.attrs.get("ispublic"); p.isfriend = int e.attrs.get("isfriend"); p.isfamily = int e.attrs.get("isfamily"); photopage.photos = p :: photopage.photos; } } } return photopage; } get_favourites(a : ref Auth, per_page, page_no : int) : ref Photopage { params := ref Parameter("page", sys->sprint("%d", page_no)) ::ref Parameter("per_page", sys->sprint("%d", per_page)) :: nil; return response_to_photopage(send_request(a, "favorites.getList", params)); } upload_jpeg(a: ref Auth, filename, title, description, tags, public : string) : string { (s, dir) := sys->stat(filename); if(s != 0) raise "stat failed for: " + filename; disp := ref Disposition; disp.data = array[int dir.length] of byte; # bye bye memory fd := sys->open(filename, Sys->OREAD); read := sys->read(fd, disp.data, len disp.data); if(len disp.data != read) raise "short read"; disp.d_type = "form-data"; disp.content_type = "image/jpeg"; disp.attributes = ref mime->Keyval("name", "photo") :: ref mime->Keyval("filename", dir.name) :: nil; d := mime->new_document(); d.add_part(disp); disp = nil; params : list of ref Parameter; if(title != "") { d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "title") :: nil, array of byte title)); params = ref Parameter("title", title) :: params; } if(description != "") { d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "description") :: nil, array of byte description)); params = ref Parameter("description", description) :: params; } if(tags != "") { d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "tags") :: nil, array of byte tags)); params = ref Parameter("tags", tags) :: params; } if(public != "") { d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "is_public") :: nil, array of byte public)); params = ref Parameter("is_public", public) :: params; } sig := sign_parameters(a, "", params); d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "api_sig") :: nil, array of byte sig)); d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "auth_token") :: nil, array of byte a.token)); d.add_part(ref Disposition("form-data", "", ref mime->Keyval("name", "api_key") :: nil, array of byte a.apikey)); bytes := d.bytes(); r := httpc->new_connection("tcp!" + IP + "!80").send_request(ref Request(Host, "POST", UploadForm, "1.1", "Maht Flickr", "Content-type: multipart/form-data; boundary=" + d.boundary :: "Accept-Encoding: identity" :: nil, bytes)); x := read_rsp_node(r.body_string()); if (x == "") raise "upload failed"; return x; } Category.to_string(c : self ref Category) : string { return sys->sprint("C:%s\n\tid: %d\n\tp:%d\n\tc: %d", c.name, c.id, c.parent, c.count); } Group.to_string(g : self ref Group) : string { return sys->sprint("G:%s\n\tnsid: %s\n\tmembers: %d", httpc->urlencode(g.name), g.nsid, g.members); } groups_browse(a: ref Auth, category_id : int) : (list of ref Category, list of ref Group) { params := ref Parameter("cat_id", sys->sprint("%d", category_id)) :: nil; r := send_request(a, "groups.browse", params); if(r == nil) return (nil, nil); categories : list of ref Category; groups : list of ref Group; g := ref Group; c : ref Category; p := xml->init_io(r.body_string(), nil, ""); for(i := p.next(); i != nil; i = p.next()) { pick e := i { Tag => case e.name { "rsp" => if(e.attrs.get("stat") == "ok") p.down(); break; "category" => p.down(); "subcat" => c = ref Category; c.id = int e.attrs.get("id"); c.parent = int category_id; c.name = e.attrs.get("name"); c.count = int e.attrs.get("count"); categories = c :: categories; "group" => g = ref Group; g.nsid =e.attrs.get("nsid"); g.name = e.attrs.get("name"); g.members = int e.attrs.get("members"); groups = g :: groups; } } } return (categories, groups); } my_recently_posted_photos(a: ref Auth, per_page, page_no : int) : ref Photopage { params := ref Parameter("page", sys->sprint("%d", page_no)) ::ref Parameter("per_page", sys->sprint("%d", per_page)) :: ref Parameter("user_id", "me") :: nil;; return response_to_photopage(send_request(a, "photos.search", params)); } Photopage.to_string(photopage: self ref Photopage) : string { page := sys->sprint("Page %d of %d x%d = %d\n", photopage.page, photopage.pages, photopage.perpage, photopage.total); p : ref Photo; for(ps := photopage.photos; ps != nil; ps = tl ps) { p = hd ps; page += "\t" + p.to_string() + "\n"; } return page; } # Put Limbo Flickr