# metaweblogfs, mount a metaweblog blog using Fuse # Diego Zamboni, March 31st, 2007 # http://www.zzamboni.org/brt/2007/03/31/metaweblogfs-ruby-fuse-xml-rpc-oh-my/ require 'xmlrpc/client' require 'fusefs' include FuseFS #### Default values ###### # Blog parameters. Edit to fit your own defhost='localhost' defpath='/xmlrpc.php' defuser='admin' defblogid=1 defpass=nil # When updating or creating files, set status to "published"? Defpublish=false ########################## class Blog def initialize(host, path, username, password, blog_id = 1, port = 80) @server = XMLRPC::Client.new(host, path, port) @blog_id = blog_id @username = username @password = password end def posts(count = 5) @server.call('metaWeblog.getRecentPosts', @blog_id, @username, @password, count) end def <<(content, retries = 5) while (retries > 0) begin puts "Posting new entry #{content.inspect}" return @server.call('metaWeblog.newPost', @blog_id, @username, @password, content, Defpublish) rescue Timeout::Error puts "Retrying #{content['title']}, retry #{retries}" retries =- 1 end end end def new(title, content, retries = 5) post={ 'title' => title, 'description' => content } self << post end def update(post, newcontent, retries = 5) post['description']=newcontent while (retries > 0) begin puts "Posting update #{post.inspect}" return @server.call('metaWeblog.editPost', post['postid'], @username, @password, post, Defpublish) rescue Timeout::Error puts "Retrying #{post['title']}, retry #{retries}" retries =- 1 end end end def publish(post, retries = 5) while (retries > 0) begin puts "Publishing post #{post.inspect}" return @server.call('metaWeblog.editPost', post['postid'], @username, @password, post, true) rescue Timeout::Error puts "Retrying #{post['title']}, retry #{retries}" retries =- 1 end end end end class BlogDir < FuseFS::FuseDir def initialize(host, path, username, password, blog_id = 1, port = 80) @host=host @path=path @username=username @password=password @blog_id=blog_id @port=port self.reload end def reload @blog = Blog.new(@host, @path, @username, @password, @blog_id, @port) puts "Reading posts index..." @posts = @blog.posts(500) puts "Loaded #{@posts.length} blog entries" end # Find the first item with a certain title. We don't handle duplicate titles # properly yet def item_for(path) (base, rest) = split_path(path) (@posts.find_all {|post| post['title'] == base })[0] end def file?(path) if self.directory?(path) return false end (self.item_for(path) != nil) end def directory?(path) (base, rest) = split_path(path) # The only directory is the top one, for which both base and rest are nil !(base || rest) end def read_file(path) self.item_for(path)['description'] end def contents(path) @posts.collect { |post| post['title'] } end def size(path) self.item_for(path)['description'].length end def can_write?(path) true end # We return true to fool vi, which wants to delete and recreate the file def can_delete?(path) true end def write_to(path, str) # puts("write_to(#{path}, #{str})...") # Skip temporary and empty files return if path =~ /\~$/ return if path =~ /^.\#/ return if str.empty? obj=self.item_for(path) if obj if obj['description'] != str @blog.update(obj, str) end else (base, rest) = split_path(path) @blog.new(base, str) end self.reload end # Touch the file to publish it, or the directory to reload def touch(path) if path == "/reload" self.reload else obj=self.item_for(path) if obj @blog.publish(obj) self.reload end end end end def usage puts "Usage: #{$0} mountdirectory password host path user [blogid]" exit end dir = ARGV.shift unless dir and File.directory?(dir) usage end pw = ARGV.shift || defpass host = ARGV.shift || defhost path = ARGV.shift || defpath user = ARGV.shift || defuser blogid = ARGV.shift || defblogid if !(host && path && user && pw && blogid) usage end puts "Connecting to #{host} #{path} #{blogid} #{user} #{pw}" blogdir = BlogDir.new(host, path, user, pw, blogid) FuseFS.set_root(blogdir) FuseFS.mount_under(dir) puts "Blog entries mounted under #{dir} - press Ctrl-C to finish" FuseFS.run