Here's some Python source that implements basic FUSE support for editing Wiki pages. It's horribly incomplete and almost fully untested, but appears to work for my testcases. Feel free to extend. No warranty, explicitly reject any possible claims of copyright, etc. Have fun!
#!/usr/bin/env python # This file was originally xmp.py # Now, only the header survives. import os, sys from errno import * from stat import * from urllib2 import * from urllib import urlencode from cookielib import CookieJar from array import array from BeautifulSoup import BeautifulStoneSoup import fcntl import fuse from fuse import Fuse if not hasattr(fuse, '__version__'): raise RuntimeError, \ "your fuse-py doesn't know of fuse.__version__, probably it's too old." fuse.fuse_python_api = (0, 2) # We use a custom file class fuse.feature_assert('stateful_files', 'has_init') def path2url(path): murl = path[1:].split("/") return "http://"+"/".join(murl[:-1])+"/doku.php?id="+murl[-1][1:]+"&do=edit" def between(text, a, b): apos = text.find(a) if apos == -1: return None text = text[apos + len(a):] bpos = text.find(b) if bpos == -1: return None return text[:bpos] def acquire(url): data = urlopen(url).read() res = array('c') res.fromstring((BeautifulStoneSoup(data, fromEncoding="utf-8", convertEntities = BeautifulStoneSoup.HTML_ENTITIES).find('textarea', id='wiki__text').contents[0][1:]).encode("utf-8")) return res def post(url, posturl, data): cook = CookieJar() op = build_opener(HTTPCookieProcessor(cook)) page = op.open(url).read() bs = BeautifulStoneSoup(page, convertEntities = BeautifulStoneSoup.HTML_ENTITIES) values={} def copy(name): values[name] = bs.find('input', attrs={'name': name})['value'] copy('sectok') copy('id') copy('date') copy('prefix') copy('suffix') copy('changecheck') values['wikitext'] = data values['do[save]'] = 'Save' values['summary'] = '' values['minoredit'] = None return op.open(Request(posturl, urlencode(values))).read() # was Xmp, shamelessly ripped off class DokuFucker(Fuse): def __init__(self, *args, **kw): Fuse.__init__(self, *args, **kw) self.file_class = self.DokuPage self.removed = {} def getattr(self, path): # print "O hai '{0}' has {1}".format(path, path.find("/?")) if (path.find("/?") != -1): t = fuse.Stat() t.st_mode = S_IFREG | 0444 t.st_dev = 0 t.st_blksize = 0 t.st_nlink = 1 # masked for recreation if path in self.removed: t.st_size = 0 del self.removed[path] else: t.st_size = len(acquire(path2url(path))) return t else: t = fuse.Stat() t.st_mode = S_IFDIR | 0755 t.st_dev = 0 t.st_blksize = 0 t.st_nlink = 2 t.st_size = 0 return t def readdir(self, path, offset): if path == "/": yield fuse.Direntry(".") yield fuse.Direntry("..") yield fuse.Direntry("test2") def chmod(self, path, mode): return 0 # No-op def chown(self, path, user, group): return 0 # No-op def truncate(self, path, len): # print "Trying to truncate {0} to {1}".format(path, len) self.removed[path] = True return 0 def unlink(self, path): self.removed[path] = True class DokuPage(object): def __init__(self, path, flags, *mode): # print "Path: " + path murl = path[1:].split("/") self.url = "http://"+"/".join(murl[:-1])+"/doku.php?id="+murl[-1][1:]+"&do=edit" self.posturl = "http://"+"/".join(murl[:-1])+"/doku.php" if path in server.removed: self.data = '' del server.removed[path] else: self.data = acquire(self.url) self.modified = False # print "URL: {0}; length {1}".format(self.url, len(self.data)) def read(self, length, offset): if offset > len(self.data): return "" if offset <= len(self.data) and offset + length > len(self.data): length = len(self.data) - offset # print "Read {0} at {1} from {2}".format(length, offset, len(self.data)) return self.data[offset:offset+length].tostring() def write(self, buf, offset): # if we already have data and try to overwrite it # or if we don't have data but try to start at an offset # bad things happen if (bool(len(self.data)) ^ bool(offset)) or len(self.data) != offset: if not offset: print "It is my hope that this is the editor starting over. " print "Based on that hope I will now erase my internal datastore. " print "If I'm wrong, you get data loss. " print "So you'd better hope I'm right. >_>" self.data = array('c') else: print "This mode cannot be supported safely! {0} into {1} at {2} ".format(len(buf), len(self.data), offset) print "Evidently, my hope that modern editors don't do this, was misplaced. " return -EINVAL self.modified = True self.data.extend(buf); return len(buf) def flush(self): # POST now if self.modified: res = post(self.url, self.posturl, self.data.tostring()) server = DokuFucker(version="%prog " + fuse.__version__) server.parse(values=server, errex=1) server.main()