diff --git a/modules/lutim/__init__.py b/modules/lutim/__init__.py
new file mode 100644
index 00000000..83a0f1b9
--- /dev/null
+++ b/modules/lutim/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2014 Vincent A
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from .backend import LutimBackend
+
+
+__all__ = ['LutimBackend']
diff --git a/modules/lutim/backend.py b/modules/lutim/backend.py
new file mode 100644
index 00000000..6beb07e6
--- /dev/null
+++ b/modules/lutim/backend.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2014 Vincent A
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.backend import BaseBackend, BackendConfig
+from weboob.capabilities.paste import ICapPaste, BasePaste
+from weboob.tools.capabilities.paste import image_mime
+from weboob.tools.value import Value
+import re
+from urlparse import urljoin
+
+from .browser import LutimBrowser
+
+
+__all__ = ['LutimBackend']
+
+
+class LutimBackend(BaseBackend, ICapPaste):
+ NAME = 'lutim'
+ DESCRIPTION = u'LUTIm website'
+ MAINTAINER = u'Vincent A'
+ EMAIL = 'dev@indigo.re'
+ LICENSE = 'AGPLv3+'
+ VERSION = '0.i'
+
+ BROWSER = LutimBrowser
+
+ CONFIG = BackendConfig(Value('base_url', label='Hoster base URL', default='http://lut.im/'))
+
+ def _base_url(self):
+ url = self.config['base_url'].get()
+ if not url.endswith('/'):
+ url = url + '/'
+ return url
+
+ def create_default_browser(self):
+ return self.create_browser(self._base_url())
+
+ def can_post(self, contents, title=None, public=None, max_age=None):
+ if re.search(r'[^a-zA-Z0-9=+/\s]', contents):
+ return 0
+ elif max_age and max_age < 86400:
+ return 0 # it cannot be shorter than one day
+ else:
+ mime = image_mime(contents, ('gif', 'jpeg', 'png'))
+ return 20 * int(mime is not None)
+
+ def new_paste(self, *a, **kw):
+ base_url = self._base_url()
+
+ class LutImage(BasePaste):
+ @classmethod
+ def id2url(cls, id):
+ return urljoin(base_url, id)
+
+ @classmethod
+ def url2id(cls, url):
+ if url.startswith(base_url):
+ return url[len(base_url):]
+
+ return LutImage(*a, **kw)
+
+ def get_paste(self, id):
+ paste = self.new_paste(id)
+
+ if '/' in id:
+ paste.id = paste.url2id(id)
+ if not paste.id:
+ return None
+
+ response = self.browser.readurl(paste.page_url)
+ if response:
+ paste.contents = response.encode('base64')
+ return paste
+
+ def post_paste(self, paste, max_age=None):
+ d = self.browser.post(paste.title or None, paste.contents.decode('base64'), (max_age or 0) // 86400)
+ if d:
+ paste.id = d['id']
diff --git a/modules/lutim/browser.py b/modules/lutim/browser.py
new file mode 100644
index 00000000..df04bf33
--- /dev/null
+++ b/modules/lutim/browser.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2014 Vincent A
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.browser import BaseBrowser
+from StringIO import StringIO
+import re
+
+from .pages import PageAll
+
+
+__all__ = ['LutimBrowser']
+
+
+class LutimBrowser(BaseBrowser):
+ ENCODING = 'utf-8'
+
+ def __init__(self, base_url, *args, **kw):
+ BaseBrowser.__init__(self, *args, **kw)
+ self.base_url = base_url
+ self.PAGES = {re.escape(self.base_url): PageAll}
+
+ def post(self, name, content, max_days):
+ self.location(self.base_url)
+ assert self.is_on_page(PageAll)
+ self.select_form(nr=0)
+ self.form['delete-day'] = [str(max_days)]
+ self.form.find_control('file').add_file(StringIO(content), filename=name)
+ self.submit()
+
+ assert self.is_on_page(PageAll)
+ return self.page.get_info()
diff --git a/modules/lutim/favicon.png b/modules/lutim/favicon.png
new file mode 100644
index 00000000..ce9e1d36
Binary files /dev/null and b/modules/lutim/favicon.png differ
diff --git a/modules/lutim/pages.py b/modules/lutim/pages.py
new file mode 100644
index 00000000..66938ce3
--- /dev/null
+++ b/modules/lutim/pages.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2014 Vincent A
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.browser import BasePage
+import re
+
+__all__ = ['PageAll']
+
+
+class PageAll(BasePage):
+ def post(self, name, content, max_days):
+ pass
+
+ def get_info(self):
+ for link in self.browser.links():
+ linkurl = link.absolute_url
+ m = re.match(re.escape(self.url) + '([a-zA-Z0-9]+)$', linkurl)
+ if m:
+ return {'id': m.group(1)}
diff --git a/modules/lutim/test.py b/modules/lutim/test.py
new file mode 100644
index 00000000..eb224e9c
--- /dev/null
+++ b/modules/lutim/test.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2014 Vincent A
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.test import BackendTest
+
+
+class LutimTest(BackendTest):
+ BACKEND = 'lutim'
+
+ # small gif file
+ DATA = 'R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==\n'
+
+ def test_lutim(self):
+ assert self.backend.can_post(self.DATA, max_age=86400)
+
+ post = self.backend.new_paste(None)
+ post.contents = self.DATA
+ post.public = True
+ self.backend.post_paste(post, max_age=86400)
+ assert post.id
+
+ got = self.backend.get_paste(post.id)
+ assert got
+ assert got.contents.decode('base64') == self.DATA.decode('base64')