pubsubclient: 728ff4b8ebf68255e5bcddd9d00e05b720806c74
1: #!/usr/bin/env python
2:
3: # Import what we need
4: import xmpp
5: import sys
6: import os
7: import time
8: import pubsubclient
9: from pubsubclient import Node
10: import lxml.etree as etree
11: from random import Random
12: import string
13: from lxml.etree import ElementTree, Element, SubElement
14: import pygtk
15: import gtk
16: import gobject
17: #import webkit
18: import feedparser
19: from StringIO import StringIO
20: from kiwi.ui.objectlist import Column, ObjectTree, ObjectList
21: import xdg.BaseDirectory
22:
23: class LeftRow:
24: """This is a row on the left-hand pane."""
25:
26: def __init__(self, name, count, unread):
27: """name is the name to display, count is the number of items,
28: unread is the number of unread items."""
29: self.name = name
30: self.count = count
31: self.unread = unread
32:
33: class FoundRow:
34: """This is a row in the window which finds and subscribes new nodes."""
35:
36: def __init__(self, name, node):
37: """name is the name to display, node is the path of the node on
38: the server."""
39: self.name = name
40: self.node = node
41:
42: class Display:
43: """This is the main window class."""
44:
45: def __init__(self, cache_directory, username, password):
46: """cache_directory is the directory to store downloaded data in,
47: username is the JID to login with, password is the password used
48: to log in."""
49: # Make a connection
50: self.jid = username
51: self.password = password
52: self.client = pubsubclient.PubSubClient(self.jid, self.password)
53:
54: # Make the window
55: self.draw_window()
56:
57: def draw_window(self):
58: """Display related things."""
59:
60: # Make a window and split it into sections
61: self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
62: self.hpaned = gtk.HPaned()
63: self.window.add(self.hpaned)
64: self.right_vpaned = gtk.VPaned()
65: self.hpaned.pack2(self.right_vpaned)
66: self.left_vbox = gtk.VBox()
67: self.hpaned.pack1(self.left_vbox)
68:
69: # Make a Kiwi ObjectList with columns "name" and "count" for
70: # subscription names and the
71: name_column = Column('name', 'Name')
72: name_column.expand = True
73: unread_column = Column('unread', 'Unread')
74: count_column = Column('count', 'Total')
75: self.node_list = ObjectList([name_column, unread_column, count_column])
76: self.left_vbox.pack_start(self.node_list)
77:
78: self.add_button = gtk.Button(stock=gtk.STOCK_ADD)
79: self.add_button.set_label("New Subscription")
80: self.add_button.connect("released", self.add_released)
81: self.left_vbox.pack_end(self.add_button, expand=False)
82:
83: title_column = Column('title')
84: author_column = Column('author')
85: date_column = Column('date')
86: title_column.expand = True
87: self.entry_list = ObjectList([title_column, author_column, date_column])
88: self.right_vpaned.pack1(self.entry_list)
89:
90: #self.webscroll = gtk.ScrolledWindow()
91: #self.webview = webkit.WebView()
92: #self.webscroll.add(self.webview)
93: #self.right_vpaned.pack2(self.webscroll)
94:
95: self.window.show_all()
96:
97: def disable_subscribe_button(self, args):
98: """Makes the subscribe button in the add-new-subscription
99: dialogue insensitive."""
100: self.add_window['add_button'].set_sensitive(False)
101:
102: def subscribe_to_node(self, args):
103: """Subscribes the logged in account to the node selected in the
104: add-new-subscription dialogue."""
105: self.add_window['node_list'].get_selected().subscribe(self.client, self.jid, return_function=self.subscription_finished)
106:
107: def subscription_finished(self, reply):
108: """Handles replies to subscription requests."""
109: print 'Reply received"'
110: if reply.find(".//error") is not None:
111: print "Error subscribing"
112: elif reply.find(".//result") is not None:
113: print "Subscription successful!"
114: # Close the new-subscription dialogue
115: self.add_window['window'].destroy()
116: # Add the subscribed-to node to the list of subscriptions
117: self.node_list.append(LeftRow("test", 0, 0))
118:
119: def add_released(self, args):
120: """Run when the "Subscribe" button is pressed."""
121: # Make a place to store all of the new GTK stuff
122: self.add_window = {}
123:
124: # Make the Add Node window and put it in there
125: self.add_window['window'] = gtk.Window()
126: self.add_window['window'].set_title("Add New Subscription")
127:
128: # Split it vertically
129: self.add_window['vbox'] = gtk.VBox()
130: self.add_window['window'].add(self.add_window['vbox'])
131:
132: # Split the top horizontally
133: self.add_window['top_hbox'] = gtk.HBox()
134: self.add_window['vbox'].pack_start(self.add_window['top_hbox'], expand=False)
135:
136: # Add the "Location:" label to the top
137: self.add_window['location_label'] = gtk.Label("Location: ")
138: self.add_window['top_hbox'].pack_start(self.add_window['location_label'], expand=False)
139:
140: # Add the "Find" button to the top
141: self.add_window['find_button'] = gtk.Button(label="Find", stock=gtk.STOCK_FIND)
142: self.add_window['find_button'].connect("released", self.find_new_nodes)
143: self.add_window['top_hbox'].pack_end(self.add_window['find_button'], expand=False)
144:
145: # Add the location entry box in between
146: self.add_window['location_entry'] = gtk.Entry()
147: self.add_window['top_hbox'].pack_end(self.add_window['location_entry'], expand=True)
148:
149: self.add_window['bottom_hbox'] = gtk.HBox()
150: self.add_window['vbox'].pack_end(self.add_window['bottom_hbox'], expand=False)
151:
152: self.add_window['find_progress'] = gtk.ProgressBar()
153: self.add_window['bottom_hbox'].pack_end(self.add_window['find_progress'], expand=True)
154:
155: self.add_window['add_button'] = gtk.Button(label="Subscribe", stock=gtk.STOCK_ADD)
156: self.add_window['add_button'].set_label("Subscribe")
157: ## FIXME: The Subscribe button should be insensitive to start
158: ## with, then activate when a node is selected.
159: #self.add_window['add_button'].set_sensitive(False)
160: self.add_window['add_button'].connect("released", self.subscribe_to_node)
161: self.add_window['bottom_hbox'].pack_end(self.add_window['add_button'], expand=False)
162:
163: # Add the list of found nodes
164: self.add_window['name_column'] = Column('name', 'Name')
165: self.add_window['location_column'] = Column('name', 'Location')
166: self.add_window['name_column'].expand = True
167: self.add_window['location_column'].expand = True
168: self.add_window['node_list'] = ObjectList([self.add_window['name_column'], self.add_window['location_column']])
169: self.add_window['vbox'].pack_end(self.add_window['node_list'], expand=True, fill=True)
170:
171: # Display everything
172: self.add_window['window'].show_all()
173:
174: def pulse_find_progress(self):
175: """Updates the progress bar when finding new nodes."""
176: # Sleep here so that we aren't checking for replies constantly
177: time.sleep(0.05)
178: # Do the pulsing
179: self.add_window['find_progress'].pulse()
180: # Return whether we're still searching
181: return self.finding
182:
183: def find_new_nodes(self, arg):
184: """Search for nodes at the server entered in the add-new-subscription window."""
185: self.add_window["entered_server"] = pubsubclient.Server(name=self.add_window['location_entry'].get_text())
186: self.client.get_nodes(self.add_window["entered_server"], None, return_function=self.found_new_nodes)
187: # Remember that we're still on the lookout for nodes
188: self.finding = True
189: gobject.idle_add(self.pulse_find_progress)
190:
191: def found_new_nodes(self, reply):
192: """The program waits until this is run. Could be more elegant
193: using a signal though"""
194: # We don't need to search any more
195: self.finding = False
196: # Run the following if we end up with a failure
197: if reply == "error":
198: # Reset the search progress
199: self.add_window['find_progress'].set_fraction(0.0)
200: # Tell the user we've failed to find anything due to an error
201: self.add_window['find_progress'].set_text('Error finding nodes at ' + self.add_window['location_entry'].get_text())
202: # Run the following if we end up with a success
203: else:
204: # Traverse the nodes we've received
205: for node in reply:
206: # Add each one to the add-subscription-dialogue's list
207: self.add_window['node_list'].append(node)
208: # Get any children of the discovered nodes
209: node.get_sub_nodes(self.client, self.found_new_nodes)
210: # We're now finding for children, so keep up the search
211: self.finding = True
212:
213: # Display the new window contents
214: self.add_window['window'].show_all()
215:
216: def connect(self):
217: """Connect the account given during initialisation."""
218: # Try to connect
219: connected = self.client.connect()
220: # Check if we succeeded
221: if connected == 1:
222: print 'Failed to connect, exiting'
223: sys.exit(1)
224: self.connected = True
225: # Process new messages when there's nothing else to do
226: gobject.idle_add(self.idle_process)
227: self.client.retrieve_subscriptions(self.jid.getDomain(), return_function=self.subscriptions_received)
228:
229: def subscriptions_received(self, subscriptions):
230: """Handles replies to retrieve_subscriptions."""
231:
232:
233: def add_nodes(self, nodes):
234: """Adds the given nodes to the subscribed list."""
235: for node in nodes:
236: if node not in self.node_list:
237: self.node_list.append(LeftRow(node.name, 0, 0))
238:
239: def handle_incoming(self, stanza):
240: #os.popen("mkdir -p pages/")
241: #while True:
242: # filename = ''.join(Random().sample(string.letters+string.digits, 16))
243: # if filename not in os.listdir("pages"): break
244: #page_file = open("pages/" + filename, 'w')
245: #page = Element('html')
246: #head = SubElement(page, 'head')
247: #css_link = SubElement(head, 'link', attrib={"rel":"stylesheet", "type":"text/css", "href":os.getcwd() + "/page_settings/test1/item.css"})
248: #body = SubElement(page, 'body')
249: #wholediv = SubElement(body, 'div', attrib={'class':'whole'})
250: #titlediv = SubElement(wholediv, 'div', attrib={"class":"title"})
251: #maindiv = SubElement(wholediv, 'div', attrib={"class":"main"})
252: #for event in stanza.xpath("//e:event", namespaces={"e":"http://jabber.org/protocol/pubsub#event"}):
253: # for item_node in event.xpath("//i:items", namespaces={"i":"http://jabber.org/protocol/pubsub#event"}):
254: # title = SubElement(titlediv, 'a')
255: # title.text = "TITLE: " + item_node.get("node")
256: # body_text = SubElement(maindiv, 'a')
257: # body_text.text = "MAIN: " + item_node.get("node")
258: #page_file.write(etree.tostring(page))
259: #page_file.close()
260: #self.webview.open(os.getcwd() + "/pages/" + filename)
261: pass
262:
263: def process(self):
264: """Handle any pending XMPP events."""
265: self.client.process()
266:
267: def idle_process(self):
268: """Check for new messages. Good to run in gobject idle time."""
269: # It's a good idea to wait a bit betweeen updates, to stop
270: # overworking the machine
271: time.sleep(0.1)
272: # Do the processing
273: self.process()
274: # Idle functions stop being called when they return False, in
275: # this case when we disconnect
276: return self.connected
277:
278: def destroy(self, widget, data=None):
279: """Close the program"""
280: gtk.main_quit()
281:
282: def main(self):
283: """Initialises and starts the GTK main loop."""
284: #gobject.idle_add(self.idle_process)
285: self.window.connect("destroy", self.destroy)
286: self.client.assign_message_handler(self.handle_incoming)
287: gtk.main()
288:
289: if __name__ == '__main__':
290: # Check for a writable cache folder, if none is found then make one
291: if not 'pubsubclient' in os.listdir(xdg.BaseDirectory.xdg_cache_home):
292: try:
293: os.mkdir(xdg.BaseDirectory.xdg_cache_home + '/pubsubclient')
294: except:
295: print 'Error: Could not create cache directory ' + xdg.BaseDirectory.xdg_cache_home + '/pubsubclient'
296: # Check for a configuration folder, if none is found then make one
297: if not 'pubsubclient' in os.listdir(xdg.BaseDirectory.xdg_config_dirs[0]):
298: try:
299: os.mkdir(xdg.BaseDirectory.xdg_config_dirs[0] + '/pubsubclient')
300: except:
301: print 'Error: Could not create config directory ' + xdg.BaseDirectory.xdg_config_dirs[0] + '/pubsubclient'
302: # Check for a configuration file, if none is found then make one
303: if not 'login' in os.listdir(xdg.BaseDirectory.xdg_config_dirs[0] + '/pubsubclient'):
304: id = raw_input("Please enter your Jabber ID: ")
305: password = raw_input("Please enter your password: ")
306: else:
307: try:
308: login_file = open(xdg.BaseDirectory.xdg_config_dirs[0] + '/pubsubclient/login', 'r')
309: for line in login_file.readlines():
310: if line[:4] == 'jid:':
311: id = line[4:].strip()
312: print "Found jid " + id
313: elif line[:5] == 'pass:':
314: password = line[5:].strip()
315: print "Found password " + password
316: except:
317: print 'Error: Could not read login details from '+ xdg.BaseDirectory.xdg_config_dirs[0] + '/pubsubclient/login'
318: id = raw_input("Please enter your Jabber ID: ")
319: password = raw_input("Please enter your password: ")
320: # Make a main window
321: display = Display(xdg.BaseDirectory.xdg_cache_home + '/pubsubclient', id, password)
322:
323: #display.webview.open(os.getcwd() + "/start.html")
324:
325: # Log on to XMPP
326: display.connect()
327:
328: #display.client.subscribe('pubsub.localhost', '/home')
329: #display.populate('pubsub.localhost')
330:
331: # Run the program
332: display.main()
Generated by git2html.