Added support for binary downloads

This commit is contained in:
sloumdrone 2018-11-18 08:43:36 -08:00
parent 49571178db
commit e11bd4d636
6 changed files with 92 additions and 32 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ __pycache__/*
__pycache__ __pycache__
*.swp *.swp
*.json *.json
*.pyc

View File

@ -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

View File

@ -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}

83
gui.py
View File

@ -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,30 +158,42 @@ 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:
self.context_menu.add_separator()
copy_link = (lambda link=href: self.copy_to_clipboard(link)) 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_command(label=" Copy URL to clipboard ", command=copy_link)
self.context_menu.add_separator()
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()

View File

@ -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

View File

@ -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://'