mirror of https://github.com/sloumdrone/burrow
Added support for binary downloads
This commit is contained in:
parent
49571178db
commit
e11bd4d636
|
@ -2,3 +2,4 @@ __pycache__/*
|
||||||
__pycache__
|
__pycache__
|
||||||
*.swp
|
*.swp
|
||||||
*.json
|
*.json
|
||||||
|
*.pyc
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################
|
################################################
|
||||||
## Burrow, a gopher client/browser
|
## Burrow, a gopher client/browser
|
||||||
## - - - - - - - - - - - - - - - - - - - - - - -
|
## - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
## Version 0.2.1
|
## Version 0.8.4
|
||||||
################################################
|
################################################
|
||||||
|
|
||||||
from gui import GUI
|
from gui import GUI
|
||||||
|
|
16
connect.py
16
connect.py
|
@ -14,10 +14,13 @@ class connect:
|
||||||
socket_conn.connect((host, port))
|
socket_conn.connect((host, port))
|
||||||
socket_conn.sendall((resource + '\r\n').encode('utf-8'))
|
socket_conn.sendall((resource + '\r\n').encode('utf-8'))
|
||||||
|
|
||||||
if itemtype in ['I','p','g']:
|
if itemtype in ['i','0','1','3']:
|
||||||
response = socket_conn.makefile(mode = 'rb', errors = 'ignore')
|
|
||||||
else:
|
|
||||||
response = socket_conn.makefile(mode = 'r', errors = 'ignore')
|
response = socket_conn.makefile(mode = 'r', errors = 'ignore')
|
||||||
|
else:
|
||||||
|
response = socket_conn.makefile(mode = 'rb', errors = 'ignore')
|
||||||
|
|
||||||
|
self.raw_response = response.read()
|
||||||
|
self.filetype = itemtype
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
print('Socket timeout')
|
print('Socket timeout')
|
||||||
socket_conn.close()
|
socket_conn.close()
|
||||||
|
@ -27,13 +30,6 @@ class connect:
|
||||||
socket_conn.close()
|
socket_conn.close()
|
||||||
return {'type': '1', 'body': '3ERROR: Unable to communicate with remote server\tfalse\tnull.host\t1'}
|
return {'type': '1', 'body': '3ERROR: Unable to communicate with remote server\tfalse\tnull.host\t1'}
|
||||||
|
|
||||||
try:
|
|
||||||
self.raw_response = response.read()
|
|
||||||
self.filetype = itemtype
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
self.raw_response = '3ERROR: Unable to decode server response\tfalse\tnull.host\t1'
|
|
||||||
self.filetype = '3'
|
|
||||||
|
|
||||||
socket_conn.close()
|
socket_conn.close()
|
||||||
|
|
||||||
return {'type': self.filetype, 'body': self.raw_response}
|
return {'type': self.filetype, 'body': self.raw_response}
|
||||||
|
|
99
gui.py
99
gui.py
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
import tkinter.simpledialog as dialog
|
import tkinter.simpledialog as dialog
|
||||||
|
from tkinter.filedialog import asksaveasfilename as savedialog
|
||||||
from connect import connect as conn
|
from connect import connect as conn
|
||||||
from parser import parser
|
from parser import parser
|
||||||
import time
|
import time
|
||||||
|
@ -17,7 +18,6 @@ class GUI:
|
||||||
self.history = []
|
self.history = []
|
||||||
self.history_location = -1
|
self.history_location = -1
|
||||||
self.message_bar_content = ''
|
self.message_bar_content = ''
|
||||||
# self.config = None
|
|
||||||
self.read_config()
|
self.read_config()
|
||||||
self.conn = conn()
|
self.conn = conn()
|
||||||
self.parser = parser()
|
self.parser = parser()
|
||||||
|
@ -93,8 +93,10 @@ class GUI:
|
||||||
#load the home screen
|
#load the home screen
|
||||||
self.load_home_screen(1)
|
self.load_home_screen(1)
|
||||||
|
|
||||||
|
|
||||||
#-----------Start GUI configuration-----------------------
|
#-----------Start GUI configuration-----------------------
|
||||||
|
|
||||||
|
|
||||||
def add_event_listeners(self):
|
def add_event_listeners(self):
|
||||||
buttons = [
|
buttons = [
|
||||||
self.btn_back,
|
self.btn_back,
|
||||||
|
@ -121,7 +123,6 @@ class GUI:
|
||||||
self.site_display.tag_bind('generic_r_click', "<Button-3>", (lambda event, href=None: self.show_context_menu(event, href)))
|
self.site_display.tag_bind('generic_r_click', "<Button-3>", (lambda event, href=None: self.show_context_menu(event, href)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def pack_geometry(self):
|
def pack_geometry(self):
|
||||||
self.top_bar.pack(expand=False,fill=tk.BOTH,side=tk.TOP,anchor=tk.NW)
|
self.top_bar.pack(expand=False,fill=tk.BOTH,side=tk.TOP,anchor=tk.NW)
|
||||||
self.top_bar.pack_propagate(False)
|
self.top_bar.pack_propagate(False)
|
||||||
|
@ -157,32 +158,44 @@ class GUI:
|
||||||
|
|
||||||
|
|
||||||
def show_context_menu(self, e, href=None):
|
def show_context_menu(self, e, href=None):
|
||||||
|
current_page = self.entry_url.get()
|
||||||
self.context_menu.delete(0,20)
|
self.context_menu.delete(0,20)
|
||||||
#add navigation options
|
#add navigation options
|
||||||
if len(self.history) > 1 or self.history_location > 0:
|
if len(self.history) > 1 and self.history_location > 0:
|
||||||
back = (lambda event=e: self.go_back(event))
|
back = (lambda event=e: self.go_back(event))
|
||||||
self.context_menu.add_command(label="Back", command=back)
|
self.context_menu.add_command(label=" Back ", command=back)
|
||||||
if len(self.history) > 1 or self.history_location < len(self.history) - 2:
|
else:
|
||||||
|
self.context_menu.add_command(label=" Back ", state=tk.DISABLED)
|
||||||
|
if len(self.history) > 1 and self.history_location < len(self.history) - 1:
|
||||||
forward = (lambda event=e: self.go_forward(event))
|
forward = (lambda event=e: self.go_forward(event))
|
||||||
self.context_menu.add_command(label="Forward", command=forward)
|
self.context_menu.add_command(label=" Forward ", command=forward)
|
||||||
if self.entry_url.get() != 'home':
|
else:
|
||||||
|
self.context_menu.add_command(label=" Forward ", state=tk.DISABLED)
|
||||||
|
refresh = (lambda event=e, link=current_page: self.handle_request(event, link, False))
|
||||||
|
self.context_menu.add_command(label=" Refresh ", command=refresh)
|
||||||
|
if current_page != 'home':
|
||||||
home = (lambda event=e: self.load_home_screen(event))
|
home = (lambda event=e: self.load_home_screen(event))
|
||||||
self.context_menu.add_command(label="Home", command=home)
|
self.context_menu.add_command(label=" Home ", command=home)
|
||||||
|
else:
|
||||||
|
self.context_menu.add_command(label=" Home ", state=tk.DISABLED)
|
||||||
|
save_as_file = (lambda data=self.site_display.get(1.0,tk.END), url=current_page: self.write_to_file(data, url))
|
||||||
|
self.context_menu.add_command(label=" Save as... ", command=save_as_file)
|
||||||
|
|
||||||
if href:
|
if href:
|
||||||
copy_link = (lambda link=href: self.copy_to_clipboard(link))
|
|
||||||
self.context_menu.add_command(label="Copy URL to clipboard", command=copy_link)
|
|
||||||
|
|
||||||
self.context_menu.add_separator()
|
self.context_menu.add_separator()
|
||||||
|
copy_link = (lambda link=href: self.copy_to_clipboard(link))
|
||||||
|
self.context_menu.add_command(label=" Copy URL to clipboard ", command=copy_link)
|
||||||
|
|
||||||
if self.is_favorite(href):
|
if self.is_favorite(href):
|
||||||
|
self.context_menu.add_separator()
|
||||||
delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link))
|
delete_favorite = (lambda event=e, link=href: self.remove_favorite(event, link))
|
||||||
self.context_menu.add_command(label="Delete from favorites", command=delete_favorite)
|
self.context_menu.add_command(label=" Delete from favorites ", command=delete_favorite)
|
||||||
rename_favorite = (lambda event=e, link=href: self.rename_favorite(event, link))
|
rename_favorite = (lambda event=e, link=href: self.rename_favorite(event, link))
|
||||||
self.context_menu.add_command(label="Rename this favorite", command=rename_favorite)
|
self.context_menu.add_command(label=" Rename this favorite ", command=rename_favorite)
|
||||||
elif href:
|
elif href:
|
||||||
|
self.context_menu.add_separator()
|
||||||
add_favorite = (lambda event=e, link=href: self.add_to_favorites(event, link))
|
add_favorite = (lambda event=e, link=href: self.add_to_favorites(event, link))
|
||||||
self.context_menu.add_command(label="Add to favorites", command=add_favorite)
|
self.context_menu.add_command(label=" Add to favorites ", command=add_favorite)
|
||||||
self.context_menu.tk_popup(e.x_root, e.y_root)
|
self.context_menu.tk_popup(e.x_root, e.y_root)
|
||||||
|
|
||||||
|
|
||||||
|
@ -193,6 +206,7 @@ class GUI:
|
||||||
|
|
||||||
# ------------Start navigation methods----------------------------
|
# ------------Start navigation methods----------------------------
|
||||||
|
|
||||||
|
|
||||||
def handle_request(self,event=False, url=False, history=True):
|
def handle_request(self,event=False, url=False, history=True):
|
||||||
self.loading_bar = tk.Label(self.entry_url, text=' Loading... ', width=12, relief=tk.FLAT, height=1, fg='#FFFFFF', bg=self.TYPES)
|
self.loading_bar = tk.Label(self.entry_url, text=' Loading... ', width=12, relief=tk.FLAT, height=1, fg='#FFFFFF', bg=self.TYPES)
|
||||||
self.loading_bar.pack(side=tk.RIGHT, padx=(0,10))
|
self.loading_bar.pack(side=tk.RIGHT, padx=(0,10))
|
||||||
|
@ -226,7 +240,6 @@ class GUI:
|
||||||
self.send_to_screen(data['body'],data['type'])
|
self.send_to_screen(data['body'],data['type'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parse_url(self, url=False):
|
def parse_url(self, url=False):
|
||||||
parsed_url = self.parser.parse_url(url)
|
parsed_url = self.parser.parse_url(url)
|
||||||
|
|
||||||
|
@ -295,14 +308,11 @@ class GUI:
|
||||||
def go_forward(self, event):
|
def go_forward(self, event):
|
||||||
if len(self.history) <= 1 or self.history_location >= len(self.history) - 1:
|
if len(self.history) <= 1 or self.history_location >= len(self.history) - 1:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.history_location += 1
|
self.history_location += 1
|
||||||
href = self.history[self.history_location]
|
href = self.history[self.history_location]
|
||||||
self.handle_request(False, href, False)
|
self.handle_request(False, href, False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#-------------Start view methods----------------
|
#-------------Start view methods----------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -403,6 +413,7 @@ class GUI:
|
||||||
styletag = 'favoritecolor'
|
styletag = 'favoritecolor'
|
||||||
else:
|
else:
|
||||||
styletag = 'linkcolor'
|
styletag = 'linkcolor'
|
||||||
|
|
||||||
hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name))
|
hover = (lambda event, href=link, tag_name=tag_name: self.hoverlink(event, href, tag_name))
|
||||||
clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name))
|
clear = (lambda event, tag_name=tag_name: self.clear_status(event, tag_name))
|
||||||
self.site_display.tag_bind(tag_name, "<Enter>", hover)
|
self.site_display.tag_bind(tag_name, "<Enter>", hover)
|
||||||
|
@ -420,18 +431,47 @@ class GUI:
|
||||||
data = data[:-2]
|
data = data[:-2]
|
||||||
self.site_display.config(state=tk.NORMAL)
|
self.site_display.config(state=tk.NORMAL)
|
||||||
self.site_display.delete(1.0, tk.END)
|
self.site_display.delete(1.0, tk.END)
|
||||||
self.site_display.insert(tk.END, data)
|
self.site_display.insert(tk.END, data, 'generic_r_click')
|
||||||
self.site_display.config(state=tk.DISABLED)
|
self.site_display.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
def show_image(self, data):
|
def show_image(self, data):
|
||||||
self.current_image = self.build_image(data)
|
self.current_image = self.build_image(data)
|
||||||
|
callback = (lambda event, image=data, write='wb': self.write_to_file(contents=image, event=event, write=write))
|
||||||
|
hover = (lambda event, href='Download this image...', tag_name='image_download': self.hoverlink(event, href, tag_name))
|
||||||
|
clear = (lambda event, tag_name='image_download': self.clear_status(event, tag_name))
|
||||||
|
self.site_display.tag_bind('image_download', "<Button-1>", callback)
|
||||||
|
self.site_display.tag_bind('image_download', "<Enter>", hover)
|
||||||
|
self.site_display.tag_bind('image_download', '<Leave>', clear)
|
||||||
self.site_display.config(state=tk.NORMAL)
|
self.site_display.config(state=tk.NORMAL)
|
||||||
self.site_display.delete(1.0, tk.END)
|
self.site_display.delete(1.0, tk.END)
|
||||||
|
self.site_display.insert(tk.END,'Download this image',('linkcolor','image_download'))
|
||||||
|
self.site_display.insert(tk.END, '\n\n')
|
||||||
self.site_display.image_create(tk.END, image = self.current_image)
|
self.site_display.image_create(tk.END, image = self.current_image)
|
||||||
self.site_display.config(state=tk.DISABLED)
|
self.site_display.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
|
def show_bin_download(self, data):
|
||||||
|
url = self.entry_url.get()
|
||||||
|
filename = url.rpartition('/')
|
||||||
|
if len(filename) > 1:
|
||||||
|
filename = filename[-1]
|
||||||
|
else:
|
||||||
|
filename = ''
|
||||||
|
|
||||||
|
callback = (lambda event, bindata=data, write='wb': self.write_to_file(contents=bindata, event=event, write=write))
|
||||||
|
hover = (lambda event, href='Download {}'.format(filename), tag_name='bin_download': self.hoverlink(event, href, tag_name))
|
||||||
|
clear = (lambda event, tag_name='bin_download': self.clear_status(event, tag_name))
|
||||||
|
self.site_display.tag_bind('bin_download', "<Button-1>", callback)
|
||||||
|
self.site_display.tag_bind('bin_download', "<Enter>", hover)
|
||||||
|
self.site_display.tag_bind('bin_download', '<Leave>', clear)
|
||||||
|
self.site_display.config(state=tk.NORMAL)
|
||||||
|
self.site_display.delete(1.0, tk.END)
|
||||||
|
self.site_display.insert(tk.END,'Download ')
|
||||||
|
self.site_display.insert(tk.END,filename,('linkcolor','bin_download'))
|
||||||
|
self.site_display.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
def send_to_screen(self, data, itemtype='1', clear=True):
|
def send_to_screen(self, data, itemtype='1', clear=True):
|
||||||
if itemtype == '0':
|
if itemtype == '0':
|
||||||
self.show_text(data)
|
self.show_text(data)
|
||||||
|
@ -440,6 +480,8 @@ class GUI:
|
||||||
self.show_menu(data, clear)
|
self.show_menu(data, clear)
|
||||||
elif itemtype in ['p','I','g']:
|
elif itemtype in ['p','I','g']:
|
||||||
self.show_image(data)
|
self.show_image(data)
|
||||||
|
elif itemtype in ['s','9','M','c',';','d','5']:
|
||||||
|
self.show_bin_download(data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.loading_bar.destroy()
|
self.loading_bar.destroy()
|
||||||
|
@ -562,6 +604,23 @@ class GUI:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def write_to_file(self, contents=None, page_url=None, event=None, write='w'):
|
||||||
|
url = self.entry_url.get()
|
||||||
|
filetype = url.rpartition('.')
|
||||||
|
if len(filetype) > 1:
|
||||||
|
filetype = filetype[-1]
|
||||||
|
else:
|
||||||
|
filetype = 'txt'
|
||||||
|
|
||||||
|
filename = savedialog(initialdir="~/Desktop/", defaultextension='.{}'.format(filetype), title="Save As File", filetypes=((filetype,'*.{}'.format(filetype)),('all files','*.*')))
|
||||||
|
if not filename or filename is None or contents is None:
|
||||||
|
return False
|
||||||
|
with open(filename, write) as f:
|
||||||
|
f.write(contents)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = GUI()
|
app = GUI()
|
||||||
app.root.mainloop()
|
app.root.mainloop()
|
||||||
|
|
|
@ -10,6 +10,7 @@ i##########CONTENT PORTALS########## false null.host 1
|
||||||
i false null.host 1
|
i false null.host 1
|
||||||
1Floodgap / gopher.floodgap.com 70
|
1Floodgap / gopher.floodgap.com 70
|
||||||
1Super Dimensional Fortress / sdf.org 70
|
1Super Dimensional Fortress / sdf.org 70
|
||||||
|
7Veronica-2: Search Engine /v2/vs gopher.floodgap.com 70
|
||||||
i false null.host 1
|
i false null.host 1
|
||||||
i false null.host 1
|
i false null.host 1
|
||||||
i#############FAVORITES############# false null.host 1
|
i#############FAVORITES############# false null.host 1
|
||||||
|
|
|
@ -18,7 +18,7 @@ class parser:
|
||||||
if url == 'home':
|
if url == 'home':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
regex = r'^(?P<protocol>(?:(gopher|http):\/\/)?)?(?P<host>[\w\-\.\d]+)(?P<port>(?::\d+)?)?(?P<type>(?:\/[\dgIp])?)?(?P<resource>(?:\/.*)?)?$'
|
regex = r'^(?P<protocol>(?:(gopher|http):\/\/)?)?(?P<host>[\w\-\.\d]+)(?P<port>(?::\d+)?)?(?P<type>(?:\/[01345679gIhisp])?)?(?P<resource>(?:\/.*)?)?$'
|
||||||
|
|
||||||
match = re.match(regex, url)
|
match = re.match(regex, url)
|
||||||
|
|
||||||
|
@ -37,6 +37,9 @@ class parser:
|
||||||
if not resource:
|
if not resource:
|
||||||
resource = '/'
|
resource = '/'
|
||||||
|
|
||||||
|
if not itemtype:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
self.filetype = itemtype[len(itemtype) - 1] if itemtype else '1'
|
self.filetype = itemtype[len(itemtype) - 1] if itemtype else '1'
|
||||||
self.protocol = protocol if protocol else 'gopher://'
|
self.protocol = protocol if protocol else 'gopher://'
|
||||||
|
|
Loading…
Reference in New Issue