Mercurial > libervia-web
view libervia.py @ 215:e830a0c60d32
server side: added the security_limit to setParam
- in addition to the check which is done by the core, libervia checks if the param to be modified was really part of the XML that has been returned by getParams with security_limit = 0.
author | souliane <souliane@mailoo.org> |
---|---|
date | Sat, 07 Sep 2013 02:07:07 +0200 |
parents | 8bbac49765d6 |
children | 4e6467efd6bf |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- """ Libervia: a Salut à Toi frontend Copyright (C) 2011, 2012, 2013 Jérôme Poisson <goffi@goffi.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ import pyjd # this is dummy in pyjs from pyjamas.ui.RootPanel import RootPanel from pyjamas.ui.HTML import HTML from pyjamas.ui.KeyboardListener import KEY_ESCAPE from pyjamas.Timer import Timer from pyjamas import Window, DOM from pyjamas.JSONService import JSONProxy from browser_side.register import RegisterBox from browser_side.contact import ContactPanel from browser_side.base_widget import WidgetsPanel from browser_side.panels import MicroblogItem from browser_side import panels, dialog from browser_side.jid import JID from browser_side.tools import html_sanitize MAX_MBLOG_CACHE = 500 #Max microblog entries kept in memories class LiberviaJsonProxy(JSONProxy): def __init__(self, *args, **kwargs): JSONProxy.__init__(self, *args, **kwargs) self.handler=self self.cb={} self.eb={} def call(self, method, cb, *args): _id = self.callMethod(method,args) if cb: if isinstance(cb, tuple): if len(cb) != 2: print ("ERROR: tuple syntax for bridge.call is (callback, errback), aborting") return self.cb[_id] = cb[0] self.eb[_id] = cb[1] else: self.cb[_id] = cb def onRemoteResponse(self, response, request_info): if request_info.id in self.cb: _cb = self.cb[request_info.id] # if isinstance(_cb, tuple): # #we have arguments attached to the callback # #we send them after the answer # callback, args = _cb # callback(response, *args) # else: # #No additional argument, we call directly the callback _cb(response) del self.cb[request_info.id] if request_info.id in self.eb: del self.eb[request_info.id] def onRemoteError(self, code, errobj, request_info): """def dump(obj): print "\n\nDUMPING %s\n\n" % obj for i in dir(obj): print "%s: %s" % (i, getattr(obj,i))""" if request_info.id in self.eb: _eb = self.eb[request_info.id] _eb((code, errobj)) del self.cb[request_info.id] del self.eb[request_info.id] else: if code != 0: print ("Internal server error") """for o in code, error, request_info: dump(o)""" else: if isinstance(errobj['message'],dict): print("Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString'])) else: print("Error: %s" % errobj['message']) class RegisterCall(LiberviaJsonProxy): def __init__(self): LiberviaJsonProxy.__init__(self, "/register_api", ["isRegistered","isConnected","connect"]) class BridgeCall(LiberviaJsonProxy): def __init__(self): LiberviaJsonProxy.__init__(self, "/json_api", ["getContacts", "addContact", "sendMessage", "sendMblog", "sendMblogComment", "getLastMblogs", "getMassiveLastMblogs", "getMblogComments", "getProfileJid", "getHistory", "getPresenceStatus", "joinMUC", "mucLeave", "getRoomsJoined", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady", "tarotGameContratChoosed", "tarotGamePlayCards", "launchRadioCollective", "getWaitingSub", "subscription", "delContact", "updateContact", "getCard", "getEntityData", "getParamsUI", "setParam", "launchAction", "disconnect", "chatStateComposing" ]) class BridgeSignals(LiberviaJsonProxy): RETRY_BASE_DELAY = 1000 def __init__(self, host): self.host = host self.retry_delay = self.RETRY_BASE_DELAY LiberviaJsonProxy.__init__(self, "/json_signal_api", ["getSignals"]) def onRemoteResponse(self, response, request_info): self.retry_delay = self.RETRY_BASE_DELAY LiberviaJsonProxy.onRemoteResponse(self, response, request_info) def onRemoteError(self, code, errobj, request_info): if errobj['message'] == 'Empty Response': Window.getLocation().reload() # XXX: reset page in case of session ended. # FIXME: Should be done more properly without hard reload LiberviaJsonProxy.onRemoteError(self, code, errobj, request_info) #we now try to reconnect if isinstance(errobj['message'],dict) and errobj['message']['faultCode']==0: Window.alert('You are not allowed to connect to server') else: def _timerCb(): self.host.bridge_signals.call('getSignals', self.host._getSignalsCB) Timer(notify=_timerCb).schedule(self.retry_delay) self.retry_delay *= 2 class SatWebFrontend: def onModuleLoad(self): print "============ onModuleLoad ==============" panels.ChatPanel.registerClass() panels.MicroblogPanel.registerClass() self.whoami = None self._selected_listeners = set() self.bridge = BridgeCall() self.bridge_signals = BridgeSignals(self) self.uni_box = None self.status_panel = panels.StatusPanel(self) self.contact_panel = ContactPanel(self) self.panel = panels.MainPanel(self) self.discuss_panel = self.panel.discuss_panel self.tab_panel = self.panel.tab_panel self.tab_panel.addTabListener(self) self.libervia_widgets = set() #keep track of all actives LiberviaWidgets self.room_list = set() #set of rooms self.mblog_cache = [] #used to keep our own blog entries in memory, to show them in new mblog panel self.avatars_cache = {} #keep track of jid's avatar hash (key=jid, value=file) self.current_action_ids = set() #self.discuss_panel.addWidget(panels.EmptyPanel(self)) self.discuss_panel.addWidget(panels.MicroblogPanel(self, [])) #self.discuss_panel.addWidget(panels.EmptyPanel(self)) self._register_box = None RootPanel().add(self.panel) DOM.addEventPreview(self) self.resize() self._register = RegisterCall() self._register.call('isRegistered',self._isRegisteredCB) self.initialised = False self.init_cache = [] # used to cache events until initialisation is done def addSelectedListener(self, callback): self._selected_listeners.add(callback) def getSelected(self): wid = self.tab_panel.getCurrentPanel() if not isinstance(wid, WidgetsPanel): print "ERROR: Tab widget is not a WidgetsPanel, can't get selected widget" return None return wid.selected def setSelected(self, widget): """Define the selected widget""" widgets_panel = self.tab_panel.getCurrentPanel() if not isinstance(widgets_panel, WidgetsPanel): return selected = widgets_panel.selected if selected == widget: return if selected: selected.removeStyleName('selected_widget') widgets_panel.selected = widget if widget: widgets_panel.selected.addStyleName('selected_widget') for callback in self._selected_listeners: callback(widget) def resize(self): """Resize elements""" Window.onResize() def onBeforeTabSelected(self, sender, tab_index): return True def onTabSelected(self, sender, tab_index): selected = self.getSelected() for callback in self._selected_listeners: callback(selected) def onEventPreview(self, event): if event.type in ["keydown", "keypress", "keyup"] and event.keyCode == KEY_ESCAPE: #needed to prevent request cancellation in Firefox event.preventDefault() return True def getAvatar(self, jid_str): """Return avatar of a jid if in cache, else ask for it""" def dataReceived(result): if 'avatar' in result: self._entityDataUpdatedCb(jid_str, 'avatar', result['avatar']) else: self.bridge.call("getCard", None, jid_str) def avatarError(error_data): # The jid is maybe not in our roster, we ask for the VCard self.bridge.call("getCard", None, jid_str) if jid_str not in self.avatars_cache: self.bridge.call('getEntityData', (dataReceived, avatarError), jid_str, ['avatar']) self.avatars_cache[jid_str] = "/media/misc/empty_avatar" return self.avatars_cache[jid_str] def registerWidget(self, wid): print "Registering", wid self.libervia_widgets.add(wid) def unregisterWidget(self, wid): try: self.libervia_widgets.remove(wid) except KeyError: print ('WARNING: trying to remove a non registered Widget:', wid) def setUniBox(self, unibox): """register the unibox widget""" self.uni_box = unibox self.uni_box.addKey("@@: ") def addTab(self, wid, label): """Create a new tab and add a widget in @param wid: LiberviaWidget to add @param label: label of the tab""" _widgets_panel = WidgetsPanel(self) _widgets_panel.addWidget(wid) self.tab_panel.add(_widgets_panel, label) self.tab_panel.selectTab(self.tab_panel.getWidgetCount() - 1) def addWidget(self, wid): """ Add a widget at the bottom of the current tab @param wid: LiberviaWidget to add """ panel = self.tab_panel.getCurrentPanel() panel.addWidget(wid) def _isRegisteredCB(self, registered): if not registered: self._register_box = RegisterBox(self.logged) self._register_box.centerBox() self._register_box.show() else: self._register.call('isConnected', self._isConnectedCB) def _isConnectedCB(self, connected): if not connected: self._register.call('connect',lambda x:self.logged()) else: self.logged() def logged(self): if self._register_box: self._register_box.hide() del self._register_box # don't work if self._register_box is None #it's time to fill the page self.bridge.call('getContacts', self._getContactsCB) self.bridge_signals.call('getSignals', self._getSignalsCB) #We want to know our own jid self.bridge.call('getProfileJid', self._getProfileJidCB) def _getContactsCB(self, contacts_data): for contact in contacts_data: jid, attributes, groups = contact self._newContactCb(jid, attributes, groups) def _getSignalsCB(self, signal_data): self.bridge_signals.call('getSignals', self._getSignalsCB) print('Got signal ==> name: %s, params: %s' % (signal_data[0],signal_data[1])) name,args = signal_data if name == 'personalEvent': self._personalEventCb(*args) elif name == 'newMessage': self._newMessageCb(*args) elif name == 'presenceUpdate': self._presenceUpdateCb(*args) elif name == 'roomJoined': self._roomJoinedCb(*args) elif name == 'roomUserJoined': self._roomUserJoinedCb(*args) elif name == 'roomUserLeft': self._roomUserLeftCb(*args) elif name == 'tarotGameStarted': self._tarotGameStartedCb(*args) elif name == 'tarotGameNew' or \ name == 'tarotGameChooseContrat' or \ name == 'tarotGameShowCards' or \ name == 'tarotGameInvalidCards' or \ name == 'tarotGameCardsPlayed' or \ name == 'tarotGameYourTurn' or \ name == 'tarotGameScore': self._tarotGameGenericCb(name, args[0], args[1:]) elif name == 'radiocolStarted': self._radioColStartedCb(*args) elif name == 'radiocolPreload': self._radioColGenericCb(name, args[0], args[1:]) elif name == 'radiocolPlay': self._radioColGenericCb(name, args[0], args[1:]) elif name == 'radiocolNoUpload': self._radioColGenericCb(name, args[0], args[1:]) elif name == 'radiocolUploadOk': self._radioColGenericCb(name, args[0], args[1:]) elif name == 'radiocolSongRejected': self._radioColGenericCb(name, args[0], args[1:]) elif name == 'subscribe': self._subscribeCb(*args) elif name == 'contactDeleted': self._contactDeletedCb(*args) elif name == 'newContact': self._newContactCb(*args) elif name == 'entityDataUpdated': self._entityDataUpdatedCb(*args) elif name == 'chatStateReceived': self._chatStateReceivedCb(*args) def _ownBlogsFills(self, mblogs): #put our own microblogs in cache, then fill all panels with them for publisher in mblogs: for mblog in mblogs[publisher]: if not mblog.has_key('content'): print ("WARNING: No content found in microblog [%s]", mblog) continue if mblog.has_key('groups'): _groups = set(mblog['groups'].split() if mblog['groups'] else []) else: _groups=None mblog_entry = MicroblogItem(mblog) self.mblog_cache.append((_groups, mblog_entry)) if len(self.mblog_cache) > MAX_MBLOG_CACHE: del self.mblog_cache[0:len(self.mblog_cache-MAX_MBLOG_CACHE)] for lib_wid in self.libervia_widgets: if isinstance(lib_wid, panels.MicroblogPanel): self.FillMicroblogPanel(lib_wid) self.initialised = True # initialisation phase is finished here for event_data in self.init_cache: # so we have to send all the cached events self._personalEventCb(*event_data) del self.init_cache def _getProfileJidCB(self, jid): self.whoami = JID(jid) #we can now ask our status self.bridge.call('getPresenceStatus', self._getPresenceStatusCb) #the rooms where we are self.bridge.call('getRoomsJoined', self._getRoomsJoinedCb) #and if there is any subscription request waiting for us self.bridge.call('getWaitingSub', self._getWaitingSubCb) #we fill the panels already here for lib_wid in self.libervia_widgets: if isinstance(lib_wid, panels.MicroblogPanel): if lib_wid.accept_all(): self.bridge.call('getMassiveLastMblogs', lib_wid.massiveInsert, 'ALL', [], 10) else: self.bridge.call('getMassiveLastMblogs', lib_wid.massiveInsert, 'GROUP', lib_wid.accepted_groups, 10) #we ask for our own microblogs: self.bridge.call('getMassiveLastMblogs', self._ownBlogsFills, 'JID', [self.whoami.bare], 10) ## Signals callbacks ## def _personalEventCb(self, sender, event_type, data): if not self.initialised: self.init_cache.append((sender, event_type, data)) return if event_type == "MICROBLOG": if not 'content' in data: print ("WARNING: No content found in microblog data") return if 'groups' in data: _groups = set(data['groups'].split() if data['groups'] else []) else: _groups=None mblog_entry = MicroblogItem(data) for lib_wid in self.libervia_widgets: if isinstance(lib_wid, panels.MicroblogPanel): self.addBlogEntry(lib_wid, sender, _groups, mblog_entry) if sender == self.whoami.bare: self.mblog_cache.append((_groups, mblog_entry)) if len(self.mblog_cache) > MAX_MBLOG_CACHE: del self.mblog_cache[0:len(self.mblog_cache-MAX_MBLOG_CACHE)] def addBlogEntry(self, mblog_panel, sender, _groups, mblog_entry): """Check if an entry can go in MicroblogPanel and add to it @param mblog_panel: MicroblogPanel instance @param sender: jid of the entry sender @param _groups: groups which can receive this entry @param mblog_entry: MicroblogItem instance""" if mblog_entry.type == "comment" or mblog_panel.isJidAccepted(sender) or (_groups == None and self.whoami and sender == self.whoami.bare) \ or (_groups and _groups.intersection(mblog_panel.accepted_groups)): mblog_panel.addEntry(mblog_entry) def FillMicroblogPanel(self, mblog_panel): """Fill a microblog panel with entries in cache @param mblog_panel: MicroblogPanel instance """ #XXX: only our own entries are cached for cache_entry in self.mblog_cache: _groups, mblog_entry = cache_entry self.addBlogEntry(mblog_panel, self.whoami.bare, *cache_entry) def getEntityMBlog(self, entity): print "geting mblog for entity [%s]" % (entity,) for lib_wid in self.libervia_widgets: if isinstance(lib_wid, panels.MicroblogPanel): if lib_wid.isJidAccepted(entity): self.bridge.call('getMassiveLastMblogs', lib_wid.massiveInsert, 'JID', [entity], 10) def _newMessageCb(self, from_jid, msg, msg_type, to_jid): _from = JID(from_jid) _to = JID(to_jid) showed = False for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and (lib_wid.target.bare == _from.bare or lib_wid.target.bare == _to.bare): lib_wid.printMessage(_from, msg) showed = True if not showed: #The message has not been showed, we must indicate it other = _to if _from.bare == self.whoami.bare else _from self.contact_panel.setContactMessageWaiting(other.bare, True) def _presenceUpdateCb(self, entity, show, priority, statuses): _entity = JID(entity) #XXX: QnD way to get our status if self.whoami and self.whoami.bare == _entity.bare and statuses: self.status_panel.changeStatus(statuses.values()[0]) if (not self.whoami or self.whoami.bare != _entity.bare): self.contact_panel.setConnected(_entity.bare, _entity.resource, show, priority, statuses) def _roomJoinedCb(self, room_jid, room_nicks, user_nick): _target = JID(room_jid) self.room_list.add(_target) chat_panel = panels.ChatPanel(self, _target, type_='group') chat_panel.setUserNick(user_nick) if _target.node.startswith('sat_tarot_'): #XXX: it's not really beautiful, but it works :) self.addTab(chat_panel, "Tarot") elif _target.node.startswith('sat_radiocol_'): self.addTab(chat_panel, "Radio collective") else: self.addTab(chat_panel, _target.node) chat_panel.setPresents(room_nicks) chat_panel.historyPrint() def _roomUserJoinedCb(self, room_jid_s, user_nick, user_data): for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and lib_wid.type == 'group' and lib_wid.target.bare == room_jid_s: lib_wid.userJoined(user_nick, user_data) def _roomUserLeftCb(self, room_jid_s, user_nick, user_data): for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and lib_wid.type == 'group' and lib_wid.target.bare == room_jid_s: lib_wid.userLeft(user_nick, user_data) def _tarotGameStartedCb(self, room_jid_s, referee, players): print ("Tarot Game Started \o/") for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and lib_wid.type == 'group' and lib_wid.target.bare == room_jid_s: lib_wid.startGame("Tarot", referee, players) def _tarotGameGenericCb(self, event_name, room_jid_s, args): for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and lib_wid.type == 'group' and lib_wid.target.bare == room_jid_s: getattr(lib_wid.getGame("Tarot"), event_name)(*args) def _radioColStartedCb(self, room_jid_s, referee): for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and lib_wid.type == 'group' and lib_wid.target.bare == room_jid_s: lib_wid.startGame("RadioCol", referee) def _radioColGenericCb(self, event_name, room_jid_s, args): for lib_wid in self.libervia_widgets: if isinstance(lib_wid,panels.ChatPanel) and lib_wid.type == 'group' and lib_wid.target.bare == room_jid_s: getattr(lib_wid.getGame("RadioCol"), event_name)(*args) def _getPresenceStatusCb(self, presence_data): for entity in presence_data: for resource in presence_data[entity]: args = presence_data[entity][resource] self._presenceUpdateCb("%s/%s" % (entity, resource), *args) def _getRoomsJoinedCb(self, room_data): for room in room_data: self._roomJoinedCb(*room) def _getWaitingSubCb(self, waiting_sub): for sub in waiting_sub: self._subscribeCb(waiting_sub[sub], sub) def _subscribeCb(self, sub_type, entity): if sub_type == 'subscribed': dialog.InfoDialog('Subscription confirmation', 'The contact <b>%s</b> has added you to his/her contact list' % html_sanitize(entity)).show() self.getEntityMBlog(entity) elif sub_type == 'unsubscribed': dialog.InfoDialog('Subscription refusal', 'The contact <b>%s</b> has refused to add you in his/her contact list' % html_sanitize(entity)).show() #TODO: remove microblogs from panels elif sub_type == 'subscribe': #The user want to subscribe to our presence _dialog = None msg = HTML('The contact <b>%s</b> want to add you in his/her contact list, do you accept ?' % html_sanitize(entity)) def ok_cb(ignore): self.bridge.call('subscription', None, "subscribed", entity, '', _dialog.getSelectedGroups()) def cancel_cb(ignore): self.bridge.call('subscription', None, "unsubscribed", entity, '', '') _dialog = dialog.GroupSelector([msg], self.contact_panel.getGroups(), [], ok_cb, cancel_cb) _dialog.setHTML('<b>Add contact request</b>') _dialog.show() def _contactDeletedCb(self, entity): self.contact_panel.removeContact(entity) def _newContactCb(self, contact, attributes, groups): self.contact_panel.updateContact(contact, attributes, groups) def _entityDataUpdatedCb(self, entity_jid_s, key, value): if key == "avatar": avatar = '/avatars/%s' % value self.avatars_cache[entity_jid_s] = avatar for lib_wid in self.libervia_widgets: if isinstance(lib_wid, panels.MicroblogPanel): if lib_wid.isJidAccepted(entity_jid_s) or (self.whoami and entity_jid_s == self.whoami.bare): lib_wid.updateValue('avatar', entity_jid_s, avatar) def _chatStateReceivedCb(self, from_jid_s, state, profile): """Callback when a new chat state is received. @param from_jid_s: JID from the contact who sent his state @param state: new state @profile: current profile """ _from = JID(from_jid_s).bare if from_jid_s != "@ALL@" else from_jid_s for lib_wid in self.libervia_widgets: if isinstance(lib_wid, panels.ChatPanel): win_from = lib_wid.target.bare good_win = win_from == _from or _from == "@ALL@" if (good_win and lib_wid.type == 'one2one'): if state: lib_wid.setTitle(win_from + " (" + state + ")") else: lib_wid.setTitle(win_from) elif (lib_wid.type == 'group'): # TODO: chat state notification for groupchat pass if __name__ == '__main__': pyjd.setup("http://localhost:8080/libervia.html") app = SatWebFrontend() app.onModuleLoad() pyjd.run()