diff --git a/plugins/Stats/StatsPlugin.py b/plugins/Stats/StatsPlugin.py
index 11f2758e..b6aa2336 100644
--- a/plugins/Stats/StatsPlugin.py
+++ b/plugins/Stats/StatsPlugin.py
@@ -53,12 +53,14 @@ class UiRequestPlugin(object):
# Memory
try:
- yield "Ip external: %s | " % config.ip_external
- yield "Port opened: %s | " % main.file_server.port_opened
+ yield "IP external: %s | " % config.ip_external
+ yield "Opened: %s | " % main.file_server.port_opened
+ yield "Recv: %.2fMB, Sent: %.2fMB | " % (float(main.file_server.bytes_recv)/1024/1024, float(main.file_server.bytes_sent)/1024/1024)
+ yield "Peerid: %s | " % main.file_server.peer_id
import psutil
process = psutil.Process(os.getpid())
mem = process.get_memory_info()[0] / float(2 ** 20)
- yield "Memory usage: %.2fMB | " % mem
+ yield "Mem: %.2fMB | " % mem
yield "Threads: %s | " % len(process.threads())
yield "CPU: usr %.2fs sys %.2fs | " % process.cpu_times()
yield "Open files: %s | " % len(process.open_files())
@@ -69,7 +71,7 @@ class UiRequestPlugin(object):
yield "
"
# Connections
- yield "Connections (%s):
" % len(main.file_server.connections)
+ yield "Connections (%s, total made: %s):
" % (len(main.file_server.connections), main.file_server.last_connection_id)
yield "
id | protocol | type | ip | open | ping | buff | "
yield "idle | open | delay | sent | received | last sent | waiting | version | peerid |
"
for connection in main.file_server.connections:
@@ -100,11 +102,15 @@ class UiRequestPlugin(object):
yield "address | connected | peers | content.json |
"
for site in self.server.sites.values():
yield self.formatTableRow([
- ("%s", site.address),
+ ("%s", (site.address, site.address)),
("%s", [peer.connection.id for peer in site.peers.values() if peer.connection and peer.connection.connected]),
("%s/%s", ( len([peer for peer in site.peers.values() if peer.connection and peer.connection.connected]), len(site.peers) ) ),
("%s", len(site.content_manager.contents)),
])
+ yield "" % site.address
+ for key, peer in site.peers.items():
+ yield "(%s) %s - " % (peer.connection, key)
+ yield "
|
"
yield "
"
diff --git a/plugins/Trayicon/TrayiconPlugin.py b/plugins/Trayicon/TrayiconPlugin.py
new file mode 100644
index 00000000..5517fa96
--- /dev/null
+++ b/plugins/Trayicon/TrayiconPlugin.py
@@ -0,0 +1,91 @@
+import re, time, cgi, os, sys
+from Plugin import PluginManager
+from Config import config
+import atexit
+
+allow_reload = False # No reload supported
+
+@PluginManager.registerTo("Actions")
+class ActionsPlugin(object):
+ def main(self):
+ global notificationicon, winfolders
+ from lib import notificationicon, winfolders
+ import gevent.threadpool
+
+ self.main = sys.modules["main"]
+
+ icon = notificationicon.NotificationIcon(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'trayicon.ico'), "ZeroNet %s" % config.version)
+ self.icon = icon
+
+ if not config.debug: # Hide console if not in debug mode
+ notificationicon.hideConsole()
+ self.console = False
+ else:
+ self.console = True
+
+ @atexit.register
+ def hideIcon():
+ icon.die()
+
+ icon.items = (
+ (self.titleIp, False),
+ (self.titleConnections, False),
+ (self.titleTransfer, False),
+ (self.titleConsole, self.toggleConsole),
+ "--",
+ ("ZeroNet Twitter", lambda: self.opensite("https://twitter.com/HelloZeroNet") ),
+ ("ZeroNet Reddit", lambda: self.opensite("http://www.reddit.com/r/zeronet/") ),
+ ("ZeroNet Github", lambda: self.opensite("https://github.com/HelloZeroNet/ZeroNet") ),
+ ("Report bug/request feature", lambda: self.opensite("https://github.com/HelloZeroNet/ZeroNet/issues") ),
+ "--",
+ ("!Open ZeroNet", lambda: self.opensite("http://%s:%s" % (config.ui_ip, config.ui_port)) ),
+ #"--",
+ #("Start ZeroNet when Windows starts", quit),
+ "--",
+ ("Quit", self.quit),
+
+ )
+
+ icon.clicked = lambda: self.opensite("http://%s:%s" % (config.ui_ip, config.ui_port))
+ gevent.threadpool.start_new_thread(icon._run, ()) # Start in real thread (not gevent compatible)
+ super(ActionsPlugin, self).main()
+ icon._die = True
+
+ def quit(self):
+ self.icon.die()
+ time.sleep(0.1)
+ self.main.ui_server.stop()
+ self.main.file_server.stop()
+ #sys.exit()
+
+ def opensite(self, url):
+ import webbrowser
+ webbrowser.open(url, new=2)
+
+ def titleIp(self):
+ title = "!IP: %s" % config.ip_external
+ if self.main.file_server.port_opened:
+ title += " (active)"
+ else:
+ title += " (passive)"
+ return title
+
+ def titleConnections(self):
+ title = "Connections: %s" % len(self.main.file_server.connections)
+ return title
+
+ def titleTransfer(self):
+ title = "Received: %.2f MB | Sent: %.2f MB" % (float(self.main.file_server.bytes_recv)/1024/1024, float(self.main.file_server.bytes_sent)/1024/1024)
+ return title
+
+ def titleConsole(self):
+ if self.console: return "+Show console window"
+ else: return "Show console window"
+
+ def toggleConsole(self):
+ if self.console:
+ notificationicon.hideConsole()
+ self.console = False
+ else:
+ notificationicon.showConsole()
+ self.console = True
diff --git a/plugins/Trayicon/__init__.py b/plugins/Trayicon/__init__.py
new file mode 100644
index 00000000..5b584962
--- /dev/null
+++ b/plugins/Trayicon/__init__.py
@@ -0,0 +1,4 @@
+import sys
+
+if sys.platform == 'win32':
+ import TrayiconPlugin
\ No newline at end of file
diff --git a/plugins/Trayicon/lib/__init__.py b/plugins/Trayicon/lib/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/Trayicon/lib/notificationicon.py b/plugins/Trayicon/lib/notificationicon.py
new file mode 100644
index 00000000..73e1c83c
--- /dev/null
+++ b/plugins/Trayicon/lib/notificationicon.py
@@ -0,0 +1,800 @@
+# Pure ctypes windows taskbar notification icon
+# via https://gist.github.com/jasonbot/5759510
+# Modified for ZeroNet
+
+import ctypes
+import ctypes.wintypes
+import os
+#import threading
+#import Queue
+import uuid
+import time
+import gevent
+
+__all__ = ['NotificationIcon']
+
+# Create popup menu
+
+CreatePopupMenu = ctypes.windll.user32.CreatePopupMenu
+CreatePopupMenu.restype = ctypes.wintypes.HMENU
+CreatePopupMenu.argtypes = []
+
+MF_BYCOMMAND = 0x0
+MF_BYPOSITION = 0x400
+
+MF_BITMAP = 0x4
+MF_CHECKED = 0x8
+MF_DISABLED = 0x2
+MF_ENABLED = 0x0
+MF_GRAYED = 0x1
+MF_MENUBARBREAK = 0x20
+MF_MENUBREAK = 0x40
+MF_OWNERDRAW = 0x100
+MF_POPUP = 0x10
+MF_SEPARATOR = 0x800
+MF_STRING = 0x0
+MF_UNCHECKED = 0x0
+
+InsertMenu = ctypes.windll.user32.InsertMenuW
+InsertMenu.restype = ctypes.wintypes.BOOL
+InsertMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.LPCWSTR]
+
+AppendMenu = ctypes.windll.user32.AppendMenuW
+AppendMenu.restype = ctypes.wintypes.BOOL
+AppendMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.wintypes.LPCWSTR]
+
+SetMenuDefaultItem = ctypes.windll.user32.SetMenuDefaultItem
+SetMenuDefaultItem.restype = ctypes.wintypes.BOOL
+SetMenuDefaultItem.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.wintypes.UINT]
+
+#class MENUITEMINFO(ctypes.Structure):
+# UINT cbSize;
+# UINT fMask;
+# UINT fType;
+# UINT fState;
+# UINT wID;
+# HMENU hSubMenu;
+# HBITMAP hbmpChecked;
+# HBITMAP hbmpUnchecked;
+# ULONG_PTR dwItemData;
+# LPTSTR dwTypeData;
+# UINT cch;
+# HBITMAP hbmpItem;
+#
+#BOOL WINAPI InsertMenuItem(
+# __in HMENU hMenu,
+# __in UINT uItem,
+# __in BOOL fByPosition,
+# __in LPCMENUITEMINFO lpmii
+#);
+#
+
+class POINT(ctypes.Structure):
+ _fields_ = [ ('x', ctypes.wintypes.LONG),
+ ('y', ctypes.wintypes.LONG)]
+
+GetCursorPos = ctypes.windll.user32.GetCursorPos
+GetCursorPos.argtypes = [ctypes.POINTER(POINT)]
+
+SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
+SetForegroundWindow.argtypes = [ctypes.wintypes.HWND]
+
+TPM_LEFTALIGN = 0x0
+TPM_CENTERALIGN = 0x4
+TPM_RIGHTALIGN = 0x8
+
+TPM_TOPALIGN = 0x0
+TPM_VCENTERALIGN = 0x10
+TPM_BOTTOMALIGN = 0x20
+
+TPM_NONOTIFY = 0x80
+TPM_RETURNCMD = 0x100
+
+TPM_LEFTBUTTON = 0x0
+TPM_RIGHTBUTTON = 0x2
+
+TPM_HORNEGANIMATION = 0x800
+TPM_HORPOSANIMATION = 0x400
+TPM_NOANIMATION = 0x4000
+TPM_VERNEGANIMATION = 0x2000
+TPM_VERPOSANIMATION = 0x1000
+
+TrackPopupMenu = ctypes.windll.user32.TrackPopupMenu
+TrackPopupMenu.restype = ctypes.wintypes.BOOL
+TrackPopupMenu.argtypes = [ctypes.wintypes.HMENU, ctypes.wintypes.UINT, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.wintypes.HWND, ctypes.c_void_p]
+
+PostMessage = ctypes.windll.user32.PostMessageW
+PostMessage.restype = ctypes.wintypes.BOOL
+PostMessage.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM]
+
+DestroyMenu = ctypes.windll.user32.DestroyMenu
+DestroyMenu.restype = ctypes.wintypes.BOOL
+DestroyMenu.argtypes = [ctypes.wintypes.HMENU]
+
+# Create notification icon
+
+GUID = ctypes.c_ubyte * 16
+
+class TimeoutVersionUnion(ctypes.Union):
+ _fields_ = [('uTimeout', ctypes.wintypes.UINT),
+ ('uVersion', ctypes.wintypes.UINT),]
+
+NIS_HIDDEN = 0x1
+NIS_SHAREDICON = 0x2
+
+class NOTIFYICONDATA(ctypes.Structure):
+ def __init__(self, *args, **kwargs):
+ super(NOTIFYICONDATA, self).__init__(*args, **kwargs)
+ self.cbSize = ctypes.sizeof(self)
+ _fields_ = [
+ ('cbSize', ctypes.wintypes.DWORD),
+ ('hWnd', ctypes.wintypes.HWND),
+ ('uID', ctypes.wintypes.UINT),
+ ('uFlags', ctypes.wintypes.UINT),
+ ('uCallbackMessage', ctypes.wintypes.UINT),
+ ('hIcon', ctypes.wintypes.HICON),
+ ('szTip', ctypes.wintypes.WCHAR * 64),
+ ('dwState', ctypes.wintypes.DWORD),
+ ('dwStateMask', ctypes.wintypes.DWORD),
+ ('szInfo', ctypes.wintypes.WCHAR * 256),
+ ('union', TimeoutVersionUnion),
+ ('szInfoTitle', ctypes.wintypes.WCHAR * 64),
+ ('dwInfoFlags', ctypes.wintypes.DWORD),
+ ('guidItem', GUID),
+ ('hBalloonIcon', ctypes.wintypes.HICON),
+ ]
+
+NIM_ADD = 0
+NIM_MODIFY = 1
+NIM_DELETE = 2
+NIM_SETFOCUS = 3
+NIM_SETVERSION = 4
+
+NIF_MESSAGE = 1
+NIF_ICON = 2
+NIF_TIP = 4
+NIF_STATE = 8
+NIF_INFO = 16
+NIF_GUID = 32
+NIF_REALTIME = 64
+NIF_SHOWTIP = 128
+
+NIIF_NONE = 0
+NIIF_INFO = 1
+NIIF_WARNING = 2
+NIIF_ERROR = 3
+NIIF_USER = 4
+
+NOTIFYICON_VERSION = 3
+NOTIFYICON_VERSION_4 = 4
+
+Shell_NotifyIcon = ctypes.windll.shell32.Shell_NotifyIconW
+Shell_NotifyIcon.restype = ctypes.wintypes.BOOL
+Shell_NotifyIcon.argtypes = [ctypes.wintypes.DWORD, ctypes.POINTER(NOTIFYICONDATA)]
+
+# Load icon/image
+
+IMAGE_BITMAP = 0
+IMAGE_ICON = 1
+IMAGE_CURSOR = 2
+
+LR_CREATEDIBSECTION = 0x00002000
+LR_DEFAULTCOLOR = 0x00000000
+LR_DEFAULTSIZE = 0x00000040
+LR_LOADFROMFILE = 0x00000010
+LR_LOADMAP3DCOLORS = 0x00001000
+LR_LOADTRANSPARENT = 0x00000020
+LR_MONOCHROME = 0x00000001
+LR_SHARED = 0x00008000
+LR_VGACOLOR = 0x00000080
+
+OIC_SAMPLE = 32512
+OIC_HAND = 32513
+OIC_QUES = 32514
+OIC_BANG = 32515
+OIC_NOTE = 32516
+OIC_WINLOGO = 32517
+OIC_WARNING = OIC_BANG
+OIC_ERROR = OIC_HAND
+OIC_INFORMATION = OIC_NOTE
+
+LoadImage = ctypes.windll.user32.LoadImageW
+LoadImage.restype = ctypes.wintypes.HANDLE
+LoadImage.argtypes = [ctypes.wintypes.HINSTANCE, ctypes.wintypes.LPCWSTR, ctypes.wintypes.UINT, ctypes.c_int, ctypes.c_int, ctypes.wintypes.UINT]
+
+# CreateWindow call
+
+WNDPROC = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.wintypes.HWND, ctypes.c_uint, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM)
+DefWindowProc = ctypes.windll.user32.DefWindowProcW
+DefWindowProc.restype = ctypes.c_int
+DefWindowProc.argtypes = [ctypes.wintypes.HWND, ctypes.c_uint, ctypes.wintypes.WPARAM, ctypes.wintypes.LPARAM]
+
+WS_OVERLAPPED = 0x00000000L
+WS_POPUP = 0x80000000L
+WS_CHILD = 0x40000000L
+WS_MINIMIZE = 0x20000000L
+WS_VISIBLE = 0x10000000L
+WS_DISABLED = 0x08000000L
+WS_CLIPSIBLINGS = 0x04000000L
+WS_CLIPCHILDREN = 0x02000000L
+WS_MAXIMIZE = 0x01000000L
+WS_CAPTION = 0x00C00000L
+WS_BORDER = 0x00800000L
+WS_DLGFRAME = 0x00400000L
+WS_VSCROLL = 0x00200000L
+WS_HSCROLL = 0x00100000L
+WS_SYSMENU = 0x00080000L
+WS_THICKFRAME = 0x00040000L
+WS_GROUP = 0x00020000L
+WS_TABSTOP = 0x00010000L
+
+WS_MINIMIZEBOX = 0x00020000L
+WS_MAXIMIZEBOX = 0x00010000L
+
+WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED |
+ WS_CAPTION |
+ WS_SYSMENU |
+ WS_THICKFRAME |
+ WS_MINIMIZEBOX |
+ WS_MAXIMIZEBOX)
+
+SM_XVIRTUALSCREEN = 76
+SM_YVIRTUALSCREEN = 77
+SM_CXVIRTUALSCREEN = 78
+SM_CYVIRTUALSCREEN = 79
+SM_CMONITORS = 80
+SM_SAMEDISPLAYFORMAT = 81
+
+WM_NULL = 0x0000
+WM_CREATE = 0x0001
+WM_DESTROY = 0x0002
+WM_MOVE = 0x0003
+WM_SIZE = 0x0005
+WM_ACTIVATE = 0x0006
+WM_SETFOCUS = 0x0007
+WM_KILLFOCUS = 0x0008
+WM_ENABLE = 0x000A
+WM_SETREDRAW = 0x000B
+WM_SETTEXT = 0x000C
+WM_GETTEXT = 0x000D
+WM_GETTEXTLENGTH = 0x000E
+WM_PAINT = 0x000F
+WM_CLOSE = 0x0010
+WM_QUERYENDSESSION = 0x0011
+WM_QUIT = 0x0012
+WM_QUERYOPEN = 0x0013
+WM_ERASEBKGND = 0x0014
+WM_SYSCOLORCHANGE = 0x0015
+WM_ENDSESSION = 0x0016
+WM_SHOWWINDOW = 0x0018
+WM_CTLCOLOR = 0x0019
+WM_WININICHANGE = 0x001A
+WM_SETTINGCHANGE = 0x001A
+WM_DEVMODECHANGE = 0x001B
+WM_ACTIVATEAPP = 0x001C
+WM_FONTCHANGE = 0x001D
+WM_TIMECHANGE = 0x001E
+WM_CANCELMODE = 0x001F
+WM_SETCURSOR = 0x0020
+WM_MOUSEACTIVATE = 0x0021
+WM_CHILDACTIVATE = 0x0022
+WM_QUEUESYNC = 0x0023
+WM_GETMINMAXINFO = 0x0024
+WM_PAINTICON = 0x0026
+WM_ICONERASEBKGND = 0x0027
+WM_NEXTDLGCTL = 0x0028
+WM_SPOOLERSTATUS = 0x002A
+WM_DRAWITEM = 0x002B
+WM_MEASUREITEM = 0x002C
+WM_DELETEITEM = 0x002D
+WM_VKEYTOITEM = 0x002E
+WM_CHARTOITEM = 0x002F
+WM_SETFONT = 0x0030
+WM_GETFONT = 0x0031
+WM_SETHOTKEY = 0x0032
+WM_GETHOTKEY = 0x0033
+WM_QUERYDRAGICON = 0x0037
+WM_COMPAREITEM = 0x0039
+WM_GETOBJECT = 0x003D
+WM_COMPACTING = 0x0041
+WM_COMMNOTIFY = 0x0044
+WM_WINDOWPOSCHANGING = 0x0046
+WM_WINDOWPOSCHANGED = 0x0047
+WM_POWER = 0x0048
+WM_COPYDATA = 0x004A
+WM_CANCELJOURNAL = 0x004B
+WM_NOTIFY = 0x004E
+WM_INPUTLANGCHANGEREQUEST = 0x0050
+WM_INPUTLANGCHANGE = 0x0051
+WM_TCARD = 0x0052
+WM_HELP = 0x0053
+WM_USERCHANGED = 0x0054
+WM_NOTIFYFORMAT = 0x0055
+WM_CONTEXTMENU = 0x007B
+WM_STYLECHANGING = 0x007C
+WM_STYLECHANGED = 0x007D
+WM_DISPLAYCHANGE = 0x007E
+WM_GETICON = 0x007F
+WM_SETICON = 0x0080
+WM_NCCREATE = 0x0081
+WM_NCDESTROY = 0x0082
+WM_NCCALCSIZE = 0x0083
+WM_NCHITTEST = 0x0084
+WM_NCPAINT = 0x0085
+WM_NCACTIVATE = 0x0086
+WM_GETDLGCODE = 0x0087
+WM_SYNCPAINT = 0x0088
+WM_NCMOUSEMOVE = 0x00A0
+WM_NCLBUTTONDOWN = 0x00A1
+WM_NCLBUTTONUP = 0x00A2
+WM_NCLBUTTONDBLCLK = 0x00A3
+WM_NCRBUTTONDOWN = 0x00A4
+WM_NCRBUTTONUP = 0x00A5
+WM_NCRBUTTONDBLCLK = 0x00A6
+WM_NCMBUTTONDOWN = 0x00A7
+WM_NCMBUTTONUP = 0x00A8
+WM_NCMBUTTONDBLCLK = 0x00A9
+WM_KEYDOWN = 0x0100
+WM_KEYUP = 0x0101
+WM_CHAR = 0x0102
+WM_DEADCHAR = 0x0103
+WM_SYSKEYDOWN = 0x0104
+WM_SYSKEYUP = 0x0105
+WM_SYSCHAR = 0x0106
+WM_SYSDEADCHAR = 0x0107
+WM_KEYLAST = 0x0108
+WM_IME_STARTCOMPOSITION = 0x010D
+WM_IME_ENDCOMPOSITION = 0x010E
+WM_IME_COMPOSITION = 0x010F
+WM_IME_KEYLAST = 0x010F
+WM_INITDIALOG = 0x0110
+WM_COMMAND = 0x0111
+WM_SYSCOMMAND = 0x0112
+WM_TIMER = 0x0113
+WM_HSCROLL = 0x0114
+WM_VSCROLL = 0x0115
+WM_INITMENU = 0x0116
+WM_INITMENUPOPUP = 0x0117
+WM_MENUSELECT = 0x011F
+WM_MENUCHAR = 0x0120
+WM_ENTERIDLE = 0x0121
+WM_MENURBUTTONUP = 0x0122
+WM_MENUDRAG = 0x0123
+WM_MENUGETOBJECT = 0x0124
+WM_UNINITMENUPOPUP = 0x0125
+WM_MENUCOMMAND = 0x0126
+WM_CTLCOLORMSGBOX = 0x0132
+WM_CTLCOLOREDIT = 0x0133
+WM_CTLCOLORLISTBOX = 0x0134
+WM_CTLCOLORBTN = 0x0135
+WM_CTLCOLORDLG = 0x0136
+WM_CTLCOLORSCROLLBAR = 0x0137
+WM_CTLCOLORSTATIC = 0x0138
+WM_MOUSEMOVE = 0x0200
+WM_LBUTTONDOWN = 0x0201
+WM_LBUTTONUP = 0x0202
+WM_LBUTTONDBLCLK = 0x0203
+WM_RBUTTONDOWN = 0x0204
+WM_RBUTTONUP = 0x0205
+WM_RBUTTONDBLCLK = 0x0206
+WM_MBUTTONDOWN = 0x0207
+WM_MBUTTONUP = 0x0208
+WM_MBUTTONDBLCLK = 0x0209
+WM_MOUSEWHEEL = 0x020A
+WM_PARENTNOTIFY = 0x0210
+WM_ENTERMENULOOP = 0x0211
+WM_EXITMENULOOP = 0x0212
+WM_NEXTMENU = 0x0213
+WM_SIZING = 0x0214
+WM_CAPTURECHANGED = 0x0215
+WM_MOVING = 0x0216
+WM_DEVICECHANGE = 0x0219
+WM_MDICREATE = 0x0220
+WM_MDIDESTROY = 0x0221
+WM_MDIACTIVATE = 0x0222
+WM_MDIRESTORE = 0x0223
+WM_MDINEXT = 0x0224
+WM_MDIMAXIMIZE = 0x0225
+WM_MDITILE = 0x0226
+WM_MDICASCADE = 0x0227
+WM_MDIICONARRANGE = 0x0228
+WM_MDIGETACTIVE = 0x0229
+WM_MDISETMENU = 0x0230
+WM_ENTERSIZEMOVE = 0x0231
+WM_EXITSIZEMOVE = 0x0232
+WM_DROPFILES = 0x0233
+WM_MDIREFRESHMENU = 0x0234
+WM_IME_SETCONTEXT = 0x0281
+WM_IME_NOTIFY = 0x0282
+WM_IME_CONTROL = 0x0283
+WM_IME_COMPOSITIONFULL = 0x0284
+WM_IME_SELECT = 0x0285
+WM_IME_CHAR = 0x0286
+WM_IME_REQUEST = 0x0288
+WM_IME_KEYDOWN = 0x0290
+WM_IME_KEYUP = 0x0291
+WM_MOUSEHOVER = 0x02A1
+WM_MOUSELEAVE = 0x02A3
+WM_CUT = 0x0300
+WM_COPY = 0x0301
+WM_PASTE = 0x0302
+WM_CLEAR = 0x0303
+WM_UNDO = 0x0304
+WM_RENDERFORMAT = 0x0305
+WM_RENDERALLFORMATS = 0x0306
+WM_DESTROYCLIPBOARD = 0x0307
+WM_DRAWCLIPBOARD = 0x0308
+WM_PAINTCLIPBOARD = 0x0309
+WM_VSCROLLCLIPBOARD = 0x030A
+WM_SIZECLIPBOARD = 0x030B
+WM_ASKCBFORMATNAME = 0x030C
+WM_CHANGECBCHAIN = 0x030D
+WM_HSCROLLCLIPBOARD = 0x030E
+WM_QUERYNEWPALETTE = 0x030F
+WM_PALETTEISCHANGING = 0x0310
+WM_PALETTECHANGED = 0x0311
+WM_HOTKEY = 0x0312
+WM_PRINT = 0x0317
+WM_PRINTCLIENT = 0x0318
+WM_HANDHELDFIRST = 0x0358
+WM_HANDHELDLAST = 0x035F
+WM_AFXFIRST = 0x0360
+WM_AFXLAST = 0x037F
+WM_PENWINFIRST = 0x0380
+WM_PENWINLAST = 0x038F
+WM_APP = 0x8000
+WM_USER = 0x0400
+WM_REFLECT = WM_USER + 0x1c00
+
+class WNDCLASSEX(ctypes.Structure):
+ def __init__(self, *args, **kwargs):
+ super(WNDCLASSEX, self).__init__(*args, **kwargs)
+ self.cbSize = ctypes.sizeof(self)
+ _fields_ = [("cbSize", ctypes.c_uint),
+ ("style", ctypes.c_uint),
+ ("lpfnWndProc", WNDPROC),
+ ("cbClsExtra", ctypes.c_int),
+ ("cbWndExtra", ctypes.c_int),
+ ("hInstance", ctypes.wintypes.HANDLE),
+ ("hIcon", ctypes.wintypes.HANDLE),
+ ("hCursor", ctypes.wintypes.HANDLE),
+ ("hBrush", ctypes.wintypes.HANDLE),
+ ("lpszMenuName", ctypes.wintypes.LPCWSTR),
+ ("lpszClassName", ctypes.wintypes.LPCWSTR),
+ ("hIconSm", ctypes.wintypes.HANDLE)]
+
+UpdateWindow = ctypes.windll.user32.UpdateWindow
+UpdateWindow.argtypes = [ctypes.wintypes.HWND]
+
+SW_HIDE = 0
+SW_SHOWNORMAL = 1
+SW_SHOW = 5
+
+ShowWindow = ctypes.windll.user32.ShowWindow
+ShowWindow.argtypes = [ctypes.wintypes.HWND, ctypes.c_int]
+
+CS_VREDRAW = 0x0001
+CS_HREDRAW = 0x0002
+CS_KEYCVTWINDOW = 0x0004
+CS_DBLCLKS = 0x0008
+CS_OWNDC = 0x0020
+CS_CLASSDC = 0x0040
+CS_PARENTDC = 0x0080
+CS_NOKEYCVT = 0x0100
+CS_NOCLOSE = 0x0200
+CS_SAVEBITS = 0x0800
+CS_BYTEALIGNCLIENT = 0x1000
+CS_BYTEALIGNWINDOW = 0x2000
+CS_GLOBALCLASS = 0x4000
+
+COLOR_SCROLLBAR = 0
+COLOR_BACKGROUND = 1
+COLOR_ACTIVECAPTION = 2
+COLOR_INACTIVECAPTION = 3
+COLOR_MENU = 4
+COLOR_WINDOW = 5
+COLOR_WINDOWFRAME = 6
+COLOR_MENUTEXT = 7
+COLOR_WINDOWTEXT = 8
+COLOR_CAPTIONTEXT = 9
+COLOR_ACTIVEBORDER = 10
+COLOR_INACTIVEBORDER = 11
+COLOR_APPWORKSPACE = 12
+COLOR_HIGHLIGHT = 13
+COLOR_HIGHLIGHTTEXT = 14
+COLOR_BTNFACE = 15
+COLOR_BTNSHADOW = 16
+COLOR_GRAYTEXT = 17
+COLOR_BTNTEXT = 18
+COLOR_INACTIVECAPTIONTEXT = 19
+COLOR_BTNHIGHLIGHT = 20
+
+LoadCursor = ctypes.windll.user32.LoadCursorW
+
+def GenerateDummyWindow(callback, uid):
+ newclass = WNDCLASSEX()
+ newclass.lpfnWndProc = callback
+ newclass.style = CS_VREDRAW | CS_HREDRAW
+ newclass.lpszClassName = uid.replace("-", "")
+ newclass.hBrush = COLOR_BACKGROUND
+ newclass.hCursor = LoadCursor(0, 32512)
+ ATOM = ctypes.windll.user32.RegisterClassExW(ctypes.byref(newclass))
+ #print "ATOM", ATOM
+ #print "CLASS", newclass.lpszClassName
+ hwnd = ctypes.windll.user32.CreateWindowExW(0,
+ newclass.lpszClassName,
+ u"Dummy Window",
+ WS_OVERLAPPEDWINDOW | WS_SYSMENU,
+ ctypes.windll.user32.GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ ctypes.windll.user32.GetSystemMetrics(SM_CYVIRTUALSCREEN),
+ 800, 600, 0, 0, 0, 0)
+ ShowWindow(hwnd, SW_SHOW)
+ UpdateWindow(hwnd)
+ ShowWindow(hwnd, SW_HIDE)
+ return hwnd
+
+# Message loop calls
+
+TIMERCALLBACK = ctypes.WINFUNCTYPE(None,
+ ctypes.wintypes.HWND,
+ ctypes.wintypes.UINT,
+ ctypes.POINTER(ctypes.wintypes.UINT),
+ ctypes.wintypes.DWORD)
+
+SetTimer = ctypes.windll.user32.SetTimer
+SetTimer.restype = ctypes.POINTER(ctypes.wintypes.UINT)
+SetTimer.argtypes = [ctypes.wintypes.HWND,
+ ctypes.POINTER(ctypes.wintypes.UINT),
+ ctypes.wintypes.UINT,
+ TIMERCALLBACK]
+
+KillTimer = ctypes.windll.user32.KillTimer
+KillTimer.restype = ctypes.wintypes.BOOL
+KillTimer.argtypes = [ctypes.wintypes.HWND,
+ ctypes.POINTER(ctypes.wintypes.UINT)]
+
+class MSG(ctypes.Structure):
+ _fields_ = [ ('HWND', ctypes.wintypes.HWND),
+ ('message', ctypes.wintypes.UINT),
+ ('wParam', ctypes.wintypes.WPARAM),
+ ('lParam', ctypes.wintypes.LPARAM),
+ ('time', ctypes.wintypes.DWORD),
+ ('pt', POINT)]
+
+GetMessage = ctypes.windll.user32.GetMessageW
+GetMessage.restype = ctypes.wintypes.BOOL
+GetMessage.argtypes = [ctypes.POINTER(MSG), ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.UINT]
+
+TranslateMessage = ctypes.windll.user32.TranslateMessage
+TranslateMessage.restype = ctypes.wintypes.ULONG
+TranslateMessage.argtypes = [ctypes.POINTER(MSG)]
+
+DispatchMessage = ctypes.windll.user32.DispatchMessageW
+DispatchMessage.restype = ctypes.wintypes.ULONG
+DispatchMessage.argtypes = [ctypes.POINTER(MSG)]
+
+def LoadIcon(iconfilename, small=False):
+ return LoadImage(0,
+ unicode(iconfilename),
+ IMAGE_ICON,
+ 16 if small else 0,
+ 16 if small else 0,
+ LR_LOADFROMFILE)
+
+
+class NotificationIcon(object):
+ def __init__(self, iconfilename, tooltip=None):
+ assert os.path.isfile(unicode(iconfilename)), "{} doesn't exist".format(iconfilename)
+ self._iconfile = unicode(iconfilename)
+ self._hicon = LoadIcon(self._iconfile, True)
+ assert self._hicon, "Failed to load {}".format(iconfilename)
+ #self._pumpqueue = Queue.Queue()
+ self._die = False
+ self._timerid = None
+ self._uid = uuid.uuid4()
+ self._tooltip = unicode(tooltip) if tooltip else u''
+ #self._thread = threading.Thread(target=self._run)
+ #self._thread.start()
+ self._info_bubble = None
+ self.items = []
+
+
+ def _bubble(self, iconinfo):
+ if self._info_bubble:
+ info_bubble = self._info_bubble
+ self._info_bubble = None
+ message = unicode(self._info_bubble)
+ iconinfo.uFlags |= NIF_INFO
+ iconinfo.szInfo = message
+ iconinfo.szInfoTitle = message
+ iconinfo.dwInfoFlags = NIIF_INFO
+ iconinfo.union.uTimeout = 10000
+ Shell_NotifyIcon(NIM_MODIFY, ctypes.pointer(iconinfo))
+
+
+ def _run(self):
+ self._windowproc = WNDPROC(self._callback)
+ self._hwnd = GenerateDummyWindow(self._windowproc, str(self._uid))
+
+ iconinfo = NOTIFYICONDATA()
+ iconinfo.hWnd = self._hwnd
+ iconinfo.uID = 100
+ iconinfo.uFlags = NIF_ICON | NIF_SHOWTIP | NIF_MESSAGE | (NIF_TIP if self._tooltip else 0)
+ iconinfo.uCallbackMessage = WM_MENUCOMMAND
+ iconinfo.hIcon = self._hicon
+ iconinfo.szTip = self._tooltip
+ iconinfo.dwState = NIS_SHAREDICON
+ iconinfo.dwInfoFlags = NIIF_INFO
+ # iconinfo.dwStateMask = NIS_SHAREDICON
+ iconinfo.szInfo = "Application Title"
+ iconinfo.union.uTimeout = 5000
+
+ Shell_NotifyIcon(NIM_ADD, ctypes.pointer(iconinfo))
+
+ iconinfo.union.uVersion = NOTIFYICON_VERSION
+ self.iconinfo = iconinfo
+
+ Shell_NotifyIcon(NIM_SETVERSION, ctypes.pointer(iconinfo))
+
+ PostMessage(self._hwnd, WM_NULL, 0, 0)
+
+ #self._timerid = SetTimer(self._hwnd, self._timerid, 25, TIMERCALLBACK())
+ message = MSG()
+ last_time = -1
+ ret = None
+ while not self._die:
+ ret = GetMessage(ctypes.pointer(message), 0, 0, 0)
+ TranslateMessage(ctypes.pointer(message))
+ DispatchMessage(ctypes.pointer(message))
+ time.sleep(0.125)
+ #KillTimer(self._hwnd, self._timerid)
+
+ Shell_NotifyIcon(NIM_DELETE, ctypes.pointer(iconinfo))
+ ctypes.windll.user32.DestroyWindow(self._hwnd)
+ ctypes.windll.user32.DestroyIcon(self._hicon)
+
+
+ def _menu(self):
+ if not hasattr(self, 'items'):
+ return
+
+ menu = CreatePopupMenu()
+ func = None
+
+ try:
+ iidx = 1000
+ defaultitem = -1
+ item_map = {}
+ for fs in self.items:
+ iidx += 1
+ if isinstance(fs, basestring):
+ if fs and not fs.strip('-_='):
+ AppendMenu(menu, MF_SEPARATOR, iidx, fs)
+ else:
+ AppendMenu(menu, MF_STRING | MF_GRAYED, iidx, fs)
+ elif isinstance(fs, tuple):
+ if callable(fs[0]):
+ itemstring = fs[0]()
+ else:
+ itemstring = unicode(fs[0])
+ flags = MF_STRING
+ if itemstring.startswith("!"):
+ itemstring = itemstring[1:]
+ defaultitem = iidx
+ if itemstring.startswith("+"):
+ itemstring = itemstring[1:]
+ flags = flags | MF_CHECKED
+ itemcallable = fs[1]
+ item_map[iidx] = itemcallable
+ if itemcallable is False:
+ flags = flags | MF_DISABLED
+ elif not callable(itemcallable):
+ flags = flags | MF_GRAYED
+ AppendMenu(menu, flags, iidx, itemstring)
+
+ if defaultitem != -1:
+ SetMenuDefaultItem(menu, defaultitem, 0)
+
+ pos = POINT()
+ GetCursorPos(ctypes.pointer(pos))
+
+ PostMessage(self._hwnd, WM_NULL, 0, 0)
+
+ SetForegroundWindow(self._hwnd)
+
+ ti = TrackPopupMenu(menu, TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, pos.x, pos.y, 0, self._hwnd, None)
+
+ if ti in item_map:
+ func = item_map[ti]
+
+ PostMessage(self._hwnd, WM_NULL, 0, 0)
+ finally:
+ DestroyMenu(menu)
+ if func: func()
+
+
+ def clicked(self):
+ self._menu()
+
+
+
+ def _callback(self, hWnd, msg, wParam, lParam):
+ # Check if the main thread is still alive
+ if msg == WM_TIMER:
+ if not any(thread.getName() == 'MainThread' and thread.isAlive()
+ for thread in threading.enumerate()):
+ self._die = True
+ elif msg == WM_MENUCOMMAND and lParam == WM_LBUTTONUP:
+ self.clicked()
+ elif msg == WM_MENUCOMMAND and lParam == WM_RBUTTONUP:
+ self._menu()
+ else:
+ return DefWindowProc(hWnd, msg, wParam, lParam)
+ return 1
+
+
+ def die(self):
+ try:
+ Shell_NotifyIcon(NIM_DELETE, ctypes.pointer(self.iconinfo))
+ except:
+ pass
+ ctypes.windll.user32.DestroyWindow(self._hwnd)
+ ctypes.windll.user32.DestroyIcon(self._hicon)
+ self._die = True
+
+
+ def pump(self):
+ try:
+ while not self._pumpqueue.empty():
+ callable = self._pumpqueue.get(False)
+ callable()
+ except Queue.Empty:
+ pass
+
+
+ def announce(self, text):
+ self._info_bubble = text
+
+
+def hideConsole():
+ ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
+
+def showConsole():
+ ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
+
+if __name__ == "__main__":
+ import time
+ def greet():
+ ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
+ print "Hello"
+ def quit():
+ ni._die = True
+ #sys.exit()
+ def announce():
+ ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
+ ni.announce("Hello there")
+
+ def clicked():
+ ni.announce("Hello")
+
+ def dynamicTitle():
+ return "!The time is: %s" % time.time()
+
+ ni = NotificationIcon(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../trayicon.ico'), "ZeroNet 0.2.9")
+ ni.items = [
+ (dynamicTitle, False),
+ ('Hello', greet),
+ ('Title', False),
+ ('!Default', greet),
+ ('+Popup bubble', announce),
+ 'Nothing',
+ '--',
+ ('Quit', quit)
+ ]
+ ni.clicked = clicked
+ import atexit
+ @atexit.register
+ def goodbye():
+ print "You are now leaving the Python sector."
+
+ ni._run()
diff --git a/plugins/Trayicon/lib/winfolders.py b/plugins/Trayicon/lib/winfolders.py
new file mode 100644
index 00000000..0e390fad
--- /dev/null
+++ b/plugins/Trayicon/lib/winfolders.py
@@ -0,0 +1,48 @@
+''' Get windows special folders without pythonwin
+ Example:
+ import specialfolders
+ start_programs = specialfolders.get(specialfolders.PROGRAMS)
+
+Code is public domain, do with it what you will.
+
+Luke Pinner - Environment.gov.au, 2010 February 10
+'''
+
+#Imports use _syntax to mask them from autocomplete IDE's
+import ctypes as _ctypes
+from ctypes.wintypes import HWND as _HWND, HANDLE as _HANDLE,DWORD as _DWORD,LPCWSTR as _LPCWSTR,MAX_PATH as _MAX_PATH, create_unicode_buffer as _cub
+_SHGetFolderPath = _ctypes.windll.shell32.SHGetFolderPathW
+
+#public special folder constants
+DESKTOP= 0
+PROGRAMS= 2
+MYDOCUMENTS= 5
+FAVORITES= 6
+STARTUP= 7
+RECENT= 8
+SENDTO= 9
+STARTMENU= 11
+MYMUSIC= 13
+MYVIDEOS= 14
+NETHOOD= 19
+FONTS= 20
+TEMPLATES= 21
+ALLUSERSSTARTMENU= 22
+ALLUSERSPROGRAMS= 23
+ALLUSERSSTARTUP= 24
+ALLUSERSDESKTOP= 25
+APPLICATIONDATA= 26
+PRINTHOOD= 27
+LOCALSETTINGSAPPLICATIONDATA= 28
+ALLUSERSFAVORITES= 31
+LOCALSETTINGSTEMPORARYINTERNETFILES=32
+COOKIES= 33
+LOCALSETTINGSHISTORY= 34
+ALLUSERSAPPLICATIONDATA= 35
+
+def get(intFolder):
+ _SHGetFolderPath.argtypes = [_HWND, _ctypes.c_int, _HANDLE, _DWORD, _LPCWSTR]
+ auPathBuffer = _cub(_MAX_PATH)
+ exit_code=_SHGetFolderPath(0, intFolder, 0, 0, auPathBuffer)
+ return auPathBuffer.value
+
diff --git a/plugins/Trayicon/trayicon.ico b/plugins/Trayicon/trayicon.ico
new file mode 100644
index 00000000..fad67073
Binary files /dev/null and b/plugins/Trayicon/trayicon.ico differ
diff --git a/src/Connection/Connection.py b/src/Connection/Connection.py
index 55d67f7d..d3851526 100644
--- a/src/Connection/Connection.py
+++ b/src/Connection/Connection.py
@@ -162,6 +162,7 @@ class Connection:
self.last_recv_time = time.time()
self.incomplete_buff_recv += 1
self.bytes_recv += len(buff)
+ self.server.bytes_recv += len(buff)
if not self.unpacker:
self.unpacker = msgpack.Unpacker()
self.unpacker.feed(buff)
@@ -198,13 +199,19 @@ class Connection:
if config.debug_socket: self.log("Got handshake response: %s, ping: %s" % (message, ping))
self.last_ping_delay = ping
self.handshake = message
- self.port = message["fileserver_port"] # Set peer fileserver port
+ if self.handshake.get("port_opened", None) == False: # Not connectable
+ self.port = 0
+ else:
+ self.port = message["fileserver_port"] # Set peer fileserver port
else:
self.log("Unknown response: %s" % message)
elif message.get("cmd"): # Handhsake request
if message["cmd"] == "handshake":
self.handshake = message["params"]
- self.port = self.handshake["fileserver_port"] # Set peer fileserver port
+ if self.handshake.get("port_opened", None) == False: # Not connectable
+ self.port = 0
+ else:
+ self.port = self.handshake["fileserver_port"] # Set peer fileserver port
if config.debug_socket: self.log("Handshake request: %s" % message)
data = self.handshakeInfo()
data["cmd"] = "response"
@@ -242,6 +249,7 @@ class Connection:
else: # Normal connection
data = msgpack.packb(message)
self.bytes_sent += len(data)
+ self.server.bytes_sent += len(data)
self.sock.sendall(data)
self.last_sent_time = time.time()
return True
diff --git a/src/Connection/ConnectionServer.py b/src/Connection/ConnectionServer.py
index 862e2f72..77afa6a6 100644
--- a/src/Connection/ConnectionServer.py
+++ b/src/Connection/ConnectionServer.py
@@ -22,6 +22,9 @@ class ConnectionServer:
self.running = True
self.thread_checker = gevent.spawn(self.checkConnections)
+ self.bytes_recv = 0
+ self.bytes_sent = 0
+
self.zmq_running = False
self.zmq_last_connection = None # Last incoming message client
@@ -82,6 +85,8 @@ class ConnectionServer:
return connection
# No connection found
+ if port == 0:
+ raise Exception("This peer is not connectable")
try:
connection = Connection(self, ip, port)
self.ips[ip] = connection
diff --git a/src/File/FileRequest.py b/src/File/FileRequest.py
index b17d6b7b..efc7e82a 100644
--- a/src/File/FileRequest.py
+++ b/src/File/FileRequest.py
@@ -131,10 +131,10 @@ class FileRequest:
address = self.unpackAddress(peer)
got_peer_keys.append("%s:%s" % address)
if (site.addPeer(*address)): added += 1
- # Send back peers that is not in the sent list
+ # Send back peers that is not in the sent list and connectable (not port 0)
peers = site.peers.values()
random.shuffle(peers)
- packed_peers = [peer.packAddress() for peer in peers if peer.key not in got_peer_keys][0:params["need"]]
+ packed_peers = [peer.packAddress() for peer in peers if not peer.key.endswith(":0") and peer.key not in got_peer_keys][0:params["need"]]
if added:
self.log.debug("Added %s peers to %s using pex, sending back %s" % (added, site, len(packed_peers)))
self.response({"peers": packed_peers})
diff --git a/src/Peer/Peer.py b/src/Peer/Peer.py
index a1410e86..09b2ced1 100644
--- a/src/Peer/Peer.py
+++ b/src/Peer/Peer.py
@@ -150,7 +150,7 @@ class Peer:
if not site: site = self.site # If no site definied request peers for this site
peers = self.site.peers.values()
random.shuffle(peers)
- packed_peers = [peer.packAddress() for peer in peers][0:need_num]
+ packed_peers = [peer.packAddress() for peer in peers if not peer.key.endswith(":0")][0:need_num]
response = self.request("pex", {"site": site.address, "peers": packed_peers, "need": need_num})
if not response or "error" in response:
return False
diff --git a/src/Site/Site.py b/src/Site/Site.py
index 8f83850e..4666d021 100644
--- a/src/Site/Site.py
+++ b/src/Site/Site.py
@@ -319,11 +319,10 @@ class Site:
address_hash = hashlib.sha1(self.address).hexdigest()
my_peer_id = sys.modules["main"].file_server.peer_id
- # Later, if we have peer exchange
- """if sys.modules["main"].file_server.port_opened:
+ if sys.modules["main"].file_server.port_opened:
fileserver_port = config.fileserver_port
else: # Port not opened, report port 0
- fileserver_port = 0"""
+ fileserver_port = 0
fileserver_port = config.fileserver_port
s = time.time()
diff --git a/src/Ui/UiServer.py b/src/Ui/UiServer.py
index a16f3490..b6848e55 100644
--- a/src/Ui/UiServer.py
+++ b/src/Ui/UiServer.py
@@ -112,9 +112,11 @@ class UiServer:
def stop(self):
+ self.log.debug("Stopping...")
# Close WS sockets
- for client in self.server.clients.values():
- client.ws.close()
+ if "clients" in dir(self.server):
+ for client in self.server.clients.values():
+ client.ws.close()
# Close http sockets
sock_closed = 0
for sock in self.server.sockets.values():
diff --git a/src/main.py b/src/main.py
index e68548b7..1524bc41 100644
--- a/src/main.py
+++ b/src/main.py
@@ -35,11 +35,10 @@ logging.getLogger('').name = "-" # Remove root prefix
from Debug import DebugHook
if config.debug:
console_log.setLevel(logging.DEBUG) # Display everything to console
- from gevent import monkey; monkey.patch_all(thread=False) # thread=False because of pyfilesystem
else:
console_log.setLevel(logging.INFO) # Display only important info to console
- from gevent import monkey; monkey.patch_all() # Make time, thread, socket gevent compatible
+from gevent import monkey; monkey.patch_all(thread=False) # Make time, socket gevent compatible
import gevent
import time
@@ -64,195 +63,196 @@ PluginManager.plugin_manager.loadPlugins()
# -- Actions --
+@PluginManager.acceptPlugins
+class Actions:
+ # Default action: Start serving UiServer and FileServer
+ def main(self):
+ logging.info("Version: %s, Python %s, Gevent: %s" % (config.version, sys.version, gevent.__version__))
+ global ui_server, file_server
+ from File import FileServer
+ from Ui import UiServer
+ logging.info("Creating UiServer....")
+ ui_server = UiServer()
+
+ logging.info("Creating FileServer....")
+ file_server = FileServer()
+
+ logging.info("Starting servers....")
+ gevent.joinall([gevent.spawn(ui_server.start), gevent.spawn(file_server.start)])
+
+
+ # Site commands
+
+ def siteCreate(self):
+ logging.info("Generating new privatekey...")
+ from Crypt import CryptBitcoin
+ privatekey = CryptBitcoin.newPrivatekey()
+ logging.info("----------------------------------------------------------------------")
+ logging.info("Site private key: %s" % privatekey)
+ logging.info(" !!! ^ Save it now, required to modify the site ^ !!!")
+ address = CryptBitcoin.privatekeyToAddress(privatekey)
+ logging.info("Site address: %s" % address)
+ logging.info("----------------------------------------------------------------------")
+
+ while True:
+ if raw_input("? Have you secured your private key? (yes, no) > ").lower() == "yes": break
+ else: logging.info("Please, secure it now, you going to need it to modify your site!")
+
+ logging.info("Creating directory structure...")
+ from Site import Site
+ os.mkdir("data/%s" % address)
+ open("data/%s/index.html" % address, "w").write("Hello %s!" % address)
+
+ logging.info("Creating content.json...")
+ site = Site(address)
+ site.content_manager.sign(privatekey=privatekey)
+ site.settings["own"] = True
+ site.saveSettings()
+
+ logging.info("Site created!")
+
+
+ def siteSign(self, address, privatekey=None, inner_path="content.json"):
+ from Site import Site
+ logging.info("Signing site: %s..." % address)
+ site = Site(address, allow_create = False)
+
+ if not privatekey: # If no privatekey in args then ask it now
+ import getpass
+ privatekey = getpass.getpass("Private key (input hidden):")
+ site.content_manager.sign(inner_path=inner_path, privatekey=privatekey, update_changed_files=True)
+
+
+ def siteVerify(self, address):
+ from Site import Site
+ logging.info("Verifing site: %s..." % address)
+ site = Site(address)
+
+ for content_inner_path in site.content_manager.contents:
+ logging.info("Verifing %s signature..." % content_inner_path)
+ if site.content_manager.verifyFile(content_inner_path, site.storage.open(content_inner_path, "rb"), ignore_same=False) == True:
+ logging.info("[OK] %s signed by address %s!" % (content_inner_path, address))
+ else:
+ logging.error("[ERROR] %s not signed by address %s!" % (content_inner_path, address))
+
+ logging.info("Verifying site files...")
+ bad_files = site.storage.verifyFiles()
+ if not bad_files:
+ logging.info("[OK] All file sha512sum matches!")
+ else:
+ logging.error("[ERROR] Error during verifying site files!")
+
+
+ def dbRebuild(self, address):
+ from Site import Site
+ logging.info("Rebuilding site sql cache: %s..." % address)
+ site = Site(address)
+ s = time.time()
+ site.storage.rebuildDb()
+ logging.info("Done in %.3fs" % (time.time()-s))
+
+
+ def dbQuery(self, address, query):
+ from Site import Site
+ import json
+ site = Site(address)
+ result = []
+ for row in site.storage.query(query):
+ result.append(dict(row))
+ print json.dumps(result, indent=4)
+
+
+ def siteAnnounce(self, address):
+ from Site.Site import Site
+ logging.info("Announcing site %s to tracker..." % address)
+ site = Site(address)
+
+ s = time.time()
+ site.announce()
+ print "Response time: %.3fs" % (time.time()-s)
+ print site.peers
+
+
+ def siteNeedFile(self, address, inner_path):
+ from Site import Site
+ site = Site(address)
+ site.announce()
+ print site.needFile(inner_path, update=True)
+
+
+ def sitePublish(self, address, peer_ip=None, peer_port=15441, inner_path="content.json"):
+ global file_server
+ from Site import Site
+ from File import FileServer # We need fileserver to handle incoming file requests
+
+ logging.info("Creating FileServer....")
+ file_server = FileServer()
+ file_server_thread = gevent.spawn(file_server.start, check_sites=False) # Dont check every site integrity
+ file_server.openport()
+ if file_server.port_opened == False:
+ logging.info("Port not opened, passive publishing not supported yet :(")
+ return
+ site = file_server.sites[address]
+ site.settings["serving"] = True # Serving the site even if its disabled
+ if peer_ip: # Announce ip specificed
+ site.addPeer(peer_ip, peer_port)
+ else: # Just ask the tracker
+ logging.info("Gathering peers from tracker")
+ site.announce() # Gather peers
+ site.publish(20, inner_path) # Push to 20 peers
+ time.sleep(3)
+ logging.info("Serving files...")
+ gevent.joinall([file_server_thread])
+ logging.info("Done.")
+
+
+
+ # Crypto commands
+
+ def cryptoPrivatekeyToAddress(self, privatekey=None):
+ from Crypt import CryptBitcoin
+ if not privatekey: # If no privatekey in args then ask it now
+ import getpass
+ privatekey = getpass.getpass("Private key (input hidden):")
+
+ print CryptBitcoin.privatekeyToAddress(privatekey)
+
+
+ # Peer
+
+ def peerPing(self, peer_ip, peer_port):
+ logging.info("Opening a simple connection server")
+ global file_server
+ from Connection import ConnectionServer
+ file_server = ConnectionServer("127.0.0.1", 1234)
+
+ from Peer import Peer
+ logging.info("Pinging 5 times peer: %s:%s..." % (peer_ip, int(peer_port)))
+ peer = Peer(peer_ip, peer_port)
+ for i in range(5):
+ s = time.time()
+ print peer.ping(),
+ print "Response time: %.3fs" % (time.time()-s)
+ time.sleep(1)
+
+
+ def peerGetFile(self, peer_ip, peer_port, site, filename):
+ logging.info("Opening a simple connection server")
+ global file_server
+ from Connection import ConnectionServer
+ file_server = ConnectionServer()
+
+ from Peer import Peer
+ logging.info("Getting %s/%s from peer: %s:%s..." % (site, filename, peer_ip, peer_port))
+ peer = Peer(peer_ip, peer_port)
+ s = time.time()
+ print peer.getFile(site, filename).read()
+ print "Response time: %.3fs" % (time.time()-s)
+
+actions = Actions()
# Starts here when running zeronet.py
def start():
- action_func = globals()[config.action] # Function reference
- action_kwargs = config.getActionArguments() # non-config arguments when calling zeronet.py
-
- action_func(**action_kwargs)
-
-
-# Start serving UiServer and PeerServer
-def main():
- logging.info("Version: %s, Python %s, Gevent: %s" % (config.version, sys.version, gevent.__version__))
- global ui_server, file_server
- from File import FileServer
- from Ui import UiServer
- logging.info("Creating UiServer....")
- ui_server = UiServer()
-
- logging.info("Creating FileServer....")
- file_server = FileServer()
-
- logging.info("Starting servers....")
- gevent.joinall([gevent.spawn(ui_server.start), gevent.spawn(file_server.start)])
-
-
-# Site commands
-
-def siteCreate():
- logging.info("Generating new privatekey...")
- from Crypt import CryptBitcoin
- privatekey = CryptBitcoin.newPrivatekey()
- logging.info("----------------------------------------------------------------------")
- logging.info("Site private key: %s" % privatekey)
- logging.info(" !!! ^ Save it now, required to modify the site ^ !!!")
- address = CryptBitcoin.privatekeyToAddress(privatekey)
- logging.info("Site address: %s" % address)
- logging.info("----------------------------------------------------------------------")
-
- while True:
- if raw_input("? Have you secured your private key? (yes, no) > ").lower() == "yes": break
- else: logging.info("Please, secure it now, you going to need it to modify your site!")
-
- logging.info("Creating directory structure...")
- from Site import Site
- os.mkdir("data/%s" % address)
- open("data/%s/index.html" % address, "w").write("Hello %s!" % address)
-
- logging.info("Creating content.json...")
- site = Site(address)
- site.content_manager.sign(privatekey=privatekey)
- site.settings["own"] = True
- site.saveSettings()
-
- logging.info("Site created!")
-
-
-def siteSign(address, privatekey=None, inner_path="content.json"):
- from Site import Site
- logging.info("Signing site: %s..." % address)
- site = Site(address, allow_create = False)
-
- if not privatekey: # If no privatekey in args then ask it now
- import getpass
- privatekey = getpass.getpass("Private key (input hidden):")
- site.content_manager.sign(inner_path=inner_path, privatekey=privatekey, update_changed_files=True)
-
-
-def siteVerify(address):
- from Site import Site
- logging.info("Verifing site: %s..." % address)
- site = Site(address)
-
- for content_inner_path in site.content_manager.contents:
- logging.info("Verifing %s signature..." % content_inner_path)
- if site.content_manager.verifyFile(content_inner_path, site.storage.open(content_inner_path, "rb"), ignore_same=False) == True:
- logging.info("[OK] %s signed by address %s!" % (content_inner_path, address))
- else:
- logging.error("[ERROR] %s not signed by address %s!" % (content_inner_path, address))
-
- logging.info("Verifying site files...")
- bad_files = site.storage.verifyFiles()
- if not bad_files:
- logging.info("[OK] All file sha512sum matches!")
- else:
- logging.error("[ERROR] Error during verifying site files!")
-
-
-def dbRebuild(address):
- from Site import Site
- logging.info("Rebuilding site sql cache: %s..." % address)
- site = Site(address)
- s = time.time()
- site.storage.rebuildDb()
- logging.info("Done in %.3fs" % (time.time()-s))
-
-
-def dbQuery(address, query):
- from Site import Site
- import json
- site = Site(address)
- result = []
- for row in site.storage.query(query):
- result.append(dict(row))
- print json.dumps(result, indent=4)
-
-
-def siteAnnounce(address):
- from Site.Site import Site
- logging.info("Announcing site %s to tracker..." % address)
- site = Site(address)
-
- s = time.time()
- site.announce()
- print "Response time: %.3fs" % (time.time()-s)
- print site.peers
-
-
-def siteNeedFile(address, inner_path):
- from Site import Site
- site = Site(address)
- site.announce()
- print site.needFile(inner_path, update=True)
-
-
-def sitePublish(address, peer_ip=None, peer_port=15441, inner_path="content.json"):
- global file_server
- from Site import Site
- from File import FileServer # We need fileserver to handle incoming file requests
-
- logging.info("Creating FileServer....")
- file_server = FileServer()
- file_server_thread = gevent.spawn(file_server.start, check_sites=False) # Dont check every site integrity
- file_server.openport()
- if file_server.port_opened == False:
- logging.info("Port not opened, passive publishing not supported yet :(")
- return
- site = file_server.sites[address]
- site.settings["serving"] = True # Serving the site even if its disabled
- if peer_ip: # Announce ip specificed
- site.addPeer(peer_ip, peer_port)
- else: # Just ask the tracker
- logging.info("Gathering peers from tracker")
- site.announce() # Gather peers
- site.publish(20, inner_path) # Push to 20 peers
- time.sleep(3)
- logging.info("Serving files...")
- gevent.joinall([file_server_thread])
- logging.info("Done.")
-
-
-
-# Crypto commands
-
-def cryptoPrivatekeyToAddress(privatekey=None):
- from Crypt import CryptBitcoin
- if not privatekey: # If no privatekey in args then ask it now
- import getpass
- privatekey = getpass.getpass("Private key (input hidden):")
-
- print CryptBitcoin.privatekeyToAddress(privatekey)
-
-
-# Peer
-
-def peerPing(peer_ip, peer_port):
- logging.info("Opening a simple connection server")
- global file_server
- from Connection import ConnectionServer
- file_server = ConnectionServer("127.0.0.1", 1234)
-
- from Peer import Peer
- logging.info("Pinging 5 times peer: %s:%s..." % (peer_ip, int(peer_port)))
- peer = Peer(peer_ip, peer_port)
- for i in range(5):
- s = time.time()
- print peer.ping(),
- print "Response time: %.3fs" % (time.time()-s)
- time.sleep(1)
-
-
-def peerGetFile(peer_ip, peer_port, site, filename):
- logging.info("Opening a simple connection server")
- global file_server
- from Connection import ConnectionServer
- file_server = ConnectionServer()
-
- from Peer import Peer
- logging.info("Getting %s/%s from peer: %s:%s..." % (site, filename, peer_ip, peer_port))
- peer = Peer(peer_ip, peer_port)
- s = time.time()
- print peer.getFile(site, filename).read()
- print "Response time: %.3fs" % (time.time()-s)
-
+ # Call function
+ func = getattr(actions, config.action, None)
+ action_kwargs = config.getActionArguments()
+ func(**action_kwargs)