def link_file(self, link): # cleanup the path isabs = link.startswith('/') isdir = link.endswith('/') link = link.replace('\\', '/') parts = [p for p in link.split('/') if p and not p == '.'] # TODO fold '..' link = '/'.join(parts) if isabs and link != '/': link = '/' + link if isdir and link != '/': link += '/' if link.startswith('/'): # document root return url_encode('/+docs/' + link.lstrip('/')) # TODO use script location as root for cgi-bin # TODO allow alternative document root for cgi-bin else: # attachment or external file try: file = self.notebook.resolve_file(link, self.path) if file.ischild(self.notebook.dir): # attachment relpath = file.relpath(self.notebook.dir) # TODO: need abstract interface for this return url_encode('/+file/' + relpath) else: # external file -> file:// return file.uri except: # typical error is a non-local file:// uri return link
def _joinuri(names): # first element must be either drive letter or UNC host if not re.match(r'^(\w:|\\\\\w)', names[0]): raise ValueError('Not an absolute path: %s' % '\\'.join(names)) elif re.match(r'^\w:$', names[0]): # Drive letter - e.g. file:///C:/foo return 'file:///' + names[0] + '/' + url_encode('/'.join(names[1:])) elif re.match(r'^\\\\\w+$', names[0]): # UNC path - e.g. file://host/share return 'file://' + url_encode(names[0].strip('\\') + '/' + '/'.join(names[1:]))
def uri(self): '''File uri property with win32 logic''' # win32 paths do not start with '/', so add another one # and avoid url encoding the second ":" in "file:///C:/..." path = self.path.replace('\\', '/') if re.match('[A-Za-z]:/', path): return 'file:///' + path[:2] + url_encode(path[2:]) else: return 'file:///' + url_encode(path)
def email_page(self, _callback=open_url): '''Menu action to open an email containing the current page. Encodes the current page as "mailto:" URI and calls L{open_url()} to start the preferred email client. ''' text = ''.join(self.page.dump(format='plain')) url = 'mailto:?subject=%s&body=%s' % ( url_encode(self.page.name, mode=URL_ENCODE_DATA), url_encode(text, mode=URL_ENCODE_DATA), ) _callback(self.widget, url)
def testCreateThumbnail(self): manager = ThumbnailManager(preferences={}) dir = Dir('./data/pixmaps') # Test API and functions #~ for file in dir.list_objects(): # TODO for filename in dir.list(): file = dir.file(filename) # Remove and assert thumbnail does not exist manager.remove_thumbnails(file) for size in (THUMB_SIZE_NORMAL, THUMB_SIZE_LARGE): thumbfile = manager.get_thumbnail_file(file, size) self.assertFalse(thumbfile.exists(), msg="File exists: %s" % thumbfile) # Get thumbnails - twice, first they don't exist, than they do for (size, pixels) in ( (THUMB_SIZE_NORMAL, 128), (THUMB_SIZE_LARGE, 256), (THUMB_SIZE_NORMAL, 128), (THUMB_SIZE_LARGE, 256), ): thumb = manager.get_thumbnail(file, size) self.assertIsInstance(thumb, gtk.gdk.Pixbuf) self.assertEqual(thumb.get_width(), pixels) self.assertEqual(thumb.get_height(), pixels) self.assertTrue(thumb.get_option('tEXt::Thumb::URI').startswith('file:///')) # Specific requirement of spec to use file:/// and not file://localhost/ self.assertEqual(thumb.get_option('tEXt::Thumb::URI'), url_encode(file.uri, URL_ENCODE_READABLE)) self.assertEqual(thumb.get_option('tEXt::Thumb::MTime'), str( int( file.mtime() ) )) thumbfile = manager.get_thumbnail_file(file, size) self.assertTrue(thumbfile.exists(), msg="Missing file: %s" % thumbfile) basename = hashlib.md5(file.uri).hexdigest() + '.png' self.assertEqual(thumbfile.basename, basename)
def pack_urilist(links): text = '' for link in links: if is_url_re.match(link): link = url_encode(link, mode=URL_ENCODE_READABLE) # just to be sure text += '%s\r\n' % link return text.encode()
def link_page(self, link): try: page = self.notebook.resolve_path(link, source=self.path) except PageNameError: return '' else: if page == self.path: return '' parent = page.commonparent(self.path) if parent == self.path: path = './' + self.path.basename + '/' downpath = page.relname(parent) path += downpath elif parent == page: uppath = self.path.relname(parent) path = '../' * (uppath.count(':') + 1) path += page.basename else: uppath = self.path.relname(parent) downpath = page.relname(parent) path = '../' * uppath.count(':') or './' path += downpath path = encode_filename(path) + self._extension #~ print '>>>', path return url_encode(path.replace(' ', '_'))
def link_page(self, link): try: page = self.notebook.resolve_path(link, source=self.path) except PageNameError: return '' else: return url_encode('/' + encode_filename(page.name) + '.html')
def file_object(self, file): '''Turn a L{File} object in a relative link or URI''' if file.ischild(self.notebook.folder): # attachment relpath = file.relpath(self.notebook.folder) return url_encode('/+file/' + relpath) elif self.notebook.document_root \ and file.ischild(self.notebook.document_root): # document root relpath = file.relpath(self.notebook.document_root) return url_encode('/+docs/' + relpath) # TODO use script location as root for cgi-bin # TODO allow alternative document root for cgi-bin else: # external file -> file:// return file.uri
def get_tmpdir(): '''Get a folder in the system temp dir for usage by zim. This zim specific temp folder has permission set to be readable only by the current users, and is touched if it didn't exist yet. Used as base folder by L{TmpFile}. @returns: a L{LocalFolder} object for the zim specific tmp folder ''' # We encode the user name using urlencoding to remove any non-ascii # characters. This is because sockets are not always unicode safe. root = tempfile.gettempdir() name = url_encode(os.environ['USER'], URL_ENCODE_READABLE) dir = LocalFolder(tempfile.gettempdir()).folder('zim-%s' % name) try: dir.touch(mode=0o700) # Limit to single user os.chmod(dir.path, 0o700) # Limit to single user when dir already existed # Raises OSError if not allowed to chmod os.listdir(dir.path) # Raises OSError if we do not have access anymore except OSError: raise AssertionError( 'Either you are not the owner of "%s" or the permissions are un-safe.\n' 'If you can not resolve this, try setting $TMP to a different location.' % dir.path) else: # All OK, so we must be owner of a safe folder now ... return dir
def runTest(self): manager = ThumbnailManager(preferences={}) dir = Dir('./data/pixmaps') # Test API and functions #~ for file in dir.list_objects(): # TODO for filename in dir.list(): file = dir.file(filename) # Remove and assert thumbnail does not exist manager.remove_thumbnails(file) for size in (THUMB_SIZE_NORMAL, THUMB_SIZE_LARGE): thumbfile = manager.get_thumbnail_file(file, size) self.assertFalse(thumbfile.exists(), msg="File exists: %s" % thumbfile) # Get thumbnails - twice, first they don't exist, than they do for (size, pixels) in ( (THUMB_SIZE_NORMAL, 128), (THUMB_SIZE_LARGE, 256), (THUMB_SIZE_NORMAL, 128), (THUMB_SIZE_LARGE, 256), ): thumb = manager.get_thumbnail(file, size) self.assertIsInstance(thumb, gtk.gdk.Pixbuf) self.assertEqual(thumb.get_width(), pixels) self.assertEqual(thumb.get_height(), pixels) self.assertTrue(thumb.get_option('tEXt::Thumb::URI').startswith('file:///')) # Specific requirement of spec to use file:/// and not file://localhost/ self.assertEqual(thumb.get_option('tEXt::Thumb::URI'), url_encode(file.uri, URL_ENCODE_READABLE)) self.assertEqual(thumb.get_option('tEXt::Thumb::MTime'), str( int( file.mtime() ) )) thumbfile = manager.get_thumbnail_file(file, size) self.assertTrue(thumbfile.exists(), msg="Missing file: %s" % thumbfile) basename = hashlib.md5(file.uri).hexdigest() + '.png' self.assertEqual(thumbfile.basename, basename)
def interwiki_link(link): '''Convert an interwiki link into an url''' assert isinstance(link, basestring) and '?' in link key, page = link.split('?', 1) url = None for line in config_file('urls.list'): if line.startswith(key+' ') or line.startswith(key+'\t'): url = line[len(key):].strip() break else: list = get_notebook_list() for name, path in list.get_names(): if name.lower() == key.lower(): url = path + '?{NAME}' break if url and is_url_re.match(url): if not ('{NAME}' in url or '{URL}' in url): url += '{URL}' url = url.replace('{NAME}', page) url = url.replace('{URL}', url_encode(page)) return url else: return None
def create_thumbnail(self, file, thumbfile, size): options = { 'tEXt::Thumb::URI': url_encode(file.uri, mode=URL_ENCODE_READABLE), # No UTF-8 here 'tEXt::Thumb::MTime': str( int( file.mtime() ) ), } pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file.path, size, size) pixbuf.save(thumbfile.path, 'png', options)
def file_object(self, file): '''Turn a L{File} object in a relative link or URI''' if file.ischild(self.notebook.dir): # attachment relpath = file.relpath(self.notebook.dir) return url_encode('/+file/' + relpath) elif self.notebook.document_root \ and file.ischild(self.notebook.document_root): # document root relpath = file.relpath(self.notebook.document_root) return url_encode('/+docs/' + relpath) # TODO use script location as root for cgi-bin # TODO allow alternative document root for cgi-bin else: # external file -> file:// return file.uri
def dump_link(self, tag, attrib, strings=None): href = self.linker.link(attrib['href']) href = url_encode(href, URL_ENCODE_READABLE) if strings: text = u''.join(strings) else: text = href return ['\\href{%s}{%s}' % (href, text)]
def encode_urls(self): """Calls encode_url() on all links that contain urls""" for link in self.getiterator("link"): href = link.attrib["href"] if is_url_re.match(href): link.attrib["href"] = url_encode(href) if link.text == href: link.text = link.attrib["href"]
def pack_urilist(links): text = '' for link in links: link = link.encode('utf-8') if is_url_re.match(link): link = url_encode(link, mode=URL_ENCODE_READABLE) # just to be sure text += '%s\r\n' % link return text
def create_thumbnail(self, file, thumbfile, size): options = { 'tEXt::Thumb::URI': url_encode(file.uri, mode=URL_ENCODE_READABLE), # No UTF-8 here 'tEXt::Thumb::MTime': str(int(file.mtime())), } pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file.path, size, size) pixbuf.save(thumbfile.path, 'png', options)
def dump_link(self, tag, attrib, strings=None): href = self.linker.link(attrib['href']) href = url_encode(href, URL_ENCODE_READABLE) if strings: text = ''.join(strings) else: text = href return ['\\href{%s}{%s}' % (href, text)]
def encode_urls(self, mode=URL_ENCODE_READABLE): '''Calls encode_url() on all links that contain urls. See zim.parsing for details. Modifies the parse tree. ''' for link in self._etree.getiterator('link'): href = link.attrib['href'] if href and is_url_re.match(href): link.attrib['href'] = url_encode(href, mode=mode) if link.text == href: link.text = link.attrib['href']
def encode_urls(self, mode=URL_ENCODE_READABLE): '''Calls encode_url() on all links that contain urls. See zim.parsing for details. Modifies the parse tree. ''' for link in self._etree.getiterator('link'): href = link.attrib['href'] if is_url_re.match(href): link.attrib['href'] = url_encode(href, mode=mode) if link.text == href: link.text = link.attrib['href']
def normalize_win32_share(path): if os.name == 'nt': if path.startswith('smb://'): # smb://host/share/.. -> \\host\share\.. path = path[4:].replace('/', '\\') path = url_decode(path) else: if path.startswith('\\\\'): # \\host\share\.. -> smb://host/share/.. path = 'smb:' + path.replace('\\', '/') path = url_encode(path) return path
def _link_notebook(self, link): if link.startswith('zim+'): link = link[4:] if '?' in link: link, path = link.split('?') # FIXME: code below is not robust because we don't know the # storage mode of linked notebook... path = url_decode(path) # was already encoded by interwiki_link() path = encode_filename(path).replace(' ', '_') return link + '/' + url_encode(path) + '.txt' else: return link
def dump_link(self, tag, attrib, strings=None): # TODO: how do you do page links within an exported document # use \label{} per page start and \ref to link it ?? href = self.linker.link(attrib['href']) if href.startswith('#'): return ['\\ref{%s}' % href.lstrip('#')] else: href = url_encode(href, URL_ENCODE_READABLE) if strings: text = ''.join(strings) else: text = href return ['\\href{%s}{%s}' % (href, text)]
def link_notebook(self, url): if url.startswith('zim+'): url = url[4:] if '?' in url: uri, path = url.split('?') # FIXME: code below is not robust because we don't know the # storage mode of linked notebook... path = url_decode(path) # was already encoded by interwiki_link() path = encode_filename(path).replace(' ', '_') return uri + '/' + url_encode(path) + '.txt' else: return url
def dump_img(self, tag, attrib, strings=None): src = attrib['src'] or '' alt = attrib.get('alt') opts = [] items = sorted(attrib.items()) for k, v in items: if k in ('src', 'alt') or k.startswith('_'): continue elif v: # skip None, "" and 0 data = url_encode(str(v), mode=URL_ENCODE_DATA) opts.append('%s=%s' % (k, data)) if opts: src += '?%s' % '&'.join(opts) if alt: return ('{{', src, '|', alt, '}}') else: return ('{{', src, '}}')
def dump_img(self, tag, attrib, strings=None): src = attrib['src'] alt = attrib.get('alt') opts = [] items = attrib.items() items.sort() # unit tests don't like random output for k, v in items: if k in ('src', 'alt') or k.startswith('_'): continue elif v: # skip None, "" and 0 data = url_encode(unicode(v), mode=URL_ENCODE_DATA) opts.append('%s=%s' % (k, data)) if opts: src += '?%s' % '&'.join(opts) if alt: return ('{{', src, '|', alt, '}}') else: return('{{', src, '}}')
def interwiki_link(link): '''Convert an interwiki link into an url''' assert isinstance(link, str) and '?' in link key, page = link.split('?', 1) lkey = key.lower() # First check known notebooks list = get_notebook_list() info = list.get_interwiki(key) if info: url = 'zim+' + info.uri + '?{NAME}' # Then search all "urls.list" in config and data dirs else: url = None files = XDGConfigFileIter( 'urls.list') # FIXME, shouldn't this be passed in ? for file in files: for line in file.readlines(): if line.startswith('#') or line.isspace(): continue try: mykey, myurl = line.split(None, 1) except ValueError: continue if mykey.lower() == lkey: url = myurl.strip() break if url is not None: break # Format URL if url: if not ('{NAME}' in url or '{URL}' in url): url += '{URL}' url = url.replace('{NAME}', page) url = url.replace('{URL}', url_encode(page)) return url else: return None
def normalize_win32_share(path): '''Translates paths for windows shares in the platform specific form. So on windows it translates C{smb://} URLs to C{\\host\share} form, and vice versa on all other platforms. Just returns the original path if it was already in the right form, or when it is not a path for a share drive. @param path: a filesystem path or URL @returns: the platform specific path or the original input path ''' if os.name == 'nt': if path.startswith('smb://'): # smb://host/share/.. -> \\host\share\.. path = path[4:].replace('/', '\\') path = url_decode(path) else: if path.startswith('\\\\'): # \\host\share\.. -> smb://host/share/.. path = 'smb:' + url_encode(path.replace('\\', '/')) return path
def page_object(self, path): '''Turn a L{Path} object in a relative link or URI''' return url_encode('/' + encode_filename(path.name) + '.html')
def resource(self, path): return url_encode('/+resources/%s' % path)
def icon(self, name): return url_encode('/+resources/%s.png' % name)
def _filepath(self, file, ref): relpath = file.relpath(ref, allowupward=True) if relpath and not relpath.startswith('.'): relpath = './' + relpath return url_encode(relpath) or file.uri
def _joinuri(names): if names[0][0] == '/': return 'file://' + url_encode('/'.join(names)) else: return 'file:///' + url_encode('/'.join(names))
def set_in_main_process(in_main_process): '''Set whether we are in the main process or not''' global _IN_MAIN_PROCESS _IN_MAIN_PROCESS = in_main_process _m = hashlib.md5() _m.update(zim.ZIM_EXECUTABLE.encode('UTF-8')) key = zim.__version__ + '-' + _m.hexdigest()[:8] # Make name specific for the install location # But don't worry about collisons, first few bytes should do it if sys.platform == 'win32': # Windows named pipe userstring = url_encode(os.environ['USER'], URL_ENCODE_READABLE) SERVER_ADDRESS = '\\\\.\\pipe\\zim-%s-%s' % (userstring, key) SERVER_ADDRESS_FAMILY = 'AF_PIPE' from multiprocessing.connection import PipeListener Listener = PipeListener else: # Unix domain socket runtime_dir = os.environ.get('XDG_RUNTIME_DIR') if runtime_dir is None: # Use tmpdir as fallback import tempfile tmpdir = tempfile.gettempdir() userstring = url_encode(os.environ['USER'], URL_ENCODE_READABLE) runtime_dir = tmpdir + '/zim-' + userstring logger.warn('XDG_RUNTIME_DIR is not set, using %s as a fallback' % runtime_dir)
def _dump_children(self, list, output, istoplevel=False): for element in list.getchildren(): text = html_encode(element.text) if not element.tag == 'pre': # text that goes into the element # always encode excepts for <pre></pre> text = encode_whitespace(text) if element.tag == 'h': tag = 'h' + str(element.attrib['level']) if self.isrtl(element): output += ['<', tag, ' dir=\'rtl\'>', text, '</', tag, '>'] else: output += ['<', tag, '>', text, '</', tag, '>'] elif element.tag == 'p': tag = 'p' if self.isrtl(element): tag += ' dir=\'rtl\'' if 'indent' in element.attrib: level = int(element.attrib['indent']) tag += ' style=\'padding-left: %ipt\'' % (30 * level) output += ['<', tag, '>\n', text] self._dump_children(element, output) # recurs output.append('</p>\n') elif element.tag == 'pre': if self.isrtl(element): output += ['<pre dir=\'rtl\'>\n', text, '</pre>\n'] else: output += ['<pre>\n', text, '</pre>\n'] elif element.tag is 'ul': output += ['<ul>\n', text] self._dump_children(element, output) # recurs output.append('</ul>\n') elif element.tag == 'li': if 'bullet' in element.attrib and element.attrib['bullet'] != '*': icon = url_encode(self.linker.icon(element.attrib['bullet'])) output += ['<li style="list-style-image: url(%s)">' % icon, text] else: output += ['<li>', text] self._dump_children(element, output) # recurs output.append('</li>\n') elif element.tag == 'img': src = url_encode(self.linker.img(element.attrib['src'])) opt = '' for o in ('width', 'height'): if o in element.attrib and int(element.attrib[o]) > 0: opt = ' %s="%s"' % (o, element.attrib[o]) output.append('<img src="%s" alt="%s"%s>' % (src, text, opt)) elif element.tag == 'link': href = url_encode(self.linker.link(element.attrib['href'])) title = text.replace('"', '"') output.append('<a href="%s" title="%s">%s</a>' % (href, title, text)) elif element.tag in ['emphasis', 'strong', 'mark', 'strike', 'code']: if element.tag == 'mark': tag = 'u' elif element.tag == 'emphasis': tag = 'em' else: tag = element.tag output += ['<', tag, '>', text, '</', tag, '>'] else: assert False, 'Unknown node type: %s' % element if not element.tail is None: tail = html_encode(element.tail) if not (istoplevel and tail.isspace()): # text in between elements, skip encoding # for whitespace between headings, paras etc. tail = encode_whitespace(tail) output.append(tail)
def uri(self): '''File uri property''' return 'file://' + url_encode(self.path)