diff --git a/activities/browser/BrowserActivity.py b/activities/browser/BrowserActivity.py index 0068f20c..53cf37ba 100644 --- a/activities/browser/BrowserActivity.py +++ b/activities/browser/BrowserActivity.py @@ -26,13 +26,53 @@ class BrowserActivity(Activity): def __init__(self, args): Activity.__init__(self, _BROWSER_ACTIVITY_TYPE) - self.uri = args[0] + if len(args) > 0: + self.uri = args[0] + else: + self.uri = 'http://www.google.com' self._mode = BrowserActivity.SOLO self._share_service = None self._model_service = None self._notif_service = None self._model = None + + self.set_title("Web Page") + + vbox = gtk.VBox() + + self._notif_bar = NotificationBar() + vbox.pack_start(self._notif_bar, False) + self._notif_bar.connect('action', self.__notif_bar_action_cb) + + self.embed = geckoembed.Embed() + self.embed.connect("title", self.__title_cb) + vbox.pack_start(self.embed) + + self.embed.show() + self.embed.load_address(self.uri) + + nav_toolbar = NavigationToolbar(self) + vbox.pack_start(nav_toolbar, False) + nav_toolbar.show() + + self.add(vbox) + vbox.show() + + logging.debug('Start presence service') + self._pservice = PresenceService.get_instance() + self._pservice.start() + + logging.debug('Track browser activities') + self._pservice.connect('service-appeared', self._service_appeared_cb) + self._pservice.track_service_type(_BROWSER_ACTIVITY_TYPE) + self._pservice.track_service_type(LocalModel.SERVICE_TYPE) + + # Join the shared activity if we were started from one + if self._initial_service: + logging.debug("BrowserActivity joining shared activity %s" % + self._initial_service.get_activity_id()) + self._pservice.join_shared_activity(self._initial_service) def _service_appeared_cb(self, pservice, buddy, service): # Make sure the service is for our activity @@ -75,50 +115,6 @@ class BrowserActivity(Activity): self._notif_bar.set_icon('stock_shared-by-me') self._notif_bar.show() - def on_connected_to_shell(self): - self.set_ellipsize_tab(True) - self.set_can_close(True) - self.set_tab_text("Web Page") - self.set_tab_icon(name="web-browser") - self.set_show_tab_icon(True) - - vbox = gtk.VBox() - - self._notif_bar = NotificationBar() - vbox.pack_start(self._notif_bar, False) - self._notif_bar.connect('action', self.__notif_bar_action_cb) - - self.embed = geckoembed.Embed() - self.embed.connect("title", self.__title_cb) - vbox.pack_start(self.embed) - - self.embed.show() - self.embed.load_address(self.uri) - - nav_toolbar = NavigationToolbar(self) - vbox.pack_start(nav_toolbar, False) - nav_toolbar.show() - - plug = self.gtk_plug() - plug.add(vbox) - plug.show() - - vbox.show() - - logging.debug('Start presence service') - self._pservice = PresenceService.get_instance() - self._pservice.start() - - logging.debug('Track browser activities') - self._pservice.connect('service-appeared', self._service_appeared_cb) - self._pservice.track_service_type(_BROWSER_ACTIVITY_TYPE) - self._pservice.track_service_type(LocalModel.SERVICE_TYPE) - - # Join the shared activity if we were started from one - if self._initial_service: - logging.debug("BrowserActivity joining shared activity %s" % self._initial_service.get_activity_id()) - self._pservice.join_shared_activity(self._initial_service) - def get_embed(self): return self.embed @@ -139,7 +135,7 @@ class BrowserActivity(Activity): self.set_mode(BrowserActivity.LEADING) def __title_cb(self, embed): - self.set_tab_text(embed.get_title()) + self.set_title(embed.get_title()) def __shared_location_changed_cb(self, model, key): self.set_has_changes(True) diff --git a/shell/ActivityContainer.py b/shell/ActivityContainer.py deleted file mode 100644 index 1e48b955..00000000 --- a/shell/ActivityContainer.py +++ /dev/null @@ -1,167 +0,0 @@ -import dbus -import gobject -import gtk -from gettext import gettext as _ - -from sugar.chat.ChatWindow import ChatWindow -from sugar.chat.MeshChat import MeshChat -from ActivityHost import ActivityHost -from PresenceWindow import PresenceWindow -from WindowManager import WindowManager -from StartPage import StartPage -from Owner import ShellOwner - -class ActivityContainerSignalHelper(gobject.GObject): - """A gobject whose sole purpose is to distribute signals for - an ActivityContainer object.""" - - __gsignals__ = { - 'local-activity-started': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), - 'local-activity-ended': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])) - } - - def __init__(self, parent): - gobject.GObject.__init__(self) - self._parent = parent - - def activity_started(self, activity_id): - self.emit('local-activity-started', self._parent, activity_id) - - def activity_ended(self, activity_id): - self.emit('local-activity-ended', self._parent, activity_id) - -class ActivityContainer(dbus.service.Object): - - def __init__(self, service, bus): - self.activities = [] - - self.bus = bus - self.service = service - - self._signal_helper = ActivityContainerSignalHelper(self) - - dbus.service.Object.__init__(self, self.service, "/com/redhat/Sugar/Shell/ActivityContainer") - bus.add_signal_receiver(self.name_owner_changed, dbus_interface = "org.freedesktop.DBus", signal_name = "NameOwnerChanged") - - self.window = gtk.Window() - self.window.connect("key-press-event", self.__key_press_event_cb) - self.window.set_title("OLPC Sugar") - - self._fullscreen = False - - self.notebook = gtk.Notebook() - self.notebook.set_scrollable(True) - - tab_label = gtk.Label(_("Everyone")) - self._start_page = StartPage(self, self._signal_helper) - self.notebook.append_page(self._start_page, tab_label) - self._start_page.show() - - self.notebook.show() - self.notebook.connect("switch-page", self.notebook_tab_changed) - self.window.add(self.notebook) - - self.window.connect("destroy", lambda w: gtk.main_quit()) - - self.current_activity = None - - # Create our owner service - self._owner = ShellOwner() - - self._presence_window = PresenceWindow(self) - self._presence_window.set_transient_for(self.window) - - wm = WindowManager(self._presence_window) - wm.set_type(WindowManager.TYPE_POPUP) - wm.set_animation(WindowManager.ANIMATION_SLIDE_IN) - wm.set_geometry(0.02, 0.1, 0.25, 0.9) - wm.set_key(gtk.keysyms.F1) - - self._chat_window = ChatWindow() - self._chat_window.set_transient_for(self.window) - - self._chat_wm = WindowManager(self._chat_window) - self._chat_wm.set_animation(WindowManager.ANIMATION_SLIDE_IN) - self._chat_wm.set_type(WindowManager.TYPE_POPUP) - self._chat_wm.set_geometry(0.28, 0.1, 0.5, 0.9) - self._chat_wm.set_key(gtk.keysyms.F1) - - self._mesh_chat = MeshChat() - - def show(self): - self.window.show() - - def set_current_activity(self, activity): - self.current_activity = activity - self._presence_window.set_activity(activity) - - if activity: - host_chat = activity.get_chat() - self._chat_window.set_chat(host_chat) - else: - self._chat_window.set_chat(self._mesh_chat) - - def notebook_tab_changed(self, notebook, page, page_number): - new_activity = notebook.get_nth_page(page_number).get_data("sugar-activity") - - if self.current_activity != None: - self.current_activity.lost_focus() - - self.set_current_activity(new_activity) - - if self.current_activity != None: - self.current_activity.got_focus() - - def name_owner_changed(self, service_name, old_service_name, new_service_name): - #print "in name_owner_changed: svc=%s oldsvc=%s newsvc=%s"%(service_name, old_service_name, new_service_name) - for owner, activity in self.activities[:]: - if owner == old_service_name: - activity_id = activity.get_host_activity_id() - self._signal_helper.activity_ended(activity_id) - self.activities.remove((owner, activity)) - #self.__print_activities() - - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer", \ - in_signature="ss", \ - out_signature="s", \ - sender_keyword="sender") - def add_activity(self, activity_name, default_type, sender): - #print "hello world, activity_name = '%s', sender = '%s'"%(activity_name, sender) - activity = ActivityHost(self, activity_name, default_type) - self.activities.append((sender, activity)) - - activity_id = activity.get_host_activity_id() - self._signal_helper.activity_started(activity_id) - - self.current_activity = activity - return activity_id - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer", \ - in_signature="sss", \ - sender_keyword="sender") - def add_activity_with_id(self, activity_name, default_type, activity_id, sender): - activity = ActivityHost(self, activity_name, default_type, activity_id) - self.activities.append((sender, activity)) - activity_id = activity.get_host_activity_id() - self._signal_helper.activity_started(activity_id) - self.current_activity = activity - - def __print_activities(self): - print "__print_activities: %d activities registered" % len(self.activities) - i = 0 - for owner, activity in self.activities: - print " %d: owner=%s activity_object_name=%s" % (i, owner, activity.dbus_object_name) - i += 1 - - def __key_press_event_cb(self, window, event): - if event.keyval == gtk.keysyms.F11: - if self._fullscreen: - window.unfullscreen() - self._fullscreen = False - else: - window.fullscreen() - self._fullscreen = True - diff --git a/shell/ActivityHost.py b/shell/ActivityHost.py deleted file mode 100644 index 1d61bebf..00000000 --- a/shell/ActivityHost.py +++ /dev/null @@ -1,269 +0,0 @@ -import dbus -import gtk -import gobject - -class ActivityHostSignalHelper(gobject.GObject): - __gsignals__ = { - 'shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) - } - - def __init__(self, parent): - gobject.GObject.__init__(self) - self._parent = parent - - def emit_shared(self): - self.emit('shared') - -class ActivityHost(dbus.service.Object): - def __init__(self, activity_container, activity_name, default_type, activity_id = None): - self.peer_service = None - - self.activity_name = activity_name - self.ellipsize_tab = False - self._shared = False - - self._signal_helper = ActivityHostSignalHelper(self) - - self.activity_container = activity_container - - if activity_id is None: - self.activity_id = sugar.util.unique_id() - else: - self.activity_id = activity_id - self._default_type = default_type - - self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%s" % self.activity_id - - dbus.service.Object.__init__(self, activity_container.service, self.dbus_object_name) - self.socket = gtk.Socket() - self.socket.set_data("sugar-activity", self) - self.socket.show() - - hbox = gtk.HBox(False, 4); - - self.tab_activity_image = gtk.Image() - self.tab_activity_image.set_from_stock(gtk.STOCK_CONVERT, gtk.ICON_SIZE_MENU) - hbox.pack_start(self.tab_activity_image) - #self.tab_activity_image.show() - - self.label_hbox = gtk.HBox(False, 4); - self.label_hbox.connect("style-set", self.__tab_label_style_set_cb) - hbox.pack_start(self.label_hbox) - - self.tab_label = gtk.Label(self.activity_name) - self.tab_label.set_single_line_mode(True) - self.tab_label.set_alignment(0.0, 0.5) - self.tab_label.set_padding(0, 0) - self.tab_label.show() - - close_image = gtk.Image() - close_image.set_from_stock (gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) - close_image.show() - - self.tab_close_button = gtk.Button() - rcstyle = gtk.RcStyle(); - rcstyle.xthickness = rcstyle.ythickness = 0; - self.tab_close_button.modify_style (rcstyle); - self.tab_close_button.add(close_image) - self.tab_close_button.set_relief(gtk.RELIEF_NONE) - self.tab_close_button.set_focus_on_click(False) - self.tab_close_button.connect("clicked", self.tab_close_button_clicked) - - self.label_hbox.pack_start(self.tab_label) - self.label_hbox.pack_start(self.tab_close_button, False, False, 0) - self.label_hbox.show() - - hbox.show() - - self._create_chat() - - notebook = self.activity_container.notebook - index = notebook.append_page(self.socket, hbox) - notebook.set_current_page(index) - - def _create_chat(self): - self._activity_chat = ActivityChat(self) - - def got_focus(self): - if self.peer_service != None: - self.peer_service.got_focus() - - def lost_focus(self): - self.peer_service.lost_focus() - - def get_chat(self): - return self._activity_chat - - def get_default_type(self): - return self._default_type - - def __close_button_clicked_reply_cb(self): - pass - - def __close_button_clicked_error_cb(self, error): - pass - - def publish(self): - self._activity_chat.publish() - self.peer_service.publish() - - def tab_close_button_clicked(self, button): - self.peer_service.close_from_user(reply_handler = self.__close_button_clicked_reply_cb, \ - error_handler = self.__close_button_clicked_error_cb) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="", \ - out_signature="t") - def get_host_xembed_id(self): - window_id = self.socket.get_id() - #print "window_id = %d"%window_id - return window_id - - def connect(self, signal, func): - self._signal_helper.connect(signal, func) - - def get_shared(self): - """Return True if this activity is shared, False if - it has not been shared yet.""" - return self._shared - - def _shared_signal(self): - self._shared = True - self._signal_helper.emit_shared() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="ss", \ - out_signature="") - def set_peer_service_name(self, peer_service_name, peer_object_name): - #print "peer_service_name = %s, peer_object_name = %s"%(peer_service_name, peer_object_name) - self.__peer_service_name = peer_service_name - self.__peer_object_name = peer_object_name - self.peer_service = dbus.Interface(self.activity_container.bus.get_object( \ - self.__peer_service_name, self.__peer_object_name), \ - "com.redhat.Sugar.Activity") - self.activity_container.bus.add_signal_receiver(self._shared_signal, - signal_name="ActivityShared", - dbus_interface="com.redhat.Sugar.Activity", - named_service=self.__peer_service_name, - path=self.__peer_object_name) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_ellipsize_tab(self, ellipsize): - self.ellipsize_tab = True - self.update_tab_size() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_can_close(self, can_close): - if can_close: - self.tab_close_button.show() - else: - self.tab_close_button.hide() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_tab_show_icon(self, show_icon): - if show_icon: - self.tab_activity_image.show() - else: - self.tab_activity_image.hide() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_has_changes(self, has_changes): - if has_changes: - attrs = pango.AttrList() - attrs.insert(pango.AttrForeground(50000, 0, 0, 0, -1)) - attrs.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)) - self.tab_label.set_attributes(attrs) - else: - self.tab_label.set_attributes(pango.AttrList()) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="s", \ - out_signature="") - def set_tab_text(self, text): - self.tab_label.set_text(text) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="ayibiiii", \ - out_signature="") - def set_tab_icon(self, data, colorspace, has_alpha, bits_per_sample, width, height, rowstride): - #print "width=%d, height=%d"%(width, height) - #print " data = ", data - pixstr = "" - for c in data: - # Work around for a bug in dbus < 0.61 where integers - # are not correctly marshalled - if c < 0: - c += 256 - pixstr += chr(c) - - pixbuf = gtk.gdk.pixbuf_new_from_data(pixstr, colorspace, has_alpha, bits_per_sample, width, height, rowstride) - #print pixbuf - self.tab_activity_image.set_from_pixbuf(pixbuf) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="", \ - out_signature="") - def shutdown(self): - #print "shutdown" - for owner, activity in self.activity_container.activities[:]: - if activity == self: - self.activity_container.activities.remove((owner, activity)) - - for i in range(self.activity_container.notebook.get_n_pages()): - child = self.activity_container.notebook.get_nth_page(i) - if child == self.socket: - #print "found child" - self.activity_container.notebook.remove_page(i) - break - - del self - - def get_host_activity_id(self): - """Real function that the shell should use for getting the - activity's ID.""" - return self.activity_id - - def get_id(self): - """Interface-type function to match activity.Activity's - get_id() function.""" - return self.activity_id - - def default_type(self): - """Interface-type function to match activity.Activity's - default_type() function.""" - return self._default_type - - def get_object_path(self): - return self.dbus_object_name - - def update_tab_size(self): - if self.ellipsize_tab: - self.tab_label.set_ellipsize(pango.ELLIPSIZE_END) - - context = self.label_hbox.get_pango_context() - font_desc = self.label_hbox.style.font_desc - metrics = context.get_metrics(font_desc, context.get_language()) - char_width = metrics.get_approximate_digit_width() - [w, h] = self.__get_close_icon_size() - tab_width = 15 * pango.PIXELS(char_width) + 2 * w - self.label_hbox.set_size_request(tab_width, -1); - else: - self.tab_label.set_ellipsize(pango.ELLIPSIZE_NONE) - self.label_hbox.set_size_request(-1, -1) - - def __get_close_icon_size(self): - settings = self.label_hbox.get_settings() - return gtk.icon_size_lookup_for_settings(settings, gtk.ICON_SIZE_MENU) - - def __tab_label_style_set_cb(self, widget, previous_style): - [w, h] = self.__get_close_icon_size() - self.tab_close_button.set_size_request (w + 5, h + 2) - self.update_tab_size() diff --git a/shell/ActivityRegistry.py b/shell/ActivityRegistry.py new file mode 100644 index 00000000..96ec3b15 --- /dev/null +++ b/shell/ActivityRegistry.py @@ -0,0 +1,29 @@ +import dbus + +class ActivityInfo: + def __init__(self, name, title): + self._name = name + self._title = title + + def get_name(self): + return self._name + + def get_title(self): + return self._title + +class ActivityRegistry(dbus.service.Object): + """Dbus service that tracks the available activities""" + + def __init__(self): + self._activities = [] + + bus = dbus.SessionBus() + bus_name = dbus.service.BusName('com.redhat.Sugar.ActivityRegistry', bus = bus) + dbus.service.Object.__init__(self, bus_name, '/com/redhat/Sugar/ActivityRegistry') + + @dbus.service.method("com.redhat.Sugar.ActivityRegistry") + def add(self, name, title): + self._activities.append(ActivityInfo(name, title)) + + def list_activities(self): + return self._activities diff --git a/shell/ConsoleLogger.py b/shell/ConsoleLogger.py index c9c968f4..fc431ecb 100644 --- a/shell/ConsoleLogger.py +++ b/shell/ConsoleLogger.py @@ -1,9 +1,6 @@ import gtk import dbus -from WindowManager import WindowManager -from ActivityContainer import ActivityContainer - class ConsoleLogger(dbus.service.Object): def __init__(self): session_bus = dbus.SessionBus() @@ -21,9 +18,8 @@ class ConsoleLogger(dbus.service.Object): self._consoles = {} - console_wm = WindowManager(self._window) - console_wm.set_type(WindowManager.TYPE_POPUP) - console_wm.set_geometry(0.1, 0.1, 0.8, 0.8) + def get_window(self): + return self._window def _create_console(self, application): sw = gtk.ScrolledWindow() @@ -51,4 +47,3 @@ class ConsoleLogger(dbus.service.Object): buf = console.get_buffer() buf.insert(buf.get_end_iter(), message) - diff --git a/shell/HomeWindow.py b/shell/HomeWindow.py new file mode 100644 index 00000000..f0124b1f --- /dev/null +++ b/shell/HomeWindow.py @@ -0,0 +1,102 @@ +from gettext import gettext as _ + +import gtk +import wnck + +from sugar.activity import Activity + +class NewActivityButton(gtk.MenuToolButton): + def __init__(self, home): + gtk.MenuToolButton.__init__(self, None, _('New Activity')) + + self._home = home + + self.set_menu(gtk.Menu()) + self.connect("show-menu", self.__show_menu_cb) + + def __show_menu_cb(self, button): + menu = gtk.Menu() + + for activity_info in self._home.list_activities(): + item = gtk.MenuItem(activity_info.get_title(), False) + name = activity_info.get_name() + item.connect('activate', self.__menu_item_activate_cb, name) + menu.append(item) + item.show() + + self.set_menu(menu) + + def __menu_item_activate_cb(self, item, name): + self._home.create(name) + +class Toolbar(gtk.Toolbar): + def __init__(self, shell): + gtk.Toolbar.__init__(self) + + new_activity_button = NewActivityButton(shell) + self.insert(new_activity_button, -1) + new_activity_button.show() + +class ActivityGrid(gtk.VBox): + def __init__(self, home): + gtk.VBox.__init__(self) + + self._home = home + self.update() + + def _add_all(self): + screen = wnck.screen_get_default() + for window in screen.get_windows(): + if not window.is_skip_tasklist(): + self.add(window) + + def _remove_all(self): + for child in self.get_children(): + self.remove(child) + + def add(self, window): + button = gtk.Button(window.get_name()) + button.connect('clicked', self.__button_clicked_cb, window) + self.pack_start(button, False) + button.show() + + def update(self): + self._remove_all() + self._add_all() + + def __button_clicked_cb(self, button, window): + self._home.activate(window) + +class HomeWindow(gtk.Window): + def __init__(self, shell): + gtk.Window.__init__(self) + + self._shell = shell + + vbox = gtk.VBox() + + toolbar = Toolbar(self) + vbox.pack_start(toolbar, False) + toolbar.show() + + self._grid = ActivityGrid(self) + vbox.pack_start(self._grid) + self._grid.show() + + self.add(vbox) + vbox.show() + + def list_activities(self): + return self._shell.get_registry().list_activities() + + def create(self, activity_name): + Activity.create(activity_name) + self.hide() + + def activate(self, activity_window): + activity_window.activate(gtk.get_current_event_time()) + self.hide() + + def show(self): + self._grid.update() + gtk.Window.show(self) diff --git a/shell/PresenceWindow.py b/shell/PresenceWindow.py index 1f131450..666f86c4 100644 --- a/shell/PresenceWindow.py +++ b/shell/PresenceWindow.py @@ -16,11 +16,11 @@ class PresenceWindow(gtk.Window): _MODEL_COL_BUDDY = 2 _MODEL_COL_VISIBLE = 3 - def __init__(self, activity_container): + def __init__(self, shell): gtk.Window.__init__(self) - self._activity_container = activity_container self._activity = None + self._shell = shell self._pservice = PresenceService.get_instance() self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb) @@ -51,7 +51,6 @@ class PresenceWindow(gtk.Window): self._share_button.set_sensitive(False) else: self._share_button.set_sensitive(True) - self._activity.connect('shared', lambda w: self._share_button.set_sensitive(False)) else: self._share_button.set_sensitive(False) @@ -113,7 +112,7 @@ class PresenceWindow(gtk.Window): vbox.show() def _share_button_clicked_cb(self, button): - self._activity_container.current_activity.publish() + self._shell.get_current_activity().publish() def _on_buddyList_buddy_selected(self, view, *args): (model, aniter) = view.get_selection().get_selected() diff --git a/shell/Shell.py b/shell/Shell.py index 025ec80c..d8befd5a 100755 --- a/shell/Shell.py +++ b/shell/Shell.py @@ -1,10 +1,37 @@ import dbus +import gtk import gobject +import wnck from sugar.LogWriter import LogWriter -from WindowManager import WindowManager from ConsoleLogger import ConsoleLogger -from ActivityContainer import ActivityContainer +from ActivityRegistry import ActivityRegistry +from HomeWindow import HomeWindow +from sugar import keybindings +from sugar.activity import Activity +from PresenceWindow import PresenceWindow +from sugar.chat.ActivityChat import ActivityChat +from Owner import ShellOwner + +class ShellDbusService(dbus.service.Object): + def __init__(self, shell, bus_name): + dbus.service.Object.__init__(self, bus_name, '/com/redhat/Sugar/Shell') + self._shell = shell + + def __toggle_people_idle(self): + self._shell.toggle_people() + + @dbus.service.method('com.redhat.Sugar.Shell') + def toggle_people(self): + gobject.idle_add(self.__toggle_people_idle) + + @dbus.service.method('com.redhat.Sugar.Shell') + def toggle_home(self): + self._shell.toggle_home() + + @dbus.service.method('com.redhat.Sugar.Shell') + def toggle_console(self): + self._shell.toggle_console() class Shell(gobject.GObject): __gsignals__ = { @@ -15,24 +42,96 @@ class Shell(gobject.GObject): def __init__(self): gobject.GObject.__init__(self) + self._screen = wnck.screen_get_default() + def start(self): - console = ConsoleLogger() + self._console = ConsoleLogger() + keybindings.setup_global_keys(self._console.get_window(), self) log_writer = LogWriter("Shell", False) log_writer.start() session_bus = dbus.SessionBus() - service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus) + bus_name = dbus.service.BusName('com.redhat.Sugar.Shell', bus=session_bus) + ShellDbusService(self, bus_name) - activity_container = ActivityContainer(service, session_bus) - activity_container.window.connect('destroy', self.__activity_container_destroy_cb) - activity_container.show() + self._owner = ShellOwner() - wm = WindowManager(activity_container.window) - wm.show() + self._registry = ActivityRegistry() - def __activity_container_destroy_cb(self, activity_container): - self.emit('close') + self._home_window = HomeWindow(self) + keybindings.setup_global_keys(self._home_window, self) + self._home_window.show() + + self._presence_window = PresenceWindow(self) + self._presence_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self._presence_window.set_skip_taskbar_hint(True) + self._presence_window.set_decorated(False) + keybindings.setup_global_keys(self._presence_window, self) + + self._chat_windows = {} + + def _toggle_window_visibility(self, window): + if window.get_property('visible'): + window.hide() + else: + window.show() + + def toggle_home(self): + self._toggle_window_visibility(self._home_window) + + def get_activity_from_xid(self, xid): + bus = dbus.SessionBus() + service = Activity.ACTIVITY_SERVICE_NAME + "%s" % xid + path = Activity.ACTIVITY_SERVICE_PATH + "/%s" % xid + proxy_obj = bus.get_object(service, path) + + return dbus.Interface(proxy_obj, 'com.redhat.Sugar.Activity') + + def get_current_activity(self): + window = self._screen.get_active_window() + + if window and window.is_skip_tasklist(): + window = self._screen.get_previously_active_window() + + if window and not window.is_skip_tasklist(): + return self.get_activity_from_xid(window.get_xid()) + else: + return None + + def toggle_people(self): + activity = self.get_current_activity() + + if activity: + activity_id = activity.get_id() + + if not self._chat_windows.has_key(activity_id): + window = gtk.Window() + window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + window.set_skip_taskbar_hint(True) + window.set_decorated(False) + keybindings.setup_global_keys(window, self) + chat = ActivityChat(activity) + window.add(chat) + chat.show() + self._chat_windows[activity_id] = window + else: + window = self._chat_windows[activity_id] + + window.move(210, 10) + window.resize(380, 440) + self._toggle_window_visibility(window) + + self._presence_window.move(10, 10) + self._presence_window.resize(180, 440) + self._presence_window.set_activity(activity) + self._toggle_window_visibility(self._presence_window) + + def toggle_console(self): + self._toggle_window_visibility(self._console.get_window()) + + def get_registry(self): + return self._registry if __name__ == "__main__": shell = Shell() diff --git a/shell/StartPage.py b/shell/StartPage.py deleted file mode 100644 index cfde8045..00000000 --- a/shell/StartPage.py +++ /dev/null @@ -1,314 +0,0 @@ -import pygtk -pygtk.require('2.0') -import gtk -import pango -import cgi -import xml.sax.saxutils -import gobject -import socket - -import dbus_bindings -from google import google -from sugar.presence.PresenceService import PresenceService -from sugar.activity import Activity - -from gettext import gettext as _ - -_BROWSER_ACTIVITY_TYPE = "_web_olpc._udp" - -_COLUMN_TITLE = 0 -_COLUMN_ADDRESS = 1 -_COLUMN_SUBTITLE = 2 -_COLUMN_SERVICE = 3 - -class SearchHelper(object): - def __init__(self, activity_id): - self.search_id = activity_id - self.found = False - -class SearchModel(gtk.ListStore): - def __init__(self, activities_model, search_text): - gtk.ListStore.__init__(self, gobject.TYPE_STRING, gobject.TYPE_STRING, - gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) - success = False - - for row in activities_model: - title = row[_COLUMN_TITLE] - address = row[_COLUMN_ADDRESS] - if title.find(search_text) >= 0 or address.find(search_text) >= 0: - self.append([ title, address, row[_COLUMN_SUBTITLE], row[_COLUMN_SERVICE] ]) - - google.LICENSE_KEY = '1As9KaJQFHIJ1L0W5EZPl6vBOFvh/Vaf' - try: - data = google.doGoogleSearch(search_text) - success = True - except socket.gaierror, exc: - if exc[0] == -3: # Temporary failure in name resolution - errdlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, - gtk.BUTTONS_OK, "There appears to be no network connection.") - errdlg.connect("response", lambda d, e: d.destroy()) - errdlg.connect("close", lambda d, e: d.destroy()) - errdlg.show() - - if success == True: - for result in data.results: - title = result.title - - # FIXME what tags should we actually strip? - title = title.replace('', '') - title = title.replace('', '') - - # FIXME I'm sure there is a better way to - # unescape these. - title = title.replace('"', '"') - title = title.replace('&', '&') - - self.append([ title, result.URL, None, None ]) - -class ActivitiesModel(gtk.ListStore): - def __init__(self): - gtk.ListStore.__init__(self, gobject.TYPE_STRING, gobject.TYPE_STRING, - gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) - - def _filter_dupe_activities(self, model, path, it, user_data): - """Search the list of list rows for an existing service that - has the activity ID we're looking for.""" - helper = user_data - (service, ) = model.get(it, _COLUMN_SERVICE) - if not service: - return False - if service.get_activity_id() == helper.search_id: - helper.found = True - return True - return False - - def add_activity(self, buddy, service): - # Web Activity check - activity_id = service.get_activity_id() - if activity_id is None: - return - # Don't show dupes - helper = SearchHelper(activity_id) - self.foreach(self._filter_dupe_activities, helper) - if helper.found == True: - return - - # Only accept browser activities for now - if service.get_type() == _BROWSER_ACTIVITY_TYPE: - escaped_title = service.get_one_property('Title') - escaped_uri = service.get_one_property('URI') - if escaped_title and escaped_uri: - title = xml.sax.saxutils.unescape(escaped_title) - address = xml.sax.saxutils.unescape(escaped_uri) - subtitle = 'Shared by %s' % buddy.get_nick_name() - self.append([ title, address, subtitle, service ]) - -class ActivitiesView(gtk.TreeView): - def __init__(self, activity_controller, model): - gtk.TreeView.__init__(self, model) - - self._owner = None - self._activity_controller = activity_controller - - self.set_headers_visible(False) - - theme = gtk.icon_theme_get_default() - size = 48 - self._web_pixbuf = theme.load_icon('emblem-web', size, 0) - self._share_pixbuf = theme.load_icon('emblem-people', size, 0) - - column = gtk.TreeViewColumn('') - self.append_column(column) - - cell = gtk.CellRendererPixbuf() - column.pack_start(cell, False) - column.set_cell_data_func(cell, self._icon_cell_data_func) - - cell = gtk.CellRendererText() - column.pack_start(cell) - column.set_cell_data_func(cell, self._cell_data_func) - - self.connect('row-activated', self._row_activated_cb) - - def _icon_cell_data_func(self, column, cell, model, it): - if model.get_value(it, _COLUMN_SERVICE) == None: - cell.set_property('pixbuf', self._web_pixbuf) - else: - cell.set_property('pixbuf', self._share_pixbuf) - - def _cell_data_func(self, column, cell, model, it): - title = model.get_value(it, _COLUMN_TITLE) - subtitle = model.get_value(it, _COLUMN_SUBTITLE) - if subtitle is None: - subtitle = model.get_value(it, _COLUMN_ADDRESS) - - markup = '' + cgi.escape(title) + '' - markup += '\n' + cgi.escape(subtitle) - - cell.set_property('markup', markup) - cell.set_property('ellipsize', pango.ELLIPSIZE_END) - - def set_owner(self, owner): - self._owner = owner - - def _row_activated_cb(self, treeview, path, column): - model = self.get_model() - address = model.get_value(model.get_iter(path), _COLUMN_ADDRESS) - service = model.get_value(model.get_iter(path), _COLUMN_SERVICE) - - print 'Activated row %s' % address - - if service is None: - browser_shell.open_browser(address) - return - - if not self._owner: - raise RuntimeError("We don't have an owner yet!") - - # If the activity is already started, switch to it - service_act_id = service.get_activity_id() - if service_act_id and self._activity_controller.have_activity(service_act_id): - self._activity_controller.switch_to_activity(service_act_id) - return - - Activity.create('com.redhat.Sugar.BrowserActivity', service, [ address ]) - -class StartPage(gtk.HBox): - def __init__(self, activity_controller, ac_signal_object): - gtk.HBox.__init__(self) - - self._ac_signal_object = ac_signal_object - self._ac_signal_object.connect("local-activity-started", - self._on_local_activity_started_cb) - self._ac_signal_object.connect("local-activity-ended", - self._on_local_activity_ended_cb) - - self._pservice = PresenceService.get_instance() - self._pservice.connect("activity-announced", self._on_activity_announced_cb) - self._pservice.connect("new-service-adv", self._on_new_service_adv_cb) - self._pservice.connect("buddy-appeared", self._on_buddy_appeared_cb) - self._pservice.connect("buddy-disappeared", self._on_buddy_disappeared_cb) - self._pservice.start() - self._pservice.track_service_type(_BROWSER_ACTIVITY_TYPE) - if self._pservice.get_owner(): - self._on_buddy_appeared_cb(self._pservice, self._pservice.get_owner()) - - vbox = gtk.VBox() - - search_box = gtk.HBox(False, 6) - search_box.set_border_width(24) - - self._search_entry = gtk.Entry() - self._search_entry.connect('activate', self._search_entry_activate_cb) - search_box.pack_start(self._search_entry) - self._search_entry.show() - - search_button = gtk.Button(_("Search")) - search_button.connect('clicked', self._search_button_clicked_cb) - search_box.pack_start(search_button, False) - search_button.show() - - vbox.pack_start(search_box, False, True) - search_box.show() - - exp_space = gtk.Label('') - vbox.pack_start(exp_space) - exp_space.show() - - self.pack_start(vbox) - vbox.show() - - vbox = gtk.VBox() - - self._search_close_box = gtk.HBox() - - self._search_close_label = gtk.Label() - self._search_close_label.set_alignment(0.0, 0.5) - self._search_close_box.pack_start(self._search_close_label) - self._search_close_label.show() - - close_image = gtk.Image() - close_image.set_from_stock (gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) - close_image.show() - - search_close_button = gtk.Button() - rcstyle = gtk.RcStyle(); - rcstyle.xthickness = rcstyle.ythickness = 0; - search_close_button.modify_style (rcstyle); - search_close_button.add(close_image) - search_close_button.set_relief(gtk.RELIEF_NONE) - search_close_button.set_focus_on_click(False) - search_close_button.connect("clicked", self.__search_close_button_clicked_cb) - - self._search_close_box.pack_start(search_close_button, False) - search_close_button.show() - - vbox.pack_start(self._search_close_box, False) - - sw = gtk.ScrolledWindow() - sw.set_size_request(320, -1) - sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) - - self._activities_model = ActivitiesModel() - - owner = self._pservice.get_owner() - self._activities = ActivitiesView(activity_controller, self._activities_model) - sw.add(self._activities) - self._activities.show() - - vbox.pack_start(sw) - sw.show() - - self.pack_start(vbox) - vbox.show() - - def __search_close_button_clicked_cb(self, button): - self._search(None) - - def _on_local_activity_started_cb(self, helper, activity_container, activity_id): - print "new local activity %s" % activity_id - - def _on_local_activity_ended_cb(self, helper, activity_container, activity_id): - print "local activity %s disappeared" % activity_id - - def _on_new_service_adv_cb(self, pservice, activity_id, short_stype): - if activity_id: - self._pservice.track_service_type(short_stype) - - def _on_buddy_appeared_cb(self, pservice, buddy): - if buddy.is_owner(): - self._activities.set_owner(buddy) - - def _on_buddy_disappeared_cb(self, pservice, buddy): - if buddy.is_owner(): - self._activities.set_owner(None) - - def _on_activity_announced_cb(self, pservice, service, buddy): - print "Found new activity service (activity %s of type %s)" % (service.get_activity_id(), service.get_type()) - self._activities_model.add_activity(buddy, service) - if self._activities.get_model() != self._activities_model: - self._search(self._last_search) - - def _search_entry_activate_cb(self, entry): - self._search() - self._search_entry.set_text('') - - def _search_button_clicked_cb(self, button): - self._search() - self._search_entry.set_text('') - - def _search(self, text = None): - if text == None: - text = self._search_entry.get_text() - - if text == None or len(text) == 0: - self._activities.set_model(self._activities_model) - self._search_close_box.hide() - else: - search_model = SearchModel(self._activities_model, text) - self._activities.set_model(search_model) - - self._search_close_label.set_text('Search for %s' % (text)) - self._search_close_box.show() - - self._last_search = text diff --git a/shell/WindowManager.py b/shell/WindowManager.py deleted file mode 100644 index af393246..00000000 --- a/shell/WindowManager.py +++ /dev/null @@ -1,206 +0,0 @@ -import time -import logging - -import gtk -import gobject - -DEFAULT_WIDTH = 640 -DEFAULT_HEIGHT = 480 - -SLIDING_TIME = 0.8 - -class SlidingHelper: - IN = 0 - OUT = 1 - - def __init__(self, manager, direction): - self._direction = direction - self._cur_time = time.time() - self._target_time = self._cur_time + SLIDING_TIME - self._manager = manager - self._start = True - self._end = False - - (x, y, width, height) = manager.get_geometry() - self._orig_y = y - if direction == SlidingHelper.IN: - self._target_y = y - manager.set_geometry(x, y - height, width, height) - else: - self._target_y = y - height - - def get_direction(self): - return self._direction - - def is_start(self): - return self._start - - def is_end(self): - return self._end - - def get_next_y(self): - self._start = False - - (x, y, width, height) = self._manager.get_geometry() - - old_time = self._cur_time - self._cur_time = time.time() - remaining = self._target_time - self._cur_time - - if remaining <= 0 or \ - (y > self._target_y and self._direction == SlidingHelper.IN) or \ - (y < self._target_y and self._direction == SlidingHelper.OUT): - self._end = True - y = self._orig_y - else: - approx_time_step = float(self._cur_time - old_time) - approx_n_steps = remaining / approx_time_step - step = (self._target_y - y) / approx_n_steps - y += step - - return y - -class WindowManager: - __managers_list = [] - - TYPE_ACTIVITY = 0 - TYPE_POPUP = 1 - - ANIMATION_NONE = 0 - ANIMATION_SLIDE_IN = 1 - - def __init__(self, window): - self._window = window - self._window_type = WindowManager.TYPE_ACTIVITY - self._animation = WindowManager.ANIMATION_NONE - self._key = 0 - self._animating = False - - window.connect("key-press-event", self.__key_press_event_cb) - - WindowManager.__managers_list.append(self) - - def __key_press_event_cb(self, window, event): - # FIXME we should fix this to work also while animating - if self._animating: - return False - - for manager in WindowManager.__managers_list: - if event.keyval == manager._key: - if manager._window.get_property('visible'): - manager.hide() - else: - manager.show() - - def get_geometry(self): - return (self._x, self._y, self._width, self._height) - - def set_geometry(self, x, y, width, height): - if self._window_type == WindowManager.TYPE_ACTIVITY: - logging.error('The geometry will be ignored for activity windows') - - self._x = x - self._y = y - self._width = width - self._height = height - - def set_animation(self, animation): - self._animation = animation - - def set_type(self, window_type): - self._window_type = window_type - - def set_key(self, key): - self._key = key - - def show(self): - self._update_hints() - self._update_size() - - if self._animation == WindowManager.ANIMATION_SLIDE_IN: - self._slide_in() - else: - self._update_position() - self._window.show() - - def hide(self): - if self._animation == WindowManager.ANIMATION_SLIDE_IN: - self._slide_out() - else: - self._window.hide() - - def _get_screen_dimensions(self): - screen_width = DEFAULT_WIDTH - screen_height = DEFAULT_HEIGHT - - for manager in WindowManager.__managers_list: - if manager._window_type == WindowManager.TYPE_ACTIVITY: - screen_width = manager._window.allocation.width - screen_height = manager._window.allocation.height - - return (screen_width, screen_height) - - def _get_screen_position(self): - result = (0, 0) - for manager in WindowManager.__managers_list: - if manager._window_type == WindowManager.TYPE_ACTIVITY: - result = manager._window.get_position() - - return result - - def _transform_position(self): - (screen_width, screen_height) = self._get_screen_dimensions() - (screen_x, screen_y) = self._get_screen_position() - - x = int(screen_width * self._x) + screen_x - y = int(screen_height * self._y) + screen_y - - return (x, y) - - def _transform_dimensions(self): - (screen_width, screen_height) = self._get_screen_dimensions() - - width = int(screen_width * self._width) - height = int(screen_height * self._height) - - return (width, height) - - def _update_hints(self): - if self._window_type == WindowManager.TYPE_POPUP: - self._window.set_decorated(False) - self._window.set_skip_taskbar_hint(True) - - def _update_size(self): - if self._window_type == WindowManager.TYPE_ACTIVITY: - self._window.resize(DEFAULT_WIDTH, DEFAULT_HEIGHT) - else: - (width, height) = self._transform_dimensions() - self._window.resize(width, height) - - def _update_position(self): - if self._window_type == WindowManager.TYPE_POPUP: - (x, y) = self._transform_position() - self._window.move(x, y) - - def __slide_timeout_cb(self, helper): - start = helper.is_start() - - self._y = helper.get_next_y() - self._update_position() - - if start and helper.get_direction() == SlidingHelper.IN: - self._window.show() - elif helper.is_end() and helper.get_direction() == SlidingHelper.OUT: - self._window.hide() - - self._animating = not helper.is_end() - - return not helper.is_end() - - def _slide_in(self): - helper = SlidingHelper(self, SlidingHelper.IN) - gobject.idle_add(self.__slide_timeout_cb, helper) - - def _slide_out(self): - helper = SlidingHelper(self, SlidingHelper.OUT) - gobject.idle_add(self.__slide_timeout_cb, helper) diff --git a/shell/google/GoogleSOAPFacade.py b/shell/google/GoogleSOAPFacade.py deleted file mode 100644 index 0aab3cf4..00000000 --- a/shell/google/GoogleSOAPFacade.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Facade that hides the differences between the SOAPpy and SOAP.py -libraries, so that google.py doesn't have to deal with them. - -@author: Brian Landers -@license: Python -@version: 0.5.4 -""" - -import warnings -from distutils.version import LooseVersion - -__author__ = "Brian Landers " -__version__ = "0.6" -__license__ = "Python" - -# -# Wrapper around the python 'warnings' facility -# -def warn( message, level=RuntimeWarning ): - warnings.warn( message, level, stacklevel=3 ) - -# We can't use older version of SOAPpy, due to bugs that break the Google API -minSOAPpyVersion = "0.11.3" - -# -# Try loading SOAPpy first. If that fails, fall back to the old SOAP.py -# -SOAPpy = None -try: - import SOAPpy - from SOAPpy import SOAPProxy, Types - - if LooseVersion( minSOAPpyVersion ) > \ - LooseVersion( SOAPpy.version.__version__ ): - - warn( "Versions of SOAPpy before %s have known bugs that prevent " + - "PyGoogle from functioning." % minSOAPpyVersion ) - raise ImportError - -except ImportError: - warn( "SOAPpy not imported. Trying legacy SOAP.py.", - DeprecationWarning ) - try: - import SOAP - except ImportError: - raise RuntimeError( "Unable to find SOAPpy or SOAP. Can't continue.\n" ) - -# -# Constants that differ between the modules -# -if SOAPpy: - false = Types.booleanType(0) - true = Types.booleanType(1) - structType = Types.structType - faultType = Types.faultType -else: - false = SOAP.booleanType(0) - true = SOAP.booleanType(1) - structType = SOAP.structType - faultType = SOAP.faultType - -# -# Get a SOAP Proxy object in the correct way for the module we're using -# -def getProxy( url, namespace, http_proxy ): - if SOAPpy: - return SOAPProxy( url, - namespace = namespace, - http_proxy = http_proxy ) - - else: - return SOAP.SOAPProxy( url, - namespace = namespace, - http_proxy = http_proxy ) - -# -# Convert an object to a dictionary in the proper way for the module -# we're using for SOAP -# -def toDict( obj ): - if SOAPpy: - return obj._asdict() - else: - return obj._asdict diff --git a/shell/google/Makefile.am b/shell/google/Makefile.am deleted file mode 100644 index 0f6057cd..00000000 --- a/shell/google/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -googledir = $(pkgdatadir)/shell/google -google_PYTHON = \ - __init__.py \ - google.py \ - GoogleSOAPFacade.py \ - SOAP.py diff --git a/shell/google/SOAP.py b/shell/google/SOAP.py deleted file mode 100755 index 032a452d..00000000 --- a/shell/google/SOAP.py +++ /dev/null @@ -1,3974 +0,0 @@ -#!/usr/bin/python -################################################################################ -# -# SOAP.py 0.9.7 - Cayce Ullman (cayce@actzero.com) -# Brian Matthews (blm@actzero.com) -# -# INCLUDED: -# - General SOAP Parser based on sax.xml (requires Python 2.0) -# - General SOAP Builder -# - SOAP Proxy for RPC client code -# - SOAP Server framework for RPC server code -# -# FEATURES: -# - Handles all of the types in the BDG -# - Handles faults -# - Allows namespace specification -# - Allows SOAPAction specification -# - Homogeneous typed arrays -# - Supports multiple schemas -# - Header support (mustUnderstand and actor) -# - XML attribute support -# - Multi-referencing support (Parser/Builder) -# - Understands SOAP-ENC:root attribute -# - Good interop, passes all client tests for Frontier, SOAP::LITE, SOAPRMI -# - Encodings -# - SSL clients (with OpenSSL configured in to Python) -# - SSL servers (with OpenSSL configured in to Python and M2Crypto installed) -# -# TODO: -# - Timeout on method calls - MCU -# - Arrays (sparse, multidimensional and partial) - BLM -# - Clean up data types - BLM -# - Type coercion system (Builder) - MCU -# - Early WSDL Support - MCU -# - Attachments - BLM -# - setup.py - MCU -# - mod_python example - MCU -# - medusa example - MCU -# - Documentation - JAG -# - Look at performance -# -################################################################################ -# -# Copyright (c) 2001, Cayce Ullman. -# Copyright (c) 2001, Brian Matthews. -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# Neither the name of actzero, inc. nor the names of its contributors may -# be used to endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -################################################################################ -# -# Additional changes: -# 0.9.7.3 - 4/18/2002 - Mark Pilgrim (f8dy@diveintomark.org) -# added dump_dict as alias for dump_dictionary for Python 2.2 compatibility -# 0.9.7.2 - 4/12/2002 - Mark Pilgrim (f8dy@diveintomark.org) -# fixed logic to unmarshal the value of "null" attributes ("true" or "1" -# means true, others false) -# 0.9.7.1 - 4/11/2002 - Mark Pilgrim (f8dy@diveintomark.org) -# added "dump_str" as alias for "dump_string" for Python 2.2 compatibility -# Between 2.1 and 2.2, type("").__name__ changed from "string" to "str" -################################################################################ - -import xml.sax -import UserList -import base64 -import cgi -import urllib -import exceptions -import copy -import re -import socket -import string -import sys -import time -import SocketServer -from types import * - -try: from M2Crypto import SSL -except: pass - -ident = '$Id: SOAP.py,v 1.1.1.1 2004/01/16 16:15:18 bluecoat93 Exp $' - -__version__ = "0.9.7.3" - -# Platform hackery - -# Check float support -try: - float("NaN") - float("INF") - float("-INF") - good_float = 1 -except: - good_float = 0 - -################################################################################ -# Exceptions -################################################################################ -class Error(exceptions.Exception): - def __init__(self, msg): - self.msg = msg - def __str__(self): - return "" % self.msg - __repr__ = __str__ - -class RecursionError(Error): - pass - -class UnknownTypeError(Error): - pass - -class HTTPError(Error): - # indicates an HTTP protocol error - def __init__(self, code, msg): - self.code = code - self.msg = msg - def __str__(self): - return "" % (self.code, self.msg) - __repr__ = __str__ - -############################################################################## -# Namespace Class -################################################################################ -def invertDict(dict): - d = {} - - for k, v in dict.items(): - d[v] = k - - return d - -class NS: - XML = "http://www.w3.org/XML/1998/namespace" - - ENV = "http://schemas.xmlsoap.org/soap/envelope/" - ENC = "http://schemas.xmlsoap.org/soap/encoding/" - - XSD = "http://www.w3.org/1999/XMLSchema" - XSD2 = "http://www.w3.org/2000/10/XMLSchema" - XSD3 = "http://www.w3.org/2001/XMLSchema" - - XSD_L = [XSD, XSD2, XSD3] - EXSD_L= [ENC, XSD, XSD2, XSD3] - - XSI = "http://www.w3.org/1999/XMLSchema-instance" - XSI2 = "http://www.w3.org/2000/10/XMLSchema-instance" - XSI3 = "http://www.w3.org/2001/XMLSchema-instance" - XSI_L = [XSI, XSI2, XSI3] - - URN = "http://soapinterop.org/xsd" - - # For generated messages - XML_T = "xml" - ENV_T = "SOAP-ENV" - ENC_T = "SOAP-ENC" - XSD_T = "xsd" - XSD2_T= "xsd2" - XSD3_T= "xsd3" - XSI_T = "xsi" - XSI2_T= "xsi2" - XSI3_T= "xsi3" - URN_T = "urn" - - NSMAP = {ENV_T: ENV, ENC_T: ENC, XSD_T: XSD, XSD2_T: XSD2, - XSD3_T: XSD3, XSI_T: XSI, XSI2_T: XSI2, XSI3_T: XSI3, - URN_T: URN} - NSMAP_R = invertDict(NSMAP) - - STMAP = {'1999': (XSD_T, XSI_T), '2000': (XSD2_T, XSI2_T), - '2001': (XSD3_T, XSI3_T)} - STMAP_R = invertDict(STMAP) - - def __init__(self): - raise Error, "Don't instantiate this" - -################################################################################ -# Configuration class -################################################################################ - -class SOAPConfig: - __readonly = ('SSLserver', 'SSLclient') - - def __init__(self, config = None, **kw): - d = self.__dict__ - - if config: - if not isinstance(config, SOAPConfig): - raise AttributeError, \ - "initializer must be SOAPConfig instance" - - s = config.__dict__ - - for k, v in s.items(): - if k[0] != '_': - d[k] = v - else: - # Setting debug also sets returnFaultInfo, dumpFaultInfo, - # dumpHeadersIn, dumpHeadersOut, dumpSOAPIn, and dumpSOAPOut - self.debug = 0 - # Setting namespaceStyle sets typesNamespace, typesNamespaceURI, - # schemaNamespace, and schemaNamespaceURI - self.namespaceStyle = '1999' - self.strictNamespaces = 0 - self.typed = 1 - self.buildWithNamespacePrefix = 1 - self.returnAllAttrs = 0 - - try: SSL; d['SSLserver'] = 1 - except: d['SSLserver'] = 0 - - try: socket.ssl; d['SSLclient'] = 1 - except: d['SSLclient'] = 0 - - for k, v in kw.items(): - if k[0] != '_': - setattr(self, k, v) - - def __setattr__(self, name, value): - if name in self.__readonly: - raise AttributeError, "readonly configuration setting" - - d = self.__dict__ - - if name in ('typesNamespace', 'typesNamespaceURI', - 'schemaNamespace', 'schemaNamespaceURI'): - - if name[-3:] == 'URI': - base, uri = name[:-3], 1 - else: - base, uri = name, 0 - - if type(value) == StringType: - if NS.NSMAP.has_key(value): - n = (value, NS.NSMAP[value]) - elif NS.NSMAP_R.has_key(value): - n = (NS.NSMAP_R[value], value) - else: - raise AttributeError, "unknown namespace" - elif type(value) in (ListType, TupleType): - if uri: - n = (value[1], value[0]) - else: - n = (value[0], value[1]) - else: - raise AttributeError, "unknown namespace type" - - d[base], d[base + 'URI'] = n - - try: - d['namespaceStyle'] = \ - NS.STMAP_R[(d['typesNamespace'], d['schemaNamespace'])] - except: - d['namespaceStyle'] = '' - - elif name == 'namespaceStyle': - value = str(value) - - if not NS.STMAP.has_key(value): - raise AttributeError, "unknown namespace style" - - d[name] = value - n = d['typesNamespace'] = NS.STMAP[value][0] - d['typesNamespaceURI'] = NS.NSMAP[n] - n = d['schemaNamespace'] = NS.STMAP[value][1] - d['schemaNamespaceURI'] = NS.NSMAP[n] - - elif name == 'debug': - d[name] = \ - d['returnFaultInfo'] = \ - d['dumpFaultInfo'] = \ - d['dumpHeadersIn'] = \ - d['dumpHeadersOut'] = \ - d['dumpSOAPIn'] = \ - d['dumpSOAPOut'] = value - - else: - d[name] = value - -Config = SOAPConfig() - -################################################################################ -# Types and Wrappers -################################################################################ - -class anyType: - _validURIs = (NS.XSD, NS.XSD2, NS.XSD3, NS.ENC) - - def __init__(self, data = None, name = None, typed = 1, attrs = None): - if self.__class__ == anyType: - raise Error, "anyType can't be instantiated directly" - - if type(name) in (ListType, TupleType): - self._ns, self._name = name - else: - self._ns, self._name = self._validURIs[0], name - self._typed = typed - self._attrs = {} - - self._cache = None - self._type = self._typeName() - - self._data = self._checkValueSpace(data) - - if attrs != None: - self._setAttrs(attrs) - - def __str__(self): - if self._name: - return "<%s %s at %d>" % (self.__class__, self._name, id(self)) - return "<%s at %d>" % (self.__class__, id(self)) - - __repr__ = __str__ - - def _checkValueSpace(self, data): - return data - - def _marshalData(self): - return str(self._data) - - def _marshalAttrs(self, ns_map, builder): - a = '' - - for attr, value in self._attrs.items(): - ns, n = builder.genns(ns_map, attr[0]) - a += n + ' %s%s="%s"' % \ - (ns, attr[1], cgi.escape(str(value), 1)) - - return a - - def _fixAttr(self, attr): - if type(attr) in (StringType, UnicodeType): - attr = (None, attr) - elif type(attr) == ListType: - attr = tuple(attr) - elif type(attr) != TupleType: - raise AttributeError, "invalid attribute type" - - if len(attr) != 2: - raise AttributeError, "invalid attribute length" - - if type(attr[0]) not in (NoneType, StringType, UnicodeType): - raise AttributeError, "invalid attribute namespace URI type" - - return attr - - def _getAttr(self, attr): - attr = self._fixAttr(attr) - - try: - return self._attrs[attr] - except: - return None - - def _setAttr(self, attr, value): - attr = self._fixAttr(attr) - - self._attrs[attr] = str(value) - - def _setAttrs(self, attrs): - if type(attrs) in (ListType, TupleType): - for i in range(0, len(attrs), 2): - self._setAttr(attrs[i], attrs[i + 1]) - - return - - if type(attrs) == DictType: - d = attrs - elif isinstance(attrs, anyType): - d = attrs._attrs - else: - raise AttributeError, "invalid attribute type" - - for attr, value in d.items(): - self._setAttr(attr, value) - - def _setMustUnderstand(self, val): - self._setAttr((NS.ENV, "mustUnderstand"), val) - - def _getMustUnderstand(self): - return self._getAttr((NS.ENV, "mustUnderstand")) - - def _setActor(self, val): - self._setAttr((NS.ENV, "actor"), val) - - def _getActor(self): - return self._getAttr((NS.ENV, "actor")) - - def _typeName(self): - return self.__class__.__name__[:-4] - - def _validNamespaceURI(self, URI, strict): - if not self._typed: - return None - if URI in self._validURIs: - return URI - if not strict: - return self._ns - raise AttributeError, \ - "not a valid namespace for type %s" % self._type - -class voidType(anyType): - pass - -class stringType(anyType): - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - return data - -class untypedType(stringType): - def __init__(self, data = None, name = None, attrs = None): - stringType.__init__(self, data, name, 0, attrs) - -class IDType(stringType): pass -class NCNameType(stringType): pass -class NameType(stringType): pass -class ENTITYType(stringType): pass -class IDREFType(stringType): pass -class languageType(stringType): pass -class NMTOKENType(stringType): pass -class QNameType(stringType): pass - -class tokenType(anyType): - _validURIs = (NS.XSD2, NS.XSD3) - __invalidre = '[\n\t]|^ | $| ' - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - if type(self.__invalidre) == StringType: - self.__invalidre = re.compile(self.__invalidre) - - if self.__invalidre.search(data): - raise ValueError, "invalid %s value" % self._type - - return data - -class normalizedStringType(anyType): - _validURIs = (NS.XSD3,) - __invalidre = '[\n\r\t]' - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - if type(self.__invalidre) == StringType: - self.__invalidre = re.compile(self.__invalidre) - - if self.__invalidre.search(data): - raise ValueError, "invalid %s value" % self._type - - return data - -class CDATAType(normalizedStringType): - _validURIs = (NS.XSD2,) - -class booleanType(anyType): - def __int__(self): - return self._data - - __nonzero__ = __int__ - - def _marshalData(self): - return ['false', 'true'][self._data] - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if data in (0, '0', 'false', ''): - return 0 - if data in (1, '1', 'true'): - return 1 - raise ValueError, "invalid %s value" % self._type - -class decimalType(anyType): - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType, FloatType): - raise Error, "invalid %s value" % self._type - - return data - -class floatType(anyType): - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType, FloatType) or \ - data < -3.4028234663852886E+38 or \ - data > 3.4028234663852886E+38: - raise ValueError, "invalid %s value" % self._type - - return data - - def _marshalData(self): - return "%.18g" % self._data # More precision - -class doubleType(anyType): - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType, FloatType) or \ - data < -1.7976931348623158E+308 or \ - data > 1.7976931348623157E+308: - raise ValueError, "invalid %s value" % self._type - - return data - - def _marshalData(self): - return "%.18g" % self._data # More precision - -class durationType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - try: - # A tuple or a scalar is OK, but make them into a list - - if type(data) == TupleType: - data = list(data) - elif type(data) != ListType: - data = [data] - - if len(data) > 6: - raise Exception, "too many values" - - # Now check the types of all the components, and find - # the first nonzero element along the way. - - f = -1 - - for i in range(len(data)): - if data[i] == None: - data[i] = 0 - continue - - if type(data[i]) not in \ - (IntType, LongType, FloatType): - raise Exception, "element %d a bad type" % i - - if data[i] and f == -1: - f = i - - # If they're all 0, just use zero seconds. - - if f == -1: - self._cache = 'PT0S' - - return (0,) * 6 - - # Make sure only the last nonzero element has a decimal fraction - # and only the first element is negative. - - d = -1 - - for i in range(f, len(data)): - if data[i]: - if d != -1: - raise Exception, \ - "all except the last nonzero element must be " \ - "integers" - if data[i] < 0 and i > f: - raise Exception, \ - "only the first nonzero element can be negative" - elif data[i] != long(data[i]): - d = i - - # Pad the list on the left if necessary. - - if len(data) < 6: - n = 6 - len(data) - f += n - d += n - data = [0] * n + data - - # Save index of the first nonzero element and the decimal - # element for _marshalData. - - self.__firstnonzero = f - self.__decimal = d - - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - d = self._data - t = 0 - - if d[self.__firstnonzero] < 0: - s = '-P' - else: - s = 'P' - - t = 0 - - for i in range(self.__firstnonzero, len(d)): - if d[i]: - if i > 2 and not t: - s += 'T' - t = 1 - if self.__decimal == i: - s += "%g" % abs(d[i]) - else: - s += "%d" % long(abs(d[i])) - s += ['Y', 'M', 'D', 'H', 'M', 'S'][i] - - self._cache = s - - return self._cache - -class timeDurationType(durationType): - _validURIs = (NS.XSD, NS.XSD2, NS.ENC) - -class dateTimeType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.time() - - if (type(data) in (IntType, LongType)): - data = list(time.gmtime(data)[:6]) - elif (type(data) == FloatType): - f = data - int(data) - data = list(time.gmtime(int(data))[:6]) - data[5] += f - elif type(data) in (ListType, TupleType): - if len(data) < 6: - raise Exception, "not enough values" - if len(data) > 9: - raise Exception, "too many values" - - data = list(data[:6]) - - cleanDate(data) - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - d = self._data - s = "%04d-%02d-%02dT%02d:%02d:%02d" % ((abs(d[0]),) + d[1:]) - if d[0] < 0: - s = '-' + s - f = d[5] - int(d[5]) - if f != 0: - s += ("%g" % f)[1:] - s += 'Z' - - self._cache = s - - return self._cache - -class recurringInstantType(anyType): - _validURIs = (NS.XSD,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = list(time.gmtime(time.time())[:6]) - if (type(data) in (IntType, LongType)): - data = list(time.gmtime(data)[:6]) - elif (type(data) == FloatType): - f = data - int(data) - data = list(time.gmtime(int(data))[:6]) - data[5] += f - elif type(data) in (ListType, TupleType): - if len(data) < 1: - raise Exception, "not enough values" - if len(data) > 9: - raise Exception, "too many values" - - data = list(data[:6]) - - if len(data) < 6: - data += [0] * (6 - len(data)) - - f = len(data) - - for i in range(f): - if data[i] == None: - if f < i: - raise Exception, \ - "only leftmost elements can be none" - else: - f = i - break - - cleanDate(data, f) - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - d = self._data - e = list(d) - neg = '' - - if e[0] < 0: - neg = '-' - e[0] = abs(e[0]) - - if not e[0]: - e[0] = '--' - elif e[0] < 100: - e[0] = '-' + "%02d" % e[0] - else: - e[0] = "%04d" % e[0] - - for i in range(1, len(e)): - if e[i] == None or (i < 3 and e[i] == 0): - e[i] = '-' - else: - if e[i] < 0: - neg = '-' - e[i] = abs(e[i]) - - e[i] = "%02d" % e[i] - - if d[5]: - f = abs(d[5] - int(d[5])) - - if f: - e[5] += ("%g" % f)[1:] - - s = "%s%s-%s-%sT%s:%s:%sZ" % ((neg,) + tuple(e)) - - self._cache = s - - return self._cache - -class timeInstantType(dateTimeType): - _validURIs = (NS.XSD, NS.XSD2, NS.ENC) - -class timePeriodType(dateTimeType): - _validURIs = (NS.XSD2, NS.ENC) - -class timeType(anyType): - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[3:6] - elif (type(data) == FloatType): - f = data - int(data) - data = list(time.gmtime(int(data))[3:6]) - data[2] += f - elif type(data) in (IntType, LongType): - data = time.gmtime(data)[3:6] - elif type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[3:6] - elif len(data) > 3: - raise Exception, "too many values" - - data = [None, None, None] + list(data) - - if len(data) < 6: - data += [0] * (6 - len(data)) - - cleanDate(data, 3) - - data = data[3:] - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - d = self._data - s = '' - - s = time.strftime("%H:%M:%S", (0, 0, 0) + d + (0, 0, -1)) - f = d[2] - int(d[2]) - if f != 0: - s += ("%g" % f)[1:] - s += 'Z' - - self._cache = s - - return self._cache - -class dateType(anyType): - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[0:3] - elif type(data) in (IntType, LongType, FloatType): - data = time.gmtime(data)[0:3] - elif type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[0:3] - elif len(data) > 3: - raise Exception, "too many values" - - data = list(data) - - if len(data) < 3: - data += [1, 1, 1][len(data):] - - data += [0, 0, 0] - - cleanDate(data) - - data = data[:3] - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - d = self._data - s = "%04d-%02d-%02dZ" % ((abs(d[0]),) + d[1:]) - if d[0] < 0: - s = '-' + s - - self._cache = s - - return self._cache - -class gYearMonthType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[0:2] - elif type(data) in (IntType, LongType, FloatType): - data = time.gmtime(data)[0:2] - elif type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[0:2] - elif len(data) > 2: - raise Exception, "too many values" - - data = list(data) - - if len(data) < 2: - data += [1, 1][len(data):] - - data += [1, 0, 0, 0] - - cleanDate(data) - - data = data[:2] - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - d = self._data - s = "%04d-%02dZ" % ((abs(d[0]),) + d[1:]) - if d[0] < 0: - s = '-' + s - - self._cache = s - - return self._cache - -class gYearType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[0:1] - elif type(data) in (IntType, LongType, FloatType): - data = [data] - - if type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[0:1] - elif len(data) < 1: - raise Exception, "too few values" - elif len(data) > 1: - raise Exception, "too many values" - - if type(data[0]) == FloatType: - try: s = int(data[0]) - except: s = long(data[0]) - - if s != data[0]: - raise Exception, "not integral" - - data = [s] - elif type(data[0]) not in (IntType, LongType): - raise Exception, "bad type" - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return data[0] - - def _marshalData(self): - if self._cache == None: - d = self._data - s = "%04dZ" % abs(d) - if d < 0: - s = '-' + s - - self._cache = s - - return self._cache - -class centuryType(anyType): - _validURIs = (NS.XSD2, NS.ENC) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[0:1] / 100 - elif type(data) in (IntType, LongType, FloatType): - data = [data] - - if type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[0:1] / 100 - elif len(data) < 1: - raise Exception, "too few values" - elif len(data) > 1: - raise Exception, "too many values" - - if type(data[0]) == FloatType: - try: s = int(data[0]) - except: s = long(data[0]) - - if s != data[0]: - raise Exception, "not integral" - - data = [s] - elif type(data[0]) not in (IntType, LongType): - raise Exception, "bad type" - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return data[0] - - def _marshalData(self): - if self._cache == None: - d = self._data - s = "%02dZ" % abs(d) - if d < 0: - s = '-' + s - - self._cache = s - - return self._cache - -class yearType(gYearType): - _validURIs = (NS.XSD2, NS.ENC) - -class gMonthDayType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[1:3] - elif type(data) in (IntType, LongType, FloatType): - data = time.gmtime(data)[1:3] - elif type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[0:2] - elif len(data) > 2: - raise Exception, "too many values" - - data = list(data) - - if len(data) < 2: - data += [1, 1][len(data):] - - data = [0] + data + [0, 0, 0] - - cleanDate(data, 1) - - data = data[1:3] - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return tuple(data) - - def _marshalData(self): - if self._cache == None: - self._cache = "--%02d-%02dZ" % self._data - - return self._cache - -class recurringDateType(gMonthDayType): - _validURIs = (NS.XSD2, NS.ENC) - -class gMonthType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[1:2] - elif type(data) in (IntType, LongType, FloatType): - data = [data] - - if type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[1:2] - elif len(data) < 1: - raise Exception, "too few values" - elif len(data) > 1: - raise Exception, "too many values" - - if type(data[0]) == FloatType: - try: s = int(data[0]) - except: s = long(data[0]) - - if s != data[0]: - raise Exception, "not integral" - - data = [s] - elif type(data[0]) not in (IntType, LongType): - raise Exception, "bad type" - - if data[0] < 1 or data[0] > 12: - raise Exception, "bad value" - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return data[0] - - def _marshalData(self): - if self._cache == None: - self._cache = "--%02d--Z" % self._data - - return self._cache - -class monthType(gMonthType): - _validURIs = (NS.XSD2, NS.ENC) - -class gDayType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - try: - if data == None: - data = time.gmtime(time.time())[2:3] - elif type(data) in (IntType, LongType, FloatType): - data = [data] - - if type(data) in (ListType, TupleType): - if len(data) == 9: - data = data[2:3] - elif len(data) < 1: - raise Exception, "too few values" - elif len(data) > 1: - raise Exception, "too many values" - - if type(data[0]) == FloatType: - try: s = int(data[0]) - except: s = long(data[0]) - - if s != data[0]: - raise Exception, "not integral" - - data = [s] - elif type(data[0]) not in (IntType, LongType): - raise Exception, "bad type" - - if data[0] < 1 or data[0] > 31: - raise Exception, "bad value" - else: - raise Exception, "invalid type" - except Exception, e: - raise ValueError, "invalid %s value - %s" % (self._type, e) - - return data[0] - - def _marshalData(self): - if self._cache == None: - self._cache = "---%02dZ" % self._data - - return self._cache - -class recurringDayType(gDayType): - _validURIs = (NS.XSD2, NS.ENC) - -class hexBinaryType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - return data - - def _marshalData(self): - if self._cache == None: - self._cache = encodeHexString(self._data) - - return self._cache - -class base64BinaryType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - return data - - def _marshalData(self): - if self._cache == None: - self._cache = base64.encodestring(self._data) - - return self._cache - -class base64Type(base64BinaryType): - _validURIs = (NS.ENC,) - -class binaryType(anyType): - _validURIs = (NS.XSD, NS.ENC) - - def __init__(self, data, name = None, typed = 1, encoding = 'base64', - attrs = None): - - anyType.__init__(self, data, name, typed, attrs) - - self._setAttr('encoding', encoding) - - def _marshalData(self): - if self._cache == None: - if self._getAttr((None, 'encoding')) == 'base64': - self._cache = base64.encodestring(self._data) - else: - self._cache = encodeHexString(self._data) - - return self._cache - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - return data - - def _setAttr(self, attr, value): - attr = self._fixAttr(attr) - - if attr[1] == 'encoding': - if attr[0] != None or value not in ('base64', 'hex'): - raise AttributeError, "invalid encoding" - - self._cache = None - - anyType._setAttr(self, attr, value) - - -class anyURIType(anyType): - _validURIs = (NS.XSD3,) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (StringType, UnicodeType): - raise AttributeError, "invalid %s type" % self._type - - return data - - def _marshalData(self): - if self._cache == None: - self._cache = urllib.quote(self._data) - - return self._cache - -class uriType(anyURIType): - _validURIs = (NS.XSD,) - -class uriReferenceType(anyURIType): - _validURIs = (NS.XSD2,) - -class NOTATIONType(anyType): - def __init__(self, data, name = None, typed = 1, attrs = None): - - if self.__class__ == NOTATIONType: - raise Error, "a NOTATION can't be instantiated directly" - - anyType.__init__(self, data, name, typed, attrs) - -class ENTITIESType(anyType): - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) in (StringType, UnicodeType): - return (data,) - - if type(data) not in (ListType, TupleType) or \ - filter (lambda x: type(x) not in (StringType, UnicodeType), data): - raise AttributeError, "invalid %s type" % self._type - - return data - - def _marshalData(self): - return ' '.join(self._data) - -class IDREFSType(ENTITIESType): pass -class NMTOKENSType(ENTITIESType): pass - -class integerType(anyType): - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType): - raise ValueError, "invalid %s value" % self._type - - return data - -class nonPositiveIntegerType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or data > 0: - raise ValueError, "invalid %s value" % self._type - - return data - -class non_Positive_IntegerType(nonPositiveIntegerType): - _validURIs = (NS.XSD,) - - def _typeName(self): - return 'non-positive-integer' - -class negativeIntegerType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or data >= 0: - raise ValueError, "invalid %s value" % self._type - - return data - -class negative_IntegerType(negativeIntegerType): - _validURIs = (NS.XSD,) - - def _typeName(self): - return 'negative-integer' - -class longType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < -9223372036854775808L or \ - data > 9223372036854775807L: - raise ValueError, "invalid %s value" % self._type - - return data - -class intType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < -2147483648L or \ - data > 2147483647: - raise ValueError, "invalid %s value" % self._type - - return data - -class shortType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < -32768 or \ - data > 32767: - raise ValueError, "invalid %s value" % self._type - - return data - -class byteType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < -128 or \ - data > 127: - raise ValueError, "invalid %s value" % self._type - - return data - -class nonNegativeIntegerType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or data < 0: - raise ValueError, "invalid %s value" % self._type - - return data - -class non_Negative_IntegerType(nonNegativeIntegerType): - _validURIs = (NS.XSD,) - - def _typeName(self): - return 'non-negative-integer' - -class unsignedLongType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < 0 or \ - data > 18446744073709551615L: - raise ValueError, "invalid %s value" % self._type - - return data - -class unsignedIntType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < 0 or \ - data > 4294967295L: - raise ValueError, "invalid %s value" % self._type - - return data - -class unsignedShortType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < 0 or \ - data > 65535: - raise ValueError, "invalid %s value" % self._type - - return data - -class unsignedByteType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or \ - data < 0 or \ - data > 255: - raise ValueError, "invalid %s value" % self._type - - return data - -class positiveIntegerType(anyType): - _validURIs = (NS.XSD2, NS.XSD3, NS.ENC) - - def _checkValueSpace(self, data): - if data == None: - raise ValueError, "must supply initial %s value" % self._type - - if type(data) not in (IntType, LongType) or data <= 0: - raise ValueError, "invalid %s value" % self._type - - return data - -class positive_IntegerType(positiveIntegerType): - _validURIs = (NS.XSD,) - - def _typeName(self): - return 'positive-integer' - -# Now compound types - -class compoundType(anyType): - def __init__(self, data = None, name = None, typed = 1, attrs = None): - if self.__class__ == compoundType: - raise Error, "a compound can't be instantiated directly" - - anyType.__init__(self, data, name, typed, attrs) - self._aslist = [] - self._asdict = {} - self._keyord = [] - - if type(data) == DictType: - self.__dict__.update(data) - - def __getitem__(self, item): - if type(item) == IntType: - return self._aslist[item] - return getattr(self, item) - - def __len__(self): - return len(self._aslist) - - def __nonzero__(self): - return 1 - - def _keys(self): - return filter(lambda x: x[0] != '_', self.__dict__.keys()) - - def _addItem(self, name, value, attrs = None): - d = self._asdict - - if d.has_key(name): - if type(d[name]) != ListType: - d[name] = [d[name]] - d[name].append(value) - else: - d[name] = value - - self._keyord.append(name) - self._aslist.append(value) - self.__dict__[name] = d[name] - - def _placeItem(self, name, value, pos, subpos = 0, attrs = None): - d = self._asdict - - if subpos == 0 and type(d[name]) != ListType: - d[name] = value - else: - d[name][subpos] = value - - self._keyord[pos] = name - self._aslist[pos] = value - self.__dict__[name] = d[name] - - def _getItemAsList(self, name, default = []): - try: - d = self.__dict__[name] - except: - return default - - if type(d) == ListType: - return d - return [d] - -class structType(compoundType): - pass - -class headerType(structType): - _validURIs = (NS.ENV,) - - def __init__(self, data = None, typed = 1, attrs = None): - structType.__init__(self, data, "Header", typed, attrs) - -class bodyType(structType): - _validURIs = (NS.ENV,) - - def __init__(self, data = None, typed = 1, attrs = None): - structType.__init__(self, data, "Body", typed, attrs) - -class arrayType(UserList.UserList, compoundType): - def __init__(self, data = None, name = None, attrs = None, - offset = 0, rank = None, asize = 0, elemsname = None): - - if data: - if type(data) not in (ListType, TupleType): - raise Error, "Data must be a sequence" - - UserList.UserList.__init__(self, data) - compoundType.__init__(self, data, name, 0, attrs) - - self._elemsname = elemsname or "item" - - if data == None: - self._rank = rank - - # According to 5.4.2.2 in the SOAP spec, each element in a - # sparse array must have a position. _posstate keeps track of - # whether we've seen a position or not. It's possible values - # are: - # -1 No elements have been added, so the state is indeterminate - # 0 An element without a position has been added, so no - # elements can have positions - # 1 An element with a position has been added, so all elements - # must have positions - - self._posstate = -1 - - self._full = 0 - - if asize in ('', None): - asize = '0' - - self._dims = map (lambda x: int(x), str(asize).split(',')) - self._dims.reverse() # It's easier to work with this way - self._poss = [0] * len(self._dims) # This will end up - # reversed too - - for i in range(len(self._dims)): - if self._dims[i] < 0 or \ - self._dims[i] == 0 and len(self._dims) > 1: - raise TypeError, "invalid Array dimensions" - - if offset > 0: - self._poss[i] = offset % self._dims[i] - offset = int(offset / self._dims[i]) - - # Don't break out of the loop if offset is 0 so we test all the - # dimensions for > 0. - if offset: - raise AttributeError, "invalid Array offset" - - a = [None] * self._dims[0] - - for i in range(1, len(self._dims)): - b = [] - - for j in range(self._dims[i]): - b.append(copy.deepcopy(a)) - - a = b - - self.data = a - - def _addItem(self, name, value, attrs): - if self._full: - raise ValueError, "Array is full" - - pos = attrs.get((NS.ENC, 'position')) - - if pos != None: - if self._posstate == 0: - raise AttributeError, \ - "all elements in a sparse Array must have a " \ - "position attribute" - - self._posstate = 1 - - try: - if pos[0] == '[' and pos[-1] == ']': - pos = map (lambda x: int(x), pos[1:-1].split(',')) - pos.reverse() - - if len(pos) == 1: - pos = pos[0] - - curpos = [0] * len(self._dims) - - for i in range(len(self._dims)): - curpos[i] = pos % self._dims[i] - pos = int(pos / self._dims[i]) - - if pos == 0: - break - - if pos: - raise Exception - elif len(pos) != len(self._dims): - raise Exception - else: - for i in range(len(self._dims)): - if pos[i] >= self._dims[i]: - raise Exception - - curpos = pos - else: - raise Exception - except: - raise AttributeError, \ - "invalid Array element position %s" % str(pos) - else: - if self._posstate == 1: - raise AttributeError, \ - "only elements in a sparse Array may have a " \ - "position attribute" - - self._posstate = 0 - - curpos = self._poss - - a = self.data - - for i in range(len(self._dims) - 1, 0, -1): - a = a[curpos[i]] - - if curpos[0] >= len(a): - a += [None] * (len(a) - curpos[0] + 1) - - a[curpos[0]] = value - - if pos == None: - self._poss[0] += 1 - - for i in range(len(self._dims) - 1): - if self._poss[i] < self._dims[i]: - break - - self._poss[i] = 0 - self._poss[i + 1] += 1 - - if self._dims[-1] and self._poss[-1] >= self._dims[-1]: - self._full = 1 - - def _placeItem(self, name, value, pos, subpos, attrs = None): - curpos = [0] * len(self._dims) - - for i in range(len(self._dims)): - if self._dims[i] == 0: - curpos[0] = pos - break - - curpos[i] = pos % self._dims[i] - pos = int(pos / self._dims[i]) - - if pos == 0: - break - - if self._dims[i] != 0 and pos: - raise Error, "array index out of range" - - a = self.data - - for i in range(len(self._dims) - 1, 0, -1): - a = a[curpos[i]] - - if curpos[0] >= len(a): - a += [None] * (len(a) - curpos[0] + 1) - - a[curpos[0]] = value - -class typedArrayType(arrayType): - def __init__(self, data = None, name = None, typed = None, attrs = None, - offset = 0, rank = None, asize = 0, elemsname = None): - - arrayType.__init__(self, data, name, attrs, offset, rank, asize, - elemsname) - - self._type = typed - -class faultType(structType, Error): - def __init__(self, faultcode = "", faultstring = "", detail = None): - self.faultcode = faultcode - self.faultstring = faultstring - if detail != None: - self.detail = detail - - structType.__init__(self, None, 0) - - def _setDetail(self, detail = None): - if detail != None: - self.detail = detail - else: - try: del self.detail - except AttributeError: pass - - def __repr__(self): - return "" % (self.faultcode, self.faultstring) - - __str__ = __repr__ - -################################################################################ -class RefHolder: - def __init__(self, name, frame): - self.name = name - self.parent = frame - self.pos = len(frame) - self.subpos = frame.namecounts.get(name, 0) - - def __repr__(self): - return "<%s %s at %d>" % (self.__class__, self.name, id(self)) - -################################################################################ -# Utility infielders -################################################################################ -def collapseWhiteSpace(s): - return re.sub('\s+', ' ', s).strip() - -def decodeHexString(data): - conv = {'0': 0x0, '1': 0x1, '2': 0x2, '3': 0x3, '4': 0x4, - '5': 0x5, '6': 0x6, '7': 0x7, '8': 0x8, '9': 0x9, 'a': 0xa, - 'b': 0xb, 'c': 0xc, 'd': 0xd, 'e': 0xe, 'f': 0xf, 'A': 0xa, - 'B': 0xb, 'C': 0xc, 'D': 0xd, 'E': 0xe, 'F': 0xf,} - ws = string.whitespace - - bin = '' - - i = 0 - - while i < len(data): - if data[i] not in ws: - break - i += 1 - - low = 0 - - while i < len(data): - c = data[i] - - if c in string.whitespace: - break - - try: - c = conv[c] - except KeyError: - raise ValueError, \ - "invalid hex string character `%s'" % c - - if low: - bin += chr(high * 16 + c) - low = 0 - else: - high = c - low = 1 - - i += 1 - - if low: - raise ValueError, "invalid hex string length" - - while i < len(data): - if data[i] not in string.whitespace: - raise ValueError, \ - "invalid hex string character `%s'" % c - - i += 1 - - return bin - -def encodeHexString(data): - h = '' - - for i in data: - h += "%02X" % ord(i) - - return h - -def leapMonth(year, month): - return month == 2 and \ - year % 4 == 0 and \ - (year % 100 != 0 or year % 400 == 0) - -def cleanDate(d, first = 0): - ranges = (None, (1, 12), (1, 31), (0, 23), (0, 59), (0, 61)) - months = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) - names = ('year', 'month', 'day', 'hours', 'minutes', 'seconds') - - if len(d) != 6: - raise ValueError, "date must have 6 elements" - - for i in range(first, 6): - s = d[i] - - if type(s) == FloatType: - if i < 5: - try: - s = int(s) - except OverflowError: - if i > 0: - raise - s = long(s) - - if s != d[i]: - raise ValueError, "%s must be integral" % names[i] - - d[i] = s - elif type(s) == LongType: - try: s = int(s) - except: pass - elif type(s) != IntType: - raise TypeError, "%s isn't a valid type" % names[i] - - if i == first and s < 0: - continue - - if ranges[i] != None and \ - (s < ranges[i][0] or ranges[i][1] < s): - raise ValueError, "%s out of range" % names[i] - - if first < 6 and d[5] >= 61: - raise ValueError, "seconds out of range" - - if first < 2: - leap = first < 1 and leapMonth(d[0], d[1]) - - if d[2] > months[d[1]] + leap: - raise ValueError, "day out of range" - -class UnderflowError(exceptions.ArithmeticError): - pass - -def debugHeader(title): - s = '*** ' + title + ' ' - print s + ('*' * (72 - len(s))) - -def debugFooter(title): - print '*' * 72 - sys.stdout.flush() - -################################################################################ -# SOAP Parser -################################################################################ -class SOAPParser(xml.sax.handler.ContentHandler): - class Frame: - def __init__(self, name, kind = None, attrs = {}, rules = {}): - self.name = name - self.kind = kind - self.attrs = attrs - self.rules = rules - - self.contents = [] - self.names = [] - self.namecounts = {} - self.subattrs = [] - - def append(self, name, data, attrs): - self.names.append(name) - self.contents.append(data) - self.subattrs.append(attrs) - - if self.namecounts.has_key(name): - self.namecounts[name] += 1 - else: - self.namecounts[name] = 1 - - def _placeItem(self, name, value, pos, subpos = 0, attrs = None): - self.contents[pos] = value - - if attrs: - self.attrs.update(attrs) - - def __len__(self): - return len(self.contents) - - def __repr__(self): - return "<%s %s at %d>" % (self.__class__, self.name, id(self)) - - def __init__(self, rules = None): - xml.sax.handler.ContentHandler.__init__(self) - self.body = None - self.header = None - self.attrs = {} - self._data = None - self._next = "E" # Keeping state for message validity - self._stack = [self.Frame('SOAP')] - - # Make two dictionaries to store the prefix <-> URI mappings, and - # initialize them with the default - self._prem = {NS.XML_T: NS.XML} - self._prem_r = {NS.XML: NS.XML_T} - self._ids = {} - self._refs = {} - self._rules = rules - - def startElementNS(self, name, qname, attrs): - # Workaround two sax bugs - if name[0] == None and name[1][0] == ' ': - name = (None, name[1][1:]) - else: - name = tuple(name) - - # First some checking of the layout of the message - - if self._next == "E": - if name[1] != 'Envelope': - raise Error, "expected `SOAP-ENV:Envelope', got `%s:%s'" % \ - (self._prem_r[name[0]], name[1]) - if name[0] != NS.ENV: - raise faultType, ("%s:VersionMismatch" % NS.ENV_T, - "Don't understand version `%s' Envelope" % name[0]) - else: - self._next = "HorB" - elif self._next == "HorB": - if name[0] == NS.ENV and name[1] in ("Header", "Body"): - self._next = None - else: - raise Error, \ - "expected `SOAP-ENV:Header' or `SOAP-ENV:Body', " \ - "got `%s'" % self._prem_r[name[0]] + ':' + name[1] - elif self._next == "B": - if name == (NS.ENV, "Body"): - self._next = None - else: - raise Error, "expected `SOAP-ENV:Body', got `%s'" % \ - self._prem_r[name[0]] + ':' + name[1] - elif self._next == "": - raise Error, "expected nothing, got `%s'" % \ - self._prem_r[name[0]] + ':' + name[1] - - if len(self._stack) == 2: - rules = self._rules - else: - try: - rules = self._stack[-1].rules[name[1]] - except: - rules = None - - if type(rules) not in (NoneType, DictType): - kind = rules - else: - kind = attrs.get((NS.ENC, 'arrayType')) - - if kind != None: - del attrs._attrs[(NS.ENC, 'arrayType')] - - i = kind.find(':') - if i >= 0: - kind = (self._prem[kind[:i]], kind[i + 1:]) - else: - kind = None - - self.pushFrame(self.Frame(name[1], kind, attrs._attrs, rules)) - - self._data = '' # Start accumulating - - def pushFrame(self, frame): - self._stack.append(frame) - - def popFrame(self): - return self._stack.pop() - - def endElementNS(self, name, qname): - # Workaround two sax bugs - if name[0] == None and name[1][0] == ' ': - ns, name = None, name[1][1:] - else: - ns, name = tuple(name) - - if self._next == "E": - raise Error, "didn't get SOAP-ENV:Envelope" - if self._next in ("HorB", "B"): - raise Error, "didn't get SOAP-ENV:Body" - - cur = self.popFrame() - attrs = cur.attrs - - idval = None - - if attrs.has_key((None, 'id')): - idval = attrs[(None, 'id')] - - if self._ids.has_key(idval): - raise Error, "duplicate id `%s'" % idval - - del attrs[(None, 'id')] - - root = 1 - - if len(self._stack) == 3: - if attrs.has_key((NS.ENC, 'root')): - root = int(attrs[(NS.ENC, 'root')]) - - # Do some preliminary checks. First, if root="0" is present, - # the element must have an id. Next, if root="n" is present, - # n something other than 0 or 1, raise an exception. - - if root == 0: - if idval == None: - raise Error, "non-root element must have an id" - elif root != 1: - raise Error, "SOAP-ENC:root must be `0' or `1'" - - del attrs[(NS.ENC, 'root')] - - while 1: - href = attrs.get((None, 'href')) - if href: - if href[0] != '#': - raise Error, "only do local hrefs right now" - if self._data != None and self._data.strip() != '': - raise Error, "hrefs can't have data" - - href = href[1:] - - if self._ids.has_key(href): - data = self._ids[href] - else: - data = RefHolder(name, self._stack[-1]) - - if self._refs.has_key(href): - self._refs[href].append(data) - else: - self._refs[href] = [data] - - del attrs[(None, 'href')] - - break - - kind = None - - if attrs: - for i in NS.XSI_L: - if attrs.has_key((i, 'type')): - kind = attrs[(i, 'type')] - del attrs[(i, 'type')] - - if kind != None: - i = kind.find(':') - if i >= 0: - kind = (self._prem[kind[:i]], kind[i + 1:]) - else: -# XXX What to do here? (None, kind) is just going to fail in convertType - kind = (None, kind) - - null = 0 - - if attrs: - for i in (NS.XSI, NS.XSI2): - if attrs.has_key((i, 'null')): - null = attrs[(i, 'null')] - del attrs[(i, 'null')] - - if attrs.has_key((NS.XSI3, 'nil')): - null = attrs[(NS.XSI3, 'nil')] - del attrs[(NS.XSI3, 'nil')] - - #MAP 4/12/2002 - must also support "true" - #null = int(null) - null = (str(null).lower() in ['true', '1']) - - if null: - if len(cur) or \ - (self._data != None and self._data.strip() != ''): - raise Error, "nils can't have data" - - data = None - - break - - if len(self._stack) == 2: - if (ns, name) == (NS.ENV, "Header"): - self.header = data = headerType(attrs = attrs) - self._next = "B" - break - elif (ns, name) == (NS.ENV, "Body"): - self.body = data = bodyType(attrs = attrs) - self._next = "" - break - elif len(self._stack) == 3 and self._next == None: - if (ns, name) == (NS.ENV, "Fault"): - data = faultType() - self._next = "" - break - - if cur.rules != None: - rule = cur.rules - - if type(rule) in (StringType, UnicodeType): -# XXX Need a namespace here - rule = (None, rule) - elif type(rule) == ListType: - rule = tuple(rule) - -# XXX What if rule != kind? - if callable(rule): - data = rule(self._data) - elif type(rule) == DictType: - data = structType(name = (ns, name), attrs = attrs) - else: - data = self.convertType(self._data, rule, attrs) - - break - - if (kind == None and cur.kind != None) or \ - (kind == (NS.ENC, 'Array')): - kind = cur.kind - - if kind == None: - kind = 'ur-type[%d]' % len(cur) - else: - kind = kind[1] - - if len(cur.namecounts) == 1: - elemsname = cur.names[0] - else: - elemsname = None - - data = self.startArray((ns, name), kind, attrs, elemsname) - - break - - if len(self._stack) == 3 and kind == None and \ - len(cur) == 0 and \ - (self._data == None or self._data.strip() == ''): - data = structType(name = (ns, name), attrs = attrs) - break - - if len(cur) == 0 and ns != NS.URN: - # Nothing's been added to the current frame so it must be a - # simple type. - - if kind == None: - # If the current item's container is an array, it will - # have a kind. If so, get the bit before the first [, - # which is the type of the array, therefore the type of - # the current item. - - kind = self._stack[-1].kind - - if kind != None: - i = kind[1].find('[') - if i >= 0: - kind = (kind[0], kind[1][:i]) - elif ns != None: - kind = (ns, name) - - if kind != None: - try: - data = self.convertType(self._data, kind, attrs) - except UnknownTypeError: - data = None - else: - data = None - - if data == None: - data = self._data or '' - - if len(attrs) == 0: - try: data = str(data) - except: pass - - break - - data = structType(name = (ns, name), attrs = attrs) - - break - - if isinstance(data, compoundType): - for i in range(len(cur)): - v = cur.contents[i] - data._addItem(cur.names[i], v, cur.subattrs[i]) - - if isinstance(v, RefHolder): - v.parent = data - - if root: - self._stack[-1].append(name, data, attrs) - - if idval != None: - self._ids[idval] = data - - if self._refs.has_key(idval): - for i in self._refs[idval]: - i.parent._placeItem(i.name, data, i.pos, i.subpos, attrs) - - del self._refs[idval] - - self.attrs[id(data)] = attrs - - if isinstance(data, anyType): - data._setAttrs(attrs) - - self._data = None # Stop accumulating - - def endDocument(self): - if len(self._refs) == 1: - raise Error, \ - "unresolved reference " + self._refs.keys()[0] - elif len(self._refs) > 1: - raise Error, \ - "unresolved references " + ', '.join(self._refs.keys()) - - def startPrefixMapping(self, prefix, uri): - self._prem[prefix] = uri - self._prem_r[uri] = prefix - - def endPrefixMapping(self, prefix): - try: - del self._prem_r[self._prem[prefix]] - del self._prem[prefix] - except: - pass - - def characters(self, c): - if self._data != None: - self._data += c - - arrayre = '^(?:(?P[^:]*):)?' \ - '(?P[^[]+)' \ - '(?:\[(?P,*)\])?' \ - '(?:\[(?P\d+(?:,\d+)*)?\])$' - - def startArray(self, name, kind, attrs, elemsname): - if type(self.arrayre) == StringType: - self.arrayre = re.compile (self.arrayre) - - offset = attrs.get((NS.ENC, "offset")) - - if offset != None: - del attrs[(NS.ENC, "offset")] - - try: - if offset[0] == '[' and offset[-1] == ']': - offset = int(offset[1:-1]) - if offset < 0: - raise Exception - else: - raise Exception - except: - raise AttributeError, "invalid Array offset" - else: - offset = 0 - - try: - m = self.arrayre.search(kind) - - if m == None: - raise Exception - - t = m.group('type') - - if t == 'ur-type': - return arrayType(None, name, attrs, offset, m.group('rank'), - m.group('asize'), elemsname) - elif m.group('ns') != None: - return typedArrayType(None, name, - (self._prem[m.group('ns')], t), attrs, offset, - m.group('rank'), m.group('asize'), elemsname) - else: - return typedArrayType(None, name, (None, t), attrs, offset, - m.group('rank'), m.group('asize'), elemsname) - except: - raise AttributeError, "invalid Array type `%s'" % kind - - # Conversion - - class DATETIMECONSTS: - SIGNre = '(?P-?)' - CENTURYre = '(?P\d{2,})' - YEARre = '(?P\d{2})' - MONTHre = '(?P\d{2})' - DAYre = '(?P\d{2})' - HOURre = '(?P\d{2})' - MINUTEre = '(?P\d{2})' - SECONDre = '(?P\d{2}(?:\.\d*)?)' - TIMEZONEre = '(?PZ)|(?P[-+])(?P\d{2}):' \ - '(?P\d{2})' - BOSre = '^\s*' - EOSre = '\s*$' - - __allres = {'sign': SIGNre, 'century': CENTURYre, 'year': YEARre, - 'month': MONTHre, 'day': DAYre, 'hour': HOURre, - 'minute': MINUTEre, 'second': SECONDre, 'timezone': TIMEZONEre, - 'b': BOSre, 'e': EOSre} - - dateTime = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)sT' \ - '%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % __allres - timeInstant = dateTime - timePeriod = dateTime - time = '%(b)s%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % \ - __allres - date = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)s' \ - '(%(timezone)s)?%(e)s' % __allres - century = '%(b)s%(sign)s%(century)s(%(timezone)s)?%(e)s' % __allres - gYearMonth = '%(b)s%(sign)s%(century)s%(year)s-%(month)s' \ - '(%(timezone)s)?%(e)s' % __allres - gYear = '%(b)s%(sign)s%(century)s%(year)s(%(timezone)s)?%(e)s' % \ - __allres - year = gYear - gMonthDay = '%(b)s--%(month)s-%(day)s(%(timezone)s)?%(e)s' % __allres - recurringDate = gMonthDay - gDay = '%(b)s---%(day)s(%(timezone)s)?%(e)s' % __allres - recurringDay = gDay - gMonth = '%(b)s--%(month)s--(%(timezone)s)?%(e)s' % __allres - month = gMonth - - recurringInstant = '%(b)s%(sign)s(%(century)s|-)(%(year)s|-)-' \ - '(%(month)s|-)-(%(day)s|-)T' \ - '(%(hour)s|-):(%(minute)s|-):(%(second)s|-)' \ - '(%(timezone)s)?%(e)s' % __allres - - duration = '%(b)s%(sign)sP' \ - '((?P\d+)Y)?' \ - '((?P\d+)M)?' \ - '((?P\d+)D)?' \ - '((?PT)' \ - '((?P\d+)H)?' \ - '((?P\d+)M)?' \ - '((?P\d*(?:\.\d*)?)S)?)?%(e)s' % \ - __allres - - timeDuration = duration - - # The extra 31 on the front is: - # - so the tuple is 1-based - # - so months[month-1] is December's days if month is 1 - - months = (31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) - - def convertDateTime(self, value, kind): - def getZoneOffset(d): - zoffs = 0 - - try: - if d['zulu'] == None: - zoffs = 60 * int(d['tzhour']) + int(d['tzminute']) - if d['tzsign'] != '-': - zoffs = -zoffs - except TypeError: - pass - - return zoffs - - def applyZoneOffset(months, zoffs, date, minfield, posday = 1): - if zoffs == 0 and (minfield > 4 or 0 <= date[5] < 60): - return date - - if minfield > 5: date[5] = 0 - if minfield > 4: date[4] = 0 - - if date[5] < 0: - date[4] += int(date[5]) / 60 - date[5] %= 60 - - date[4] += zoffs - - if minfield > 3 or 0 <= date[4] < 60: return date - - date[3] += date[4] / 60 - date[4] %= 60 - - if minfield > 2 or 0 <= date[3] < 24: return date - - date[2] += date[3] / 24 - date[3] %= 24 - - if minfield > 1: - if posday and date[2] <= 0: - date[2] += 31 # zoffs is at most 99:59, so the - # day will never be less than -3 - return date - - while 1: - # The date[1] == 3 (instead of == 2) is because we're - # going back a month, so we need to know if the previous - # month is February, so we test if this month is March. - - leap = minfield == 0 and date[1] == 3 and \ - date[0] % 4 == 0 and \ - (date[0] % 100 != 0 or date[0] % 400 == 0) - - if 0 < date[2] <= months[date[1]] + leap: break - - date[2] += months[date[1] - 1] + leap - - date[1] -= 1 - - if date[1] > 0: break - - date[1] = 12 - - if minfield > 0: break - - date[0] -= 1 - - return date - - try: - exp = getattr(self.DATETIMECONSTS, kind) - except AttributeError: - return None - - if type(exp) == StringType: - exp = re.compile(exp) - setattr (self.DATETIMECONSTS, kind, exp) - - m = exp.search(value) - - try: - if m == None: - raise Exception - - d = m.groupdict() - f = ('century', 'year', 'month', 'day', - 'hour', 'minute', 'second') - fn = len(f) # Index of first non-None value - r = [] - - if kind in ('duration', 'timeDuration'): - if d['sep'] != None and d['hour'] == None and \ - d['minute'] == None and d['second'] == None: - raise Exception - - f = f[1:] - - for i in range(len(f)): - s = d[f[i]] - - if s != None: - if f[i] == 'second': - s = float(s) - else: - try: s = int(s) - except ValueError: s = long(s) - - if i < fn: fn = i - - r.append(s) - - if fn > len(r): # Any non-Nones? - raise Exception - - if d['sign'] == '-': - r[fn] = -r[fn] - - return tuple(r) - - if kind == 'recurringInstant': - for i in range(len(f)): - s = d[f[i]] - - if s == None or s == '-': - if i > fn: - raise Exception - s = None - else: - if i < fn: - fn = i - - if f[i] == 'second': - s = float(s) - else: - try: - s = int(s) - except ValueError: - s = long(s) - - r.append(s) - - s = r.pop(0) - - if fn == 0: - r[0] += s * 100 - else: - fn -= 1 - - if fn < len(r) and d['sign'] == '-': - r[fn] = -r[fn] - - cleanDate(r, fn) - - return tuple(applyZoneOffset(self.DATETIMECONSTS.months, - getZoneOffset(d), r, fn, 0)) - - r = [0, 0, 1, 1, 0, 0, 0] - - for i in range(len(f)): - field = f[i] - - s = d.get(field) - - if s != None: - if field == 'second': - s = float(s) - else: - try: - s = int(s) - except ValueError: - s = long(s) - - if i < fn: - fn = i - - r[i] = s - - if fn > len(r): # Any non-Nones? - raise Exception - - s = r.pop(0) - - if fn == 0: - r[0] += s * 100 - else: - fn -= 1 - - if d.get('sign') == '-': - r[fn] = -r[fn] - - cleanDate(r, fn) - - zoffs = getZoneOffset(d) - - if zoffs: - r = applyZoneOffset(self.DATETIMECONSTS.months, zoffs, r, fn) - - if kind == 'century': - return r[0] / 100 - - s = [] - - for i in range(1, len(f)): - if d.has_key(f[i]): - s.append(r[i - 1]) - - if len(s) == 1: - return s[0] - return tuple(s) - except Exception, e: - raise Error, "invalid %s value `%s' - %s" % (kind, value, e) - - intlimits = \ - { - 'nonPositiveInteger': (0, None, 0), - 'non-positive-integer': (0, None, 0), - 'negativeInteger': (0, None, -1), - 'negative-integer': (0, None, -1), - 'long': (1, -9223372036854775808L, - 9223372036854775807L), - 'int': (0, -2147483648L, 2147483647), - 'short': (0, -32768, 32767), - 'byte': (0, -128, 127), - 'nonNegativeInteger': (0, 0, None), - 'non-negative-integer': (0, 0, None), - 'positiveInteger': (0, 1, None), - 'positive-integer': (0, 1, None), - 'unsignedLong': (1, 0, 18446744073709551615L), - 'unsignedInt': (0, 0, 4294967295L), - 'unsignedShort': (0, 0, 65535), - 'unsignedByte': (0, 0, 255), - } - floatlimits = \ - { - 'float': (7.0064923216240861E-46, -3.4028234663852886E+38, - 3.4028234663852886E+38), - 'double': (2.4703282292062327E-324, -1.7976931348623158E+308, - 1.7976931348623157E+308), - } - zerofloatre = '[1-9]' - - def convertType(self, d, t, attrs): - dnn = d or '' - - if t[0] in NS.EXSD_L: - if t[1] == "integer": - try: - d = int(d) - if len(attrs): - d = long(d) - except: - d = long(d) - return d - if self.intlimits.has_key (t[1]): - l = self.intlimits[t[1]] - try: d = int(d) - except: d = long(d) - - if l[1] != None and d < l[1]: - raise UnderflowError, "%s too small" % d - if l[2] != None and d > l[2]: - raise OverflowError, "%s too large" % d - - if l[0] or len(attrs): - return long(d) - return d - if t[1] == "string": - if len(attrs): - return unicode(dnn) - try: - return str(dnn) - except: - return dnn - if t[1] == "boolean": - d = d.strip().lower() - if d in ('0', 'false'): - return 0 - if d in ('1', 'true'): - return 1 - raise AttributeError, "invalid boolean value" - if self.floatlimits.has_key (t[1]): - l = self.floatlimits[t[1]] - s = d.strip().lower() - try: - d = float(s) - except: - # Some platforms don't implement the float stuff. This - # is close, but NaN won't be > "INF" as required by the - # standard. - - if s in ("nan", "inf"): - return 1e300**2 - if s == "-inf": - return -1e300**2 - - raise - - if str (d) == 'nan': - if s != 'nan': - raise ValueError, "invalid %s" % t[1] - elif str (d) == '-inf': - if s != '-inf': - raise UnderflowError, "%s too small" % t[1] - elif str (d) == 'inf': - if s != 'inf': - raise OverflowError, "%s too large" % t[1] - elif d < 0: - if d < l[1]: - raise UnderflowError, "%s too small" % t[1] - elif d > 0: - if d < l[0] or d > l[2]: - raise OverflowError, "%s too large" % t[1] - elif d == 0: - if type(self.zerofloatre) == StringType: - self.zerofloatre = re.compile(self.zerofloatre) - - if self.zerofloatre.search(s): - raise UnderflowError, "invalid %s" % t[1] - - return d - if t[1] in ("dateTime", "date", "timeInstant", "time"): - return self.convertDateTime(d, t[1]) - if t[1] == "decimal": - return float(d) - if t[1] in ("language", "QName", "NOTATION", "NMTOKEN", "Name", - "NCName", "ID", "IDREF", "ENTITY"): - return collapseWhiteSpace(d) - if t[1] in ("IDREFS", "ENTITIES", "NMTOKENS"): - d = collapseWhiteSpace(d) - return d.split() - if t[0] in NS.XSD_L: - if t[1] in ("base64", "base64Binary"): - return base64.decodestring(d) - if t[1] == "hexBinary": - return decodeHexString(d) - if t[1] == "anyURI": - return urllib.unquote(collapseWhiteSpace(d)) - if t[1] in ("normalizedString", "token"): - return collapseWhiteSpace(d) - if t[0] == NS.ENC: - if t[1] == "base64": - return base64.decodestring(d) - if t[0] == NS.XSD: - if t[1] == "binary": - try: - e = attrs[(None, 'encoding')] - - if e == 'hex': - return decodeHexString(d) - elif e == 'base64': - return base64.decodestring(d) - except: - pass - - raise Error, "unknown or missing binary encoding" - if t[1] == "uri": - return urllib.unquote(collapseWhiteSpace(d)) - if t[1] == "recurringInstant": - return self.convertDateTime(d, t[1]) - if t[0] in (NS.XSD2, NS.ENC): - if t[1] == "uriReference": - return urllib.unquote(collapseWhiteSpace(d)) - if t[1] == "timePeriod": - return self.convertDateTime(d, t[1]) - if t[1] in ("century", "year"): - return self.convertDateTime(d, t[1]) - if t[0] in (NS.XSD, NS.XSD2, NS.ENC): - if t[1] == "timeDuration": - return self.convertDateTime(d, t[1]) - if t[0] == NS.XSD3: - if t[1] == "anyURI": - return urllib.unquote(collapseWhiteSpace(d)) - if t[1] in ("gYearMonth", "gMonthDay"): - return self.convertDateTime(d, t[1]) - if t[1] == "gYear": - return self.convertDateTime(d, t[1]) - if t[1] == "gMonth": - return self.convertDateTime(d, t[1]) - if t[1] == "gDay": - return self.convertDateTime(d, t[1]) - if t[1] == "duration": - return self.convertDateTime(d, t[1]) - if t[0] in (NS.XSD2, NS.XSD3): - if t[1] == "token": - return collapseWhiteSpace(d) - if t[1] == "recurringDate": - return self.convertDateTime(d, t[1]) - if t[1] == "month": - return self.convertDateTime(d, t[1]) - if t[1] == "recurringDay": - return self.convertDateTime(d, t[1]) - if t[0] == NS.XSD2: - if t[1] == "CDATA": - return collapseWhiteSpace(d) - - raise UnknownTypeError, "unknown type `%s'" % (t[0] + ':' + t[1]) - -################################################################################ -# call to SOAPParser that keeps all of the info -################################################################################ -def _parseSOAP(xml_str, rules = None): - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO - - parser = xml.sax.make_parser() - t = SOAPParser(rules = rules) - parser.setContentHandler(t) - e = xml.sax.handler.ErrorHandler() - parser.setErrorHandler(e) - - inpsrc = xml.sax.xmlreader.InputSource() - inpsrc.setByteStream(StringIO(xml_str)) - - # turn on namespace mangeling - parser.setFeature(xml.sax.handler.feature_namespaces,1) - - parser.parse(inpsrc) - - return t - -################################################################################ -# SOAPParser's more public interface -################################################################################ -def parseSOAP(xml_str, attrs = 0): - t = _parseSOAP(xml_str) - - if attrs: - return t.body, t.attrs - return t.body - - -def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None): - t = _parseSOAP(xml_str, rules = rules) - p = t.body._aslist[0] - - # Empty string, for RPC this translates into a void - if type(p) in (type(''), type(u'')) and p in ('', u''): - name = "Response" - for k in t.body.__dict__.keys(): - if k[0] != "_": - name = k - p = structType(name) - - if header or body or attrs: - ret = (p,) - if header : ret += (t.header,) - if body: ret += (t.body,) - if attrs: ret += (t.attrs,) - return ret - else: - return p - - -################################################################################ -# SOAP Builder -################################################################################ -class SOAPBuilder: - _xml_top = '\n' - _xml_enc_top = '\n' - _env_top = '%(ENV_T)s:Envelope %(ENV_T)s:encodingStyle="%(ENC)s"' % \ - NS.__dict__ - _env_bot = '\n' % NS.__dict__ - - # Namespaces potentially defined in the Envelope tag. - - _env_ns = {NS.ENC: NS.ENC_T, NS.ENV: NS.ENV_T, - NS.XSD: NS.XSD_T, NS.XSD2: NS.XSD2_T, NS.XSD3: NS.XSD3_T, - NS.XSI: NS.XSI_T, NS.XSI2: NS.XSI2_T, NS.XSI3: NS.XSI3_T} - - def __init__(self, args = (), kw = {}, method = None, namespace = None, - header = None, methodattrs = None, envelope = 1, encoding = 'UTF-8', - use_refs = 0, config = Config): - - # Test the encoding, raising an exception if it's not known - if encoding != None: - ''.encode(encoding) - - self.args = args - self.kw = kw - self.envelope = envelope - self.encoding = encoding - self.method = method - self.namespace = namespace - self.header = header - self.methodattrs= methodattrs - self.use_refs = use_refs - self.config = config - self.out = '' - self.tcounter = 0 - self.ncounter = 1 - self.icounter = 1 - self.envns = {} - self.ids = {} - self.depth = 0 - self.multirefs = [] - self.multis = 0 - self.body = not isinstance(args, bodyType) - - def build(self): - ns_map = {} - - # Cache whether typing is on or not - typed = self.config.typed - - if self.header: - # Create a header. - self.dump(self.header, "Header", typed = typed) - self.header = None # Wipe it out so no one is using it. - if self.body: - # Call genns to record that we've used SOAP-ENV. - self.depth += 1 - body_ns = self.genns(ns_map, NS.ENV)[0] - self.out += "<%sBody>\n" % body_ns - - if self.method: - self.depth += 1 - a = '' - if self.methodattrs: - for (k, v) in self.methodattrs.items(): - a += ' %s="%s"' % (k, v) - - if self.namespace: # Use the namespace info handed to us - methodns, n = self.genns(ns_map, self.namespace) - else: - methodns, n = '', '' - - self.out += '<%s%s%s%s%s>\n' % \ - (methodns, self.method, n, a, self.genroot(ns_map)) - - try: - if type(self.args) != TupleType: - args = (self.args,) - else: - args = self.args - - for i in args: - self.dump(i, typed = typed, ns_map = ns_map) - - for (k, v) in self.kw.items(): - self.dump(v, k, typed = typed, ns_map = ns_map) - except RecursionError: - if self.use_refs == 0: - # restart - b = SOAPBuilder(args = self.args, kw = self.kw, - method = self.method, namespace = self.namespace, - header = self.header, methodattrs = self.methodattrs, - envelope = self.envelope, encoding = self.encoding, - use_refs = 1, config = self.config) - return b.build() - raise - - if self.method: - self.out += "\n" % (methodns, self.method) - self.depth -= 1 - - if self.body: - # dump may add to self.multirefs, but the for loop will keep - # going until it has used all of self.multirefs, even those - # entries added while in the loop. - - self.multis = 1 - - for obj, tag in self.multirefs: - self.dump(obj, tag, typed = typed, ns_map = ns_map) - - self.out += "\n" % body_ns - self.depth -= 1 - - if self.envelope: - e = map (lambda ns: 'xmlns:%s="%s"' % (ns[1], ns[0]), - self.envns.items()) - - self.out = '<' + self._env_top + ' '.join([''] + e) + '>\n' + \ - self.out + \ - self._env_bot - - if self.encoding != None: - self.out = self._xml_enc_top % self.encoding + self.out - - return self.out.encode(self.encoding) - - return self._xml_top + self.out - - def gentag(self): - self.tcounter += 1 - return "v%d" % self.tcounter - - def genns(self, ns_map, nsURI): - if nsURI == None: - return ('', '') - - if type(nsURI) == TupleType: # already a tuple - if len(nsURI) == 2: - ns, nsURI = nsURI - else: - ns, nsURI = None, nsURI[0] - else: - ns = None - - if ns_map.has_key(nsURI): - return (ns_map[nsURI] + ':', '') - - if self._env_ns.has_key(nsURI): - ns = self.envns[nsURI] = ns_map[nsURI] = self._env_ns[nsURI] - return (ns + ':', '') - - if not ns: - ns = "ns%d" % self.ncounter - self.ncounter += 1 - ns_map[nsURI] = ns - if self.config.buildWithNamespacePrefix: - return (ns + ':', ' xmlns:%s="%s"' % (ns, nsURI)) - else: - return ('', ' xmlns="%s"' % (nsURI)) - - def genroot(self, ns_map): - if self.depth != 2: - return '' - - ns, n = self.genns(ns_map, NS.ENC) - return ' %sroot="%d"%s' % (ns, not self.multis, n) - - # checkref checks an element to see if it needs to be encoded as a - # multi-reference element or not. If it returns None, the element has - # been handled and the caller can continue with subsequent elements. - # If it returns a string, the string should be included in the opening - # tag of the marshaled element. - - def checkref(self, obj, tag, ns_map): - if self.depth < 2: - return '' - - if not self.ids.has_key(id(obj)): - n = self.ids[id(obj)] = self.icounter - self.icounter = n + 1 - - if self.use_refs == 0: - return '' - - if self.depth == 2: - return ' id="i%d"' % n - - self.multirefs.append((obj, tag)) - else: - if self.use_refs == 0: - raise RecursionError, "Cannot serialize recursive object" - - n = self.ids[id(obj)] - - if self.multis and self.depth == 2: - return ' id="i%d"' % n - - self.out += '<%s href="#i%d"%s/>\n' % (tag, n, self.genroot(ns_map)) - return None - - # dumpers - - def dump(self, obj, tag = None, typed = 1, ns_map = {}): - ns_map = ns_map.copy() - self.depth += 1 - - if type(tag) not in (NoneType, StringType, UnicodeType): - raise KeyError, "tag must be a string or None" - - try: - meth = getattr(self, "dump_" + type(obj).__name__) - meth(obj, tag, typed, ns_map) - except AttributeError: - if type(obj) == LongType: - obj_type = "integer" - else: - obj_type = type(obj).__name__ - - self.out += self.dumper(None, obj_type, obj, tag, typed, - ns_map, self.genroot(ns_map)) - - self.depth -= 1 - - # generic dumper - def dumper(self, nsURI, obj_type, obj, tag, typed = 1, ns_map = {}, - rootattr = '', id = '', - xml = '<%(tag)s%(type)s%(id)s%(attrs)s%(root)s>%(data)s\n'): - - if nsURI == None: - nsURI = self.config.typesNamespaceURI - - tag = tag or self.gentag() - - a = n = t = '' - if typed and obj_type: - ns, n = self.genns(ns_map, nsURI) - ins = self.genns(ns_map, self.config.schemaNamespaceURI)[0] - t = ' %stype="%s%s"%s' % (ins, ns, obj_type, n) - - try: a = obj._marshalAttrs(ns_map, self) - except: pass - - try: data = obj._marshalData() - except: data = obj - - return xml % {"tag": tag, "type": t, "data": data, "root": rootattr, - "id": id, "attrs": a} - - def dump_float(self, obj, tag, typed = 1, ns_map = {}): - # Terrible windows hack - if not good_float: - if obj == float(1e300**2): - obj = "INF" - elif obj == float(-1e300**2): - obj = "-INF" - - obj = str(obj) - if obj in ('inf', '-inf'): - obj = str(obj).upper() - elif obj == 'nan': - obj = 'NaN' - self.out += self.dumper(None, "float", obj, tag, typed, ns_map, - self.genroot(ns_map)) - - def dump_string(self, obj, tag, typed = 0, ns_map = {}): - tag = tag or self.gentag() - - id = self.checkref(obj, tag, ns_map) - if id == None: - return - - try: data = obj._marshalData() - except: data = obj - - self.out += self.dumper(None, "string", cgi.escape(data), tag, - typed, ns_map, self.genroot(ns_map), id) - - dump_unicode = dump_string - dump_str = dump_string # 4/12/2002 - MAP - for Python 2.2 - - def dump_None(self, obj, tag, typed = 0, ns_map = {}): - tag = tag or self.gentag() - ns = self.genns(ns_map, self.config.schemaNamespaceURI)[0] - - self.out += '<%s %snull="1"%s/>\n' % (tag, ns, self.genroot(ns_map)) - - def dump_list(self, obj, tag, typed = 1, ns_map = {}): - if type(obj) == InstanceType: - data = obj.data - else: - data = obj - - tag = tag or self.gentag() - - id = self.checkref(obj, tag, ns_map) - if id == None: - return - - try: - sample = data[0] - empty = 0 - except: - sample = structType() - empty = 1 - - # First scan list to see if all are the same type - same_type = 1 - - if not empty: - for i in data[1:]: - if type(sample) != type(i) or \ - (type(sample) == InstanceType and \ - sample.__class__ != i.__class__): - same_type = 0 - break - - ndecl = '' - if same_type: - if (isinstance(sample, structType)) or \ - type(sample) == DictType: # force to urn struct - - try: - tns = obj._ns or NS.URN - except: - tns = NS.URN - - ns, ndecl = self.genns(ns_map, tns) - - try: - typename = last._typename - except: - typename = "SOAPStruct" - - t = ns + typename - - elif isinstance(sample, anyType): - ns = sample._validNamespaceURI(self.config.typesNamespaceURI, - self.config.strictNamespaces) - if ns: - ns, ndecl = self.genns(ns_map, ns) - t = ns + sample._type - else: - t = 'ur-type' - else: - t = self.genns(ns_map, self.config.typesNamespaceURI)[0] + \ - type(sample).__name__ - else: - t = self.genns(ns_map, self.config.typesNamespaceURI)[0] + \ - "ur-type" - - try: a = obj._marshalAttrs(ns_map, self) - except: a = '' - - ens, edecl = self.genns(ns_map, NS.ENC) - ins, idecl = self.genns(ns_map, self.config.schemaNamespaceURI) - - self.out += \ - '<%s %sarrayType="%s[%d]" %stype="%sArray"%s%s%s%s%s%s>\n' %\ - (tag, ens, t, len(data), ins, ens, ndecl, edecl, idecl, - self.genroot(ns_map), id, a) - - typed = not same_type - - try: elemsname = obj._elemsname - except: elemsname = "item" - - for i in data: - self.dump(i, elemsname, typed, ns_map) - - self.out += '\n' % tag - - dump_tuple = dump_list - - def dump_dictionary(self, obj, tag, typed = 1, ns_map = {}): - tag = tag or self.gentag() - - id = self.checkref(obj, tag, ns_map) - if id == None: - return - - try: a = obj._marshalAttrs(ns_map, self) - except: a = '' - - self.out += '<%s%s%s%s>\n' % \ - (tag, id, a, self.genroot(ns_map)) - - for (k, v) in obj.items(): - if k[0] != "_": - self.dump(v, k, 1, ns_map) - - self.out += '\n' % tag - dump_dict = dump_dictionary # 4/18/2002 - MAP - for Python 2.2 - - def dump_instance(self, obj, tag, typed = 1, ns_map = {}): - if not tag: - # If it has a name use it. - if isinstance(obj, anyType) and obj._name: - tag = obj._name - else: - tag = self.gentag() - - if isinstance(obj, arrayType): # Array - self.dump_list(obj, tag, typed, ns_map) - return - - if isinstance(obj, faultType): # Fault - cns, cdecl = self.genns(ns_map, NS.ENC) - vns, vdecl = self.genns(ns_map, NS.ENV) - self.out += '''<%sFault %sroot="1"%s%s> -%s -%s -''' % (vns, cns, vdecl, cdecl, obj.faultcode, obj.faultstring) - if hasattr(obj, "detail"): - self.dump(obj.detail, "detail", typed, ns_map) - self.out += "\n" % vns - return - - r = self.genroot(ns_map) - - try: a = obj._marshalAttrs(ns_map, self) - except: a = '' - - if isinstance(obj, voidType): # void - self.out += "<%s%s%s>\n" % (tag, a, r, tag) - return - - id = self.checkref(obj, tag, ns_map) - if id == None: - return - - if isinstance(obj, structType): - # Check for namespace - ndecl = '' - ns = obj._validNamespaceURI(self.config.typesNamespaceURI, - self.config.strictNamespaces) - if ns: - ns, ndecl = self.genns(ns_map, ns) - tag = ns + tag - self.out += "<%s%s%s%s%s>\n" % (tag, ndecl, id, a, r) - - # If we have order use it. - order = 1 - - for i in obj._keys(): - if i not in obj._keyord: - order = 0 - break - if order: - for i in range(len(obj._keyord)): - self.dump(obj._aslist[i], obj._keyord[i], 1, ns_map) - else: - # don't have pristine order information, just build it. - for (k, v) in obj.__dict__.items(): - if k[0] != "_": - self.dump(v, k, 1, ns_map) - - if isinstance(obj, bodyType): - self.multis = 1 - - for v, k in self.multirefs: - self.dump(v, k, typed = typed, ns_map = ns_map) - - self.out += '\n' % tag - - elif isinstance(obj, anyType): - t = '' - - if typed: - ns = obj._validNamespaceURI(self.config.typesNamespaceURI, - self.config.strictNamespaces) - if ns: - ons, ondecl = self.genns(ns_map, ns) - ins, indecl = self.genns(ns_map, - self.config.schemaNamespaceURI) - t = ' %stype="%s%s"%s%s' % \ - (ins, ons, obj._type, ondecl, indecl) - - self.out += '<%s%s%s%s%s>%s\n' % \ - (tag, t, id, a, r, obj._marshalData(), tag) - - else: # Some Class - self.out += '<%s%s%s>\n' % (tag, id, r) - - for (k, v) in obj.__dict__.items(): - if k[0] != "_": - self.dump(v, k, 1, ns_map) - - self.out += '\n' % tag - - -################################################################################ -# SOAPBuilder's more public interface -################################################################################ -def buildSOAP(args=(), kw={}, method=None, namespace=None, header=None, - methodattrs=None,envelope=1,encoding='UTF-8',config=Config): - t = SOAPBuilder(args=args,kw=kw, method=method, namespace=namespace, - header=header, methodattrs=methodattrs,envelope=envelope, - encoding=encoding, config=config) - return t.build() - -################################################################################ -# RPC -################################################################################ - -def SOAPUserAgent(): - return "SOAP.py " + __version__ + " (actzero.com)" - -################################################################################ -# Client -################################################################################ -class SOAPAddress: - def __init__(self, url, config = Config): - proto, uri = urllib.splittype(url) - - # apply some defaults - if uri[0:2] != '//': - if proto != None: - uri = proto + ':' + uri - - uri = '//' + uri - proto = 'http' - - host, path = urllib.splithost(uri) - - try: - int(host) - host = 'localhost:' + host - except: - pass - - if not path: - path = '/' - - if proto not in ('http', 'https'): - raise IOError, "unsupported SOAP protocol" - if proto == 'https' and not config.SSLclient: - raise AttributeError, \ - "SSL client not supported by this Python installation" - - self.proto = proto - self.host = host - self.path = path - - def __str__(self): - return "%(proto)s://%(host)s%(path)s" % self.__dict__ - - __repr__ = __str__ - - -class HTTPTransport: - # Need a Timeout someday? - def call(self, addr, data, soapaction = '', encoding = None, - http_proxy = None, config = Config): - - import httplib - - if not isinstance(addr, SOAPAddress): - addr = SOAPAddress(addr, config) - - # Build a request - if http_proxy: - real_addr = http_proxy - real_path = addr.proto + "://" + addr.host + addr.path - else: - real_addr = addr.host - real_path = addr.path - - if addr.proto == 'https': - r = httplib.HTTPS(real_addr) - else: - r = httplib.HTTP(real_addr) - - r.putrequest("POST", real_path) - - r.putheader("Host", addr.host) - r.putheader("User-agent", SOAPUserAgent()) - t = 'text/xml'; - if encoding != None: - t += '; charset="%s"' % encoding - r.putheader("Content-type", t) - r.putheader("Content-length", str(len(data))) - r.putheader("SOAPAction", '"%s"' % soapaction) - - if config.dumpHeadersOut: - s = 'Outgoing HTTP headers' - debugHeader(s) - print "POST %s %s" % (real_path, r._http_vsn_str) - print "Host:", addr.host - print "User-agent: SOAP.py " + __version__ + " (actzero.com)" - print "Content-type:", t - print "Content-length:", len(data) - print 'SOAPAction: "%s"' % soapaction - debugFooter(s) - - r.endheaders() - - if config.dumpSOAPOut: - s = 'Outgoing SOAP' - debugHeader(s) - print data, - if data[-1] != '\n': - print - debugFooter(s) - - # send the payload - r.send(data) - - # read response line - code, msg, headers = r.getreply() - - if config.dumpHeadersIn: - s = 'Incoming HTTP headers' - debugHeader(s) - if headers.headers: - print "HTTP/1.? %d %s" % (code, msg) - print "\n".join(map (lambda x: x.strip(), headers.headers)) - else: - print "HTTP/0.9 %d %s" % (code, msg) - debugFooter(s) - - if config.dumpSOAPIn: - data = r.getfile().read() - - s = 'Incoming SOAP' - debugHeader(s) - print data, - if data[-1] != '\n': - print - debugFooter(s) - - if code not in (200, 500): - raise HTTPError(code, msg) - - if not config.dumpSOAPIn: - data = r.getfile().read() - - # return response payload - return data - -################################################################################ -# SOAP Proxy -################################################################################ -class SOAPProxy: - def __init__(self, proxy, namespace = None, soapaction = '', - header = None, methodattrs = None, transport = HTTPTransport, - encoding = 'UTF-8', throw_faults = 1, unwrap_results = 1, - http_proxy=None, config = Config): - - # Test the encoding, raising an exception if it's not known - if encoding != None: - ''.encode(encoding) - - self.proxy = SOAPAddress(proxy, config) - self.namespace = namespace - self.soapaction = soapaction - self.header = header - self.methodattrs = methodattrs - self.transport = transport() - self.encoding = encoding - self.throw_faults = throw_faults - self.unwrap_results = unwrap_results - self.http_proxy = http_proxy - self.config = config - - - def __call(self, name, args, kw, ns = None, sa = None, hd = None, - ma = None): - - ns = ns or self.namespace - ma = ma or self.methodattrs - - if sa: # Get soapaction - if type(sa) == TupleType: sa = sa[0] - else: - sa = self.soapaction - - if hd: # Get header - if type(hd) == TupleType: - hd = hd[0] - else: - hd = self.header - - hd = hd or self.header - - if ma: # Get methodattrs - if type(ma) == TupleType: ma = ma[0] - else: - ma = self.methodattrs - ma = ma or self.methodattrs - - m = buildSOAP(args = args, kw = kw, method = name, namespace = ns, - header = hd, methodattrs = ma, encoding = self.encoding, - config = self.config) - #print m - - r = self.transport.call(self.proxy, m, sa, encoding = self.encoding, - http_proxy = self.http_proxy, - config = self.config) - - #print r - p, attrs = parseSOAPRPC(r, attrs = 1) - - try: - throw_struct = self.throw_faults and \ - isinstance (p, faultType) - except: - throw_struct = 0 - - if throw_struct: - raise p - - # Bubble a regular result up, if there is only element in the - # struct, assume that is the result and return it. - # Otherwise it will return the struct with all the elements - # as attributes. - if self.unwrap_results: - try: - count = 0 - for i in p.__dict__.keys(): - if i[0] != "_": # don't move the private stuff - count += 1 - t = getattr(p, i) - if count == 1: p = t # Only one piece of data, bubble it up - except: - pass - - if self.config.returnAllAttrs: - return p, attrs - return p - - def _callWithBody(self, body): - return self.__call(None, body, {}) - - def __getattr__(self, name): # hook to catch method calls - return self.__Method(self.__call, name, config = self.config) - - # To handle attribute wierdness - class __Method: - # Some magic to bind a SOAP method to an RPC server. - # Supports "nested" methods (e.g. examples.getStateName) -- concept - # borrowed from xmlrpc/soaplib -- www.pythonware.com - # Altered (improved?) to let you inline namespaces on a per call - # basis ala SOAP::LITE -- www.soaplite.com - - def __init__(self, call, name, ns = None, sa = None, hd = None, - ma = None, config = Config): - - self.__call = call - self.__name = name - self.__ns = ns - self.__sa = sa - self.__hd = hd - self.__ma = ma - self.__config = config - if self.__name[0] == "_": - if self.__name in ["__repr__","__str__"]: - self.__call__ = self.__repr__ - else: - self.__call__ = self.__f_call - else: - self.__call__ = self.__r_call - - def __getattr__(self, name): - if self.__name[0] == "_": - # Don't nest method if it is a directive - return self.__class__(self.__call, name, self.__ns, - self.__sa, self.__hd, self.__ma) - - return self.__class__(self.__call, "%s.%s" % (self.__name, name), - self.__ns, self.__sa, self.__hd, self.__ma) - - def __f_call(self, *args, **kw): - if self.__name == "_ns": self.__ns = args - elif self.__name == "_sa": self.__sa = args - elif self.__name == "_hd": self.__hd = args - elif self.__name == "_ma": self.__ma = args - return self - - def __r_call(self, *args, **kw): - return self.__call(self.__name, args, kw, self.__ns, self.__sa, - self.__hd, self.__ma) - - def __repr__(self): - return "<%s at %d>" % (self.__class__, id(self)) - -################################################################################ -# Server -################################################################################ - -# Method Signature class for adding extra info to registered funcs, right now -# used just to indicate it should be called with keywords, instead of ordered -# params. -class MethodSig: - def __init__(self, func, keywords=0, context=0): - self.func = func - self.keywords = keywords - self.context = context - self.__name__ = func.__name__ - - def __call__(self, *args, **kw): - return apply(self.func,args,kw) - -class SOAPContext: - def __init__(self, header, body, attrs, xmldata, connection, httpheaders, - soapaction): - - self.header = header - self.body = body - self.attrs = attrs - self.xmldata = xmldata - self.connection = connection - self.httpheaders= httpheaders - self.soapaction = soapaction - -# A class to describe how header messages are handled -class HeaderHandler: - # Initially fail out if there are any problems. - def __init__(self, header, attrs): - for i in header.__dict__.keys(): - if i[0] == "_": - continue - - d = getattr(header, i) - - try: - fault = int(attrs[id(d)][(NS.ENV, 'mustUnderstand')]) - except: - fault = 0 - - if fault: - raise faultType, ("%s:MustUnderstand" % NS.ENV_T, - "Don't understand `%s' header element but " - "mustUnderstand attribute is set." % i) - - -################################################################################ -# SOAP Server -################################################################################ -class SOAPServer(SocketServer.TCPServer): - import BaseHTTPServer - - class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def version_string(self): - return '' + \ - 'SOAP.py ' + __version__ + ' (Python ' + \ - sys.version.split()[0] + ')' - - def date_time_string(self): - self.__last_date_time_string = \ - SOAPServer.BaseHTTPServer.BaseHTTPRequestHandler.\ - date_time_string(self) - - return self.__last_date_time_string - - def do_POST(self): - try: - if self.server.config.dumpHeadersIn: - s = 'Incoming HTTP headers' - debugHeader(s) - print self.raw_requestline.strip() - print "\n".join(map (lambda x: x.strip(), - self.headers.headers)) - debugFooter(s) - - data = self.rfile.read(int(self.headers["content-length"])) - - if self.server.config.dumpSOAPIn: - s = 'Incoming SOAP' - debugHeader(s) - print data, - if data[-1] != '\n': - print - debugFooter(s) - - (r, header, body, attrs) = \ - parseSOAPRPC(data, header = 1, body = 1, attrs = 1) - - method = r._name - args = r._aslist - kw = r._asdict - - ns = r._ns - resp = "" - # For fault messages - if ns: - nsmethod = "%s:%s" % (ns, method) - else: - nsmethod = method - - try: - # First look for registered functions - if self.server.funcmap.has_key(ns) and \ - self.server.funcmap[ns].has_key(method): - f = self.server.funcmap[ns][method] - else: # Now look at registered objects - # Check for nested attributes. This works even if - # there are none, because the split will return - # [method] - f = self.server.objmap[ns] - l = method.split(".") - for i in l: - f = getattr(f, i) - except: - resp = buildSOAP(faultType("%s:Client" % NS.ENV_T, - "No method %s found" % nsmethod, - "%s %s" % tuple(sys.exc_info()[0:2])), - encoding = self.server.encoding, - config = self.server.config) - status = 500 - else: - try: - if header: - x = HeaderHandler(header, attrs) - - # If it's wrapped, some special action may be needed - - if isinstance(f, MethodSig): - c = None - - if f.context: # Build context object - c = SOAPContext(header, body, attrs, data, - self.connection, self.headers, - self.headers["soapaction"]) - - if f.keywords: - # This is lame, but have to de-unicode - # keywords - - strkw = {} - - for (k, v) in kw.items(): - strkw[str(k)] = v - if c: - strkw["_SOAPContext"] = c - fr = apply(f, (), strkw) - elif c: - fr = apply(f, args, {'_SOAPContext':c}) - else: - fr = apply(f, args, {}) - else: - fr = apply(f, args, {}) - - if type(fr) == type(self) and \ - isinstance(fr, voidType): - resp = buildSOAP(kw = {'%sResponse' % method: fr}, - encoding = self.server.encoding, - config = self.server.config) - else: - resp = buildSOAP(kw = - {'%sResponse' % method: {'Result': fr}}, - encoding = self.server.encoding, - config = self.server.config) - except Exception, e: - import traceback - info = sys.exc_info() - - if self.server.config.dumpFaultInfo: - s = 'Method %s exception' % nsmethod - debugHeader(s) - traceback.print_exception(info[0], info[1], - info[2]) - debugFooter(s) - - if isinstance(e, faultType): - f = e - else: - f = faultType("%s:Server" % NS.ENV_T, - "Method %s failed." % nsmethod) - - if self.server.config.returnFaultInfo: - f._setDetail("".join(traceback.format_exception( - info[0], info[1], info[2]))) - elif not hasattr(f, 'detail'): - f._setDetail("%s %s" % (info[0], info[1])) - - resp = buildSOAP(f, encoding = self.server.encoding, - config = self.server.config) - status = 500 - else: - status = 200 - except faultType, e: - import traceback - info = sys.exc_info() - - if self.server.config.dumpFaultInfo: - s = 'Received fault exception' - debugHeader(s) - traceback.print_exception(info[0], info[1], - info[2]) - debugFooter(s) - - if self.server.config.returnFaultInfo: - e._setDetail("".join(traceback.format_exception( - info[0], info[1], info[2]))) - elif not hasattr(e, 'detail'): - e._setDetail("%s %s" % (info[0], info[1])) - - resp = buildSOAP(e, encoding = self.server.encoding, - config = self.server.config) - status = 500 - except: - # internal error, report as HTTP server error - if self.server.config.dumpFaultInfo: - import traceback - s = 'Internal exception' - debugHeader(s) - traceback.print_exc () - debugFooter(s) - self.send_response(500) - self.end_headers() - - if self.server.config.dumpHeadersOut and \ - self.request_version != 'HTTP/0.9': - s = 'Outgoing HTTP headers' - debugHeader(s) - if self.responses.has_key(status): - s = ' ' + self.responses[status][0] - else: - s = '' - print "%s %d%s" % (self.protocol_version, 500, s) - print "Server:", self.version_string() - print "Date:", self.__last_date_time_string - debugFooter(s) - else: - # got a valid SOAP response - self.send_response(status) - - t = 'text/xml'; - if self.server.encoding != None: - t += '; charset="%s"' % self.server.encoding - self.send_header("Content-type", t) - self.send_header("Content-length", str(len(resp))) - self.end_headers() - - if self.server.config.dumpHeadersOut and \ - self.request_version != 'HTTP/0.9': - s = 'Outgoing HTTP headers' - debugHeader(s) - if self.responses.has_key(status): - s = ' ' + self.responses[status][0] - else: - s = '' - print "%s %d%s" % (self.protocol_version, status, s) - print "Server:", self.version_string() - print "Date:", self.__last_date_time_string - print "Content-type:", t - print "Content-length:", len(resp) - debugFooter(s) - - if self.server.config.dumpSOAPOut: - s = 'Outgoing SOAP' - debugHeader(s) - print resp, - if resp[-1] != '\n': - print - debugFooter(s) - - self.wfile.write(resp) - self.wfile.flush() - - # We should be able to shut down both a regular and an SSL - # connection, but under Python 2.1, calling shutdown on an - # SSL connections drops the output, so this work-around. - # This should be investigated more someday. - - if self.server.config.SSLserver and \ - isinstance(self.connection, SSL.Connection): - self.connection.set_shutdown(SSL.SSL_SENT_SHUTDOWN | - SSL.SSL_RECEIVED_SHUTDOWN) - else: - self.connection.shutdown(1) - - def log_message(self, format, *args): - if self.server.log: - SOAPServer.BaseHTTPServer.BaseHTTPRequestHandler.\ - log_message (self, format, *args) - - def __init__(self, addr = ('localhost', 8000), - RequestHandler = SOAPRequestHandler, log = 1, encoding = 'UTF-8', - config = Config, namespace = None, ssl_context = None): - - # Test the encoding, raising an exception if it's not known - if encoding != None: - ''.encode(encoding) - - if ssl_context != None and not config.SSLserver: - raise AttributeError, \ - "SSL server not supported by this Python installation" - - self.namespace = namespace - self.objmap = {} - self.funcmap = {} - self.ssl_context = ssl_context - self.encoding = encoding - self.config = config - self.log = log - - self.allow_reuse_address= 1 - - SocketServer.TCPServer.__init__(self, addr, RequestHandler) - - def get_request(self): - sock, addr = SocketServer.TCPServer.get_request(self) - - if self.ssl_context: - sock = SSL.Connection(self.ssl_context, sock) - sock._setup_ssl(addr) - if sock.accept_ssl() != 1: - raise socket.error, "Couldn't accept SSL connection" - - return sock, addr - - def registerObject(self, object, namespace = ''): - if namespace == '': namespace = self.namespace - self.objmap[namespace] = object - - def registerFunction(self, function, namespace = '', funcName = None): - if not funcName : funcName = function.__name__ - if namespace == '': namespace = self.namespace - if self.funcmap.has_key(namespace): - self.funcmap[namespace][funcName] = function - else: - self.funcmap[namespace] = {funcName : function} - - def registerKWObject(self, object, namespace = ''): - if namespace == '': namespace = self.namespace - for i in dir(object.__class__): - if i[0] != "_" and callable(getattr(object, i)): - self.registerKWFunction(getattr(object,i), namespace) - - # convenience - wraps your func for you. - def registerKWFunction(self, function, namespace = '', funcName = None): - self.registerFunction(MethodSig(function,keywords=1), namespace, - funcName) diff --git a/shell/google/__init__.py b/shell/google/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/shell/google/google.py b/shell/google/google.py deleted file mode 100755 index a20ba513..00000000 --- a/shell/google/google.py +++ /dev/null @@ -1,638 +0,0 @@ -""" -Python wrapper for Google web APIs - -This module allows you to access Google's web APIs through SOAP, -to do things like search Google and get the results programmatically. -Described U{here } - -You need a Google-provided license key to use these services. -Follow the link above to get one. These functions will look in -several places (in this order) for the license key: - - - the "license_key" argument of each function - - the module-level LICENSE_KEY variable (call setLicense once to set it) - - an environment variable called GOOGLE_LICENSE_KEY - - a file called ".googlekey" in the current directory - - a file called "googlekey.txt" in the current directory - - a file called ".googlekey" in your home directory - - a file called "googlekey.txt" in your home directory - - a file called ".googlekey" in the same directory as google.py - - a file called "googlekey.txt" in the same directory as google.py - -Sample usage:: - - >>> import google - >>> google.setLicense('...') # must get your own key! - >>> data = google.doGoogleSearch('python') - >>> data.meta.searchTime - 0.043221000000000002 - - >>> data.results[0].URL - 'http://www.python.org/' - - >>> data.results[0].title - 'Python Language Website' - -@newfield contrib: Contributors -@author: Mark Pilgrim -@author: Brian Landers -@license: Python -@version: 0.6 -@contrib: David Ascher, for the install script -@contrib: Erik Max Francis, for the command line interface -@contrib: Michael Twomey, for HTTP proxy support -@contrib: Mark Recht, for patches to support SOAPpy -""" - -__author__ = "Mark Pilgrim (f8dy@diveintomark.org)" -__version__ = "0.6" -__cvsversion__ = "$Revision: 1.5 $"[11:-2] -__date__ = "$Date: 2004/02/25 23:46:07 $"[7:-2] -__copyright__ = "Copyright (c) 2002 Mark Pilgrim" -__license__ = "Python" -__credits__ = """David Ascher, for the install script -Erik Max Francis, for the command line interface -Michael Twomey, for HTTP proxy support""" - -import os, sys, getopt -import GoogleSOAPFacade - -LICENSE_KEY = None -HTTP_PROXY = None - -# -# Constants -# -_url = 'http://api.google.com/search/beta2' -_namespace = 'urn:GoogleSearch' -_googlefile1 = ".googlekey" -_googlefile2 = "googlekey.txt" - -_false = GoogleSOAPFacade.false -_true = GoogleSOAPFacade.true - -_licenseLocations = ( - ( lambda key: key, - 'passed to the function in license_key variable' ), - ( lambda key: LICENSE_KEY, - 'module-level LICENSE_KEY variable (call setLicense to set it)' ), - ( lambda key: os.environ.get( 'GOOGLE_LICENSE_KEY', None ), - 'an environment variable called GOOGLE_LICENSE_KEY' ), - ( lambda key: _contentsOf( os.getcwd(), _googlefile1 ), - '%s in the current directory' % _googlefile1), - ( lambda key: _contentsOf( os.getcwd(), _googlefile2 ), - '%s in the current directory' % _googlefile2), - ( lambda key: _contentsOf( os.environ.get( 'HOME', '' ), _googlefile1 ), - '%s in your home directory' % _googlefile1), - ( lambda key: _contentsOf( os.environ.get( 'HOME', '' ), _googlefile2 ), - '%s in your home directory' % _googlefile2 ), - ( lambda key: _contentsOf( _getScriptDir(), _googlefile1 ), - '%s in the google.py directory' % _googlefile1 ), - ( lambda key: _contentsOf( _getScriptDir(), _googlefile2 ), - '%s in the google.py directory' % _googlefile2 ) -) - -## ---------------------------------------------------------------------- -## Exceptions -## ---------------------------------------------------------------------- - -class NoLicenseKey(Exception): - """ - Thrown when the API is unable to find a valid license key. - """ - pass - -## ---------------------------------------------------------------------- -## administrative functions (non-API) -## ---------------------------------------------------------------------- - -def _version(): - """ - Display a formatted version string for the module - """ - print """PyGoogle %(__version__)s -%(__copyright__)s -released %(__date__)s - -Thanks to: -%(__credits__)s""" % globals() - - -def _usage(): - """ - Display usage information for the command-line interface - """ - program = os.path.basename(sys.argv[0]) - print """Usage: %(program)s [options] [querytype] query - -options: - -k, --key= Google license key (see important note below) - -1, -l, --lucky show only first hit - -m, --meta show meta information - -r, --reverse show results in reverse order - -x, --proxy= use HTTP proxy - -h, --help print this help - -v, --version print version and copyright information - -t, --test run test queries - -querytype: - -s, --search= search (default) - -c, --cache= retrieve cached page - -p, --spelling= check spelling - -IMPORTANT NOTE: all Google functions require a valid license key; -visit http://www.google.com/apis/ to get one. %(program)s will look in -these places (in order) and use the first license key it finds: - * the key specified on the command line""" % vars() - for get, location in _licenseLocations[2:]: - print " *", location - -## ---------------------------------------------------------------------- -## utility functions (API) -## ---------------------------------------------------------------------- - -def setLicense(license_key): - """ - Set the U{Google APIs } license key - - @param license_key: The new key to use - @type license_key: String - @todo: validate the key? - """ - global LICENSE_KEY - LICENSE_KEY = license_key - - -def getLicense(license_key = None): - """ - Get the U{Google APIs } license key - - The key can be read from any number of locations. See the module-leve - documentation for the search order. - - @return: the license key - @rtype: String - @raise NoLicenseKey: if no valid key could be found - """ - for get, location in _licenseLocations: - rc = get(license_key) - if rc: return rc - _usage() - raise NoLicenseKey, 'get a license key at http://www.google.com/apis/' - - -def setProxy(http_proxy): - """ - Set the HTTP proxy to be used when accessing Google - - @param http_proxy: the proxy to use - @type http_proxy: String - @todo: validiate the input? - """ - global HTTP_PROXY - HTTP_PROXY = http_proxy - - -def getProxy(http_proxy = None): - """ - Get the HTTP proxy we use for accessing Google - - @return: the proxy - @rtype: String - """ - return http_proxy or HTTP_PROXY - - -def _contentsOf(dirname, filename): - filename = os.path.join(dirname, filename) - if not os.path.exists(filename): return None - fsock = open(filename) - contents = fsock.read() - fsock.close() - return contents - - -def _getScriptDir(): - if __name__ == '__main__': - return os.path.abspath(os.path.dirname(sys.argv[0])) - else: - return os.path.abspath(os.path.dirname(sys.modules[__name__].__file__)) - - -def _marshalBoolean(value): - if value: - return _true - else: - return _false - - -def _getRemoteServer( http_proxy ): - return GoogleSOAPFacade.getProxy( _url, _namespace, http_proxy ) - - -## ---------------------------------------------------------------------- -## search results classes -## ---------------------------------------------------------------------- - -class _SearchBase: - def __init__(self, params): - for k, v in params.items(): - if isinstance(v, GoogleSOAPFacade.structType): - v = GoogleSOAPFacade.toDict( v ) - - try: - if isinstance(v[0], GoogleSOAPFacade.structType): - v = [ SOAPProxy.toDict( node ) for node in v ] - - except: - pass - self.__dict__[str(k)] = v - -## ---------------------------------------------------------------------- - -class SearchResultsMetaData(_SearchBase): - """ - Container class for metadata about a given search query's results. - - @ivar documentFiltering: is duplicate page filtering active? - - @ivar searchComments: human-readable informational message - - example:: - - "'the' is a very common word and was not included in your search" - - @ivar estimatedTotalResultsCount: estimated total number of results - for this query. - - @ivar estimateIsExact: is estimatedTotalResultsCount an exact value? - - @ivar searchQuery: search string that initiated this search - - @ivar startIndex: index of the first result returned (zero-based) - - @ivar endIndex: index of the last result returned (zero-based) - - @ivar searchTips: human-readable informational message on how to better - use Google. - - @ivar directoryCategories: list of categories for the search results - - This field is a list of dictionaries, like so:: - - { 'fullViewableName': 'the Open Directory category', - 'specialEncoding': 'encoding scheme of this directory category' - } - - @ivar searchTime: total search time, in seconds - """ - pass - -## ---------------------------------------------------------------------- - -class SearchResult(_SearchBase): - """ - Encapsulates the results from a search. - - @ivar URL: URL - - @ivar title: title (HTML) - - @ivar snippet: snippet showing query context (HTML - - @ivar cachedSize: size of cached version of this result, (KB) - - @ivar relatedInformationPresent: is the "related:" keyword supported? - - Flag indicates that the "related:" keyword is supported for this URL - - @ivar hostName: used when filtering occurs - - When filtering occurs, a maximum of two results from any given - host is returned. When this occurs, the second resultElement - that comes from that host contains the host name in this parameter. - - @ivar directoryCategory: Open Directory category information - - This field is a dictionary with the following values:: - - { 'fullViewableName': 'the Open Directory category', - 'specialEncoding' : 'encoding scheme of this directory category' - } - - @ivar directoryTitle: Open Directory title of this result (or blank) - - @ivar summary: Open Directory summary for this result (or blank) - """ - pass - -## ---------------------------------------------------------------------- - -class SearchReturnValue: - """ - complete search results for a single query - - @ivar meta: L{SearchResultsMetaData} instance for this query - - @ivar results: list of L{SearchResult} objects for this query - """ - def __init__( self, metadata, results ): - self.meta = metadata - self.results = results - -## ---------------------------------------------------------------------- -## main functions -## ---------------------------------------------------------------------- - -def doGoogleSearch( q, start = 0, maxResults = 10, filter = 1, - restrict='', safeSearch = 0, language = '', - inputencoding = '', outputencoding = '',\ - license_key = None, http_proxy = None ): - """ - Search Google using the SOAP API and return the results. - - You need a license key to call this function; see the - U{Google APIs } site to get one. - Then you can either pass it to this function every time, or - set it globally; see the L{google} module-level docs for details. - - See U{http://www.google.com/help/features.html} - for examples of advanced features. Anything that works at the - Google web site will work as a query string in this method. - - You can use the C{start} and C{maxResults} parameters to page - through multiple pages of results. Note that 'maxResults' is - currently limited by Google to 10. - - See the API reference for more advanced examples and a full list of - country codes and topics for use in the C{restrict} parameter, along - with legal values for the C{language}, C{inputencoding}, and - C{outputencoding} parameters. - - You can download the API documentation - U{http://www.google.com/apis/download.html }. - - @param q: search string. - @type q: String - - @param start: (optional) zero-based index of first desired result. - @type start: int - - @param maxResults: (optional) maximum number of results to return. - @type maxResults: int - - @param filter: (optional) flag to request filtering of similar results - @type filter: int - - @param restrict: (optional) restrict results by country or topic. - @type restrict: String - - @param safeSearch: (optional) - @type safeSearch: int - - @param language: (optional) - @type language: String - - @param inputencoding: (optional) - @type inputencoding: String - - @param outputencoding: (optional) - @type outputencoding: String - - @param license_key: (optional) the Google API license key to use - @type license_key: String - - @param http_proxy: (optional) the HTTP proxy to use for talking to Google - @type http_proxy: String - - @return: the search results encapsulated in an object - @rtype: L{SearchReturnValue} - """ - license_key = getLicense( license_key ) - http_proxy = getProxy( http_proxy ) - remoteserver = _getRemoteServer( http_proxy ) - - filter = _marshalBoolean( filter ) - safeSearch = _marshalBoolean( safeSearch ) - - data = remoteserver.doGoogleSearch( license_key, q, start, maxResults, - filter, restrict, safeSearch, - language, inputencoding, - outputencoding ) - - metadata = GoogleSOAPFacade.toDict( data ) - del metadata["resultElements"] - - metadata = SearchResultsMetaData( metadata ) - - results = [ SearchResult( GoogleSOAPFacade.toDict( node ) ) \ - for node in data.resultElements ] - - return SearchReturnValue( metadata, results ) - -## ---------------------------------------------------------------------- - -def doGetCachedPage( url, license_key = None, http_proxy = None ): - """ - Retrieve a page from the Google cache. - - You need a license key to call this function; see the - U{Google APIs } site to get one. - Then you can either pass it to this function every time, or - set it globally; see the L{google} module-level docs for details. - - @param url: full URL to the page to retrieve - @type url: String - - @param license_key: (optional) the Google API key to use - @type license_key: String - - @param http_proxy: (optional) the HTTP proxy server to use - @type http_proxy: String - - @return: full text of the cached page - @rtype: String - """ - license_key = getLicense( license_key ) - http_proxy = getProxy( http_proxy ) - remoteserver = _getRemoteServer( http_proxy ) - - return remoteserver.doGetCachedPage( license_key, url ) - -## ---------------------------------------------------------------------- - -def doSpellingSuggestion( phrase, license_key = None, http_proxy = None ): - """ - Get spelling suggestions from Google - - You need a license key to call this function; see the - U{Google APIs } site to get one. - Then you can either pass it to this function every time, or - set it globally; see the L{google} module-level docs for details. - - @param phrase: word or phrase to spell-check - @type phrase: String - - @param license_key: (optional) the Google API key to use - @type license_key: String - - @param http_proxy: (optional) the HTTP proxy to use - @type http_proxy: String - - @return: text of any suggested replacement, or None - """ - license_key = getLicense( license_key ) - http_proxy = getProxy( http_proxy) - remoteserver = _getRemoteServer( http_proxy ) - - return remoteserver.doSpellingSuggestion( license_key, phrase ) - -## ---------------------------------------------------------------------- -## functional test suite (see googletest.py for unit test suite) -## ---------------------------------------------------------------------- - -def _test(): - """ - Run functional test suite. - """ - try: - getLicense(None) - except NoLicenseKey: - return - - print "Searching for Python at google.com..." - data = doGoogleSearch( "Python" ) - _output( data, { "func": "doGoogleSearch"} ) - - print "\nSearching for 5 _French_ pages about Python, " - print "encoded in ISO-8859-1..." - - data = doGoogleSearch( "Python", language = 'lang_fr', - outputencoding = 'ISO-8859-1', - maxResults = 5 ) - - _output( data, { "func": "doGoogleSearch" } ) - - phrase = "Pyhton programming languager" - print "\nTesting spelling suggestions for '%s'..." % phrase - - data = doSpellingSuggestion( phrase ) - - _output( data, { "func": "doSpellingSuggestion" } ) - -## ---------------------------------------------------------------------- -## Command-line interface -## ---------------------------------------------------------------------- - -class _OutputFormatter: - def boil(self, data): - if type(data) == type(u""): - return data.encode("ISO-8859-1", "replace") - else: - return data - -class _TextOutputFormatter(_OutputFormatter): - def common(self, data, params): - if params.get("showMeta", 0): - meta = data.meta - for category in meta.directoryCategories: - print "directoryCategory: %s" % \ - self.boil(category["fullViewableName"]) - for attr in [node for node in dir(meta) if \ - node <> "directoryCategories" and node[:2] <> '__']: - print "%s:" % attr, self.boil(getattr(meta, attr)) - - def doGoogleSearch(self, data, params): - results = data.results - if params.get("feelingLucky", 0): - results = results[:1] - if params.get("reverseOrder", 0): - results.reverse() - for result in results: - for attr in dir(result): - if attr == "directoryCategory": - print "directoryCategory:", \ - self.boil(result.directoryCategory["fullViewableName"]) - elif attr[:2] <> '__': - print "%s:" % attr, self.boil(getattr(result, attr)) - print - self.common(data, params) - - def doGetCachedPage(self, data, params): - print data - self.common(data, params) - - doSpellingSuggestion = doGetCachedPage - -def _makeFormatter(outputFormat): - classname = "_%sOutputFormatter" % outputFormat.capitalize() - return globals()[classname]() - -def _output(results, params): - formatter = _makeFormatter(params.get("outputFormat", "text")) - outputmethod = getattr(formatter, params["func"]) - outputmethod(results, params) - -def main(argv): - """ - Command-line interface. - """ - if not argv: - _usage() - return - q = None - func = None - http_proxy = None - license_key = None - feelingLucky = 0 - showMeta = 0 - reverseOrder = 0 - runTest = 0 - outputFormat = "text" - try: - opts, args = getopt.getopt(argv, "s:c:p:k:lmrx:hvt1", - ["search=", "cache=", "spelling=", "key=", "lucky", "meta", - "reverse", "proxy=", "help", "version", "test"]) - except getopt.GetoptError: - _usage() - sys.exit(2) - for opt, arg in opts: - if opt in ("-s", "--search"): - q = arg - func = "doGoogleSearch" - elif opt in ("-c", "--cache"): - q = arg - func = "doGetCachedPage" - elif opt in ("-p", "--spelling"): - q = arg - func = "doSpellingSuggestion" - elif opt in ("-k", "--key"): - license_key = arg - elif opt in ("-l", "-1", "--lucky"): - feelingLucky = 1 - elif opt in ("-m", "--meta"): - showMeta = 1 - elif opt in ("-r", "--reverse"): - reverseOrder = 1 - elif opt in ("-x", "--proxy"): - http_proxy = arg - elif opt in ("-h", "--help"): - _usage() - elif opt in ("-v", "--version"): - _version() - elif opt in ("-t", "--test"): - runTest = 1 - if runTest: - setLicense(license_key) - setProxy(http_proxy) - _test() - if args and not q: - q = args[0] - func = "doGoogleSearch" - if func: - results = globals()[func]( q, http_proxy=http_proxy, - license_key=license_key ) - _output(results, locals()) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/shell/shell.py b/shell/shell.py deleted file mode 100755 index a3f07c8f..00000000 --- a/shell/shell.py +++ /dev/null @@ -1,574 +0,0 @@ -import dbus -import dbus.service -import dbus.glib - -import pygtk -pygtk.require('2.0') -import gtk -import pango -import gobject - -import sugar.util -from sugar.chat.ChatWindow import ChatWindow -from sugar.chat.ActivityChat import ActivityChat -from sugar.chat.MeshChat import MeshChat -from sugar.LogWriter import LogWriter - -from Owner import ShellOwner -from StartPage import StartPage -from WindowManager import WindowManager -from PresenceWindow import PresenceWindow - -class ActivityHostSignalHelper(gobject.GObject): - __gsignals__ = { - 'shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) - } - - def __init__(self, parent): - gobject.GObject.__init__(self) - self._parent = parent - - def emit_shared(self): - self.emit('shared') - -class ActivityHost(dbus.service.Object): - def __init__(self, activity_container, activity_name, default_type, activity_id = None): - self.peer_service = None - - self.activity_name = activity_name - self.ellipsize_tab = False - self._shared = False - - self._signal_helper = ActivityHostSignalHelper(self) - - self.activity_container = activity_container - - if activity_id is None: - self.activity_id = sugar.util.unique_id() - else: - self.activity_id = activity_id - self._default_type = default_type - - self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%s" % self.activity_id - - dbus.service.Object.__init__(self, activity_container.service, self.dbus_object_name) - self.socket = gtk.Socket() - self.socket.set_data("sugar-activity", self) - self.socket.show() - - hbox = gtk.HBox(False, 4); - - self.tab_activity_image = gtk.Image() - self.tab_activity_image.set_from_stock(gtk.STOCK_CONVERT, gtk.ICON_SIZE_MENU) - hbox.pack_start(self.tab_activity_image) - #self.tab_activity_image.show() - - self.label_hbox = gtk.HBox(False, 4); - self.label_hbox.connect("style-set", self.__tab_label_style_set_cb) - hbox.pack_start(self.label_hbox) - - self.tab_label = gtk.Label(self.activity_name) - self.tab_label.set_single_line_mode(True) - self.tab_label.set_alignment(0.0, 0.5) - self.tab_label.set_padding(0, 0) - self.tab_label.show() - - close_image = gtk.Image() - close_image.set_from_stock (gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) - close_image.show() - - self.tab_close_button = gtk.Button() - rcstyle = gtk.RcStyle(); - rcstyle.xthickness = rcstyle.ythickness = 0; - self.tab_close_button.modify_style (rcstyle); - self.tab_close_button.add(close_image) - self.tab_close_button.set_relief(gtk.RELIEF_NONE) - self.tab_close_button.set_focus_on_click(False) - self.tab_close_button.connect("clicked", self.tab_close_button_clicked) - - self.label_hbox.pack_start(self.tab_label) - self.label_hbox.pack_start(self.tab_close_button, False, False, 0) - self.label_hbox.show() - - hbox.show() - - self._create_chat() - - notebook = self.activity_container.notebook - index = notebook.append_page(self.socket, hbox) - notebook.set_current_page(index) - - def _create_chat(self): - self._activity_chat = ActivityChat(self) - - def got_focus(self): - if self.peer_service != None: - self.peer_service.got_focus() - - def lost_focus(self): - if self.peer_service != None: - self.peer_service.lost_focus() - - def get_chat(self): - return self._activity_chat - - def get_default_type(self): - return self._default_type - - def __close_button_clicked_reply_cb(self): - pass - - def __close_button_clicked_error_cb(self, error): - pass - - def publish(self): - self._activity_chat.publish() - self.peer_service.publish() - - def tab_close_button_clicked(self, button): - self.peer_service.close_from_user(reply_handler = self.__close_button_clicked_reply_cb, \ - error_handler = self.__close_button_clicked_error_cb) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="", \ - out_signature="t") - def get_host_xembed_id(self): - window_id = self.socket.get_id() - #print "window_id = %d"%window_id - return window_id - - def connect(self, signal, func): - self._signal_helper.connect(signal, func) - - def get_shared(self): - """Return True if this activity is shared, False if - it has not been shared yet.""" - return self._shared - - def _shared_signal(self): - self._shared = True - self._signal_helper.emit_shared() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="ss", \ - out_signature="") - def set_peer_service_name(self, peer_service_name, peer_object_name): - #print "peer_service_name = %s, peer_object_name = %s"%(peer_service_name, peer_object_name) - self.__peer_service_name = peer_service_name - self.__peer_object_name = peer_object_name - self.peer_service = dbus.Interface(self.activity_container.bus.get_object( \ - self.__peer_service_name, self.__peer_object_name), \ - "com.redhat.Sugar.Activity") - self.activity_container.bus.add_signal_receiver(self._shared_signal, - signal_name="ActivityShared", - dbus_interface="com.redhat.Sugar.Activity", - named_service=self.__peer_service_name, - path=self.__peer_object_name) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_ellipsize_tab(self, ellipsize): - self.ellipsize_tab = True - self.update_tab_size() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_can_close(self, can_close): - if can_close: - self.tab_close_button.show() - else: - self.tab_close_button.hide() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_tab_show_icon(self, show_icon): - if show_icon: - self.tab_activity_image.show() - else: - self.tab_activity_image.hide() - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="b", \ - out_signature="") - def set_has_changes(self, has_changes): - if has_changes: - attrs = pango.AttrList() - attrs.insert(pango.AttrForeground(50000, 0, 0, 0, -1)) - attrs.insert(pango.AttrWeight(pango.WEIGHT_BOLD, 0, -1)) - self.tab_label.set_attributes(attrs) - else: - self.tab_label.set_attributes(pango.AttrList()) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="s", \ - out_signature="") - def set_tab_text(self, text): - self.tab_label.set_text(text) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="ayibiiii", \ - out_signature="") - def set_tab_icon(self, data, colorspace, has_alpha, bits_per_sample, width, height, rowstride): - #print "width=%d, height=%d"%(width, height) - #print " data = ", data - pixstr = "" - for c in data: - # Work around for a bug in dbus < 0.61 where integers - # are not correctly marshalled - if c < 0: - c += 256 - pixstr += chr(c) - - pixbuf = gtk.gdk.pixbuf_new_from_data(pixstr, colorspace, has_alpha, bits_per_sample, width, height, rowstride) - #print pixbuf - self.tab_activity_image.set_from_pixbuf(pixbuf) - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityHost", \ - in_signature="", \ - out_signature="") - def shutdown(self): - #print "shutdown" - for owner, activity in self.activity_container.activities[:]: - if activity == self: - self.activity_container.activities.remove((owner, activity)) - - for i in range(self.activity_container.notebook.get_n_pages()): - child = self.activity_container.notebook.get_nth_page(i) - if child == self.socket: - #print "found child" - self.activity_container.notebook.remove_page(i) - break - - del self - - def get_host_activity_id(self): - """Real function that the shell should use for getting the - activity's ID.""" - return self.activity_id - - def get_id(self): - """Interface-type function to match activity.Activity's - get_id() function.""" - return self.activity_id - - def default_type(self): - """Interface-type function to match activity.Activity's - default_type() function.""" - return self._default_type - - def get_object_path(self): - return self.dbus_object_name - - def update_tab_size(self): - if self.ellipsize_tab: - self.tab_label.set_ellipsize(pango.ELLIPSIZE_END) - - context = self.label_hbox.get_pango_context() - font_desc = self.label_hbox.style.font_desc - metrics = context.get_metrics(font_desc, context.get_language()) - char_width = metrics.get_approximate_digit_width() - [w, h] = self.__get_close_icon_size() - tab_width = 15 * pango.PIXELS(char_width) + 2 * w - self.label_hbox.set_size_request(tab_width, -1); - else: - self.tab_label.set_ellipsize(pango.ELLIPSIZE_NONE) - self.label_hbox.set_size_request(-1, -1) - - def __get_close_icon_size(self): - settings = self.label_hbox.get_settings() - return gtk.icon_size_lookup_for_settings(settings, gtk.ICON_SIZE_MENU) - - def __tab_label_style_set_cb(self, widget, previous_style): - [w, h] = self.__get_close_icon_size() - self.tab_close_button.set_size_request (w + 5, h + 2) - self.update_tab_size() - -class ActivityContainerSignalHelper(gobject.GObject): - """A gobject whose sole purpose is to distribute signals for - an ActivityContainer object.""" - - __gsignals__ = { - 'local-activity-started': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), - 'local-activity-ended': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])) - } - - def __init__(self, parent): - gobject.GObject.__init__(self) - self._parent = parent - - def activity_started(self, activity_id): - self.emit('local-activity-started', self._parent, activity_id) - - def activity_ended(self, activity_id): - self.emit('local-activity-ended', self._parent, activity_id) - -class ActivityContainer(dbus.service.Object): - - def __init__(self, service, bus): - self.activities = [] - - self.bus = bus - self.service = service - - self._signal_helper = ActivityContainerSignalHelper(self) - - dbus.service.Object.__init__(self, self.service, "/com/redhat/Sugar/Shell/ActivityContainer") - bus.add_signal_receiver(self.name_owner_changed, dbus_interface = "org.freedesktop.DBus", signal_name = "NameOwnerChanged") - - self.window = gtk.Window() - self.window.connect("key-press-event", self.__key_press_event_cb) - self.window.set_title("OLPC Sugar") - - self._fullscreen = False - - self.notebook = gtk.Notebook() - self.notebook.set_scrollable(True) - - tab_label = gtk.Label("Everyone") - self._start_page = StartPage(self, self._signal_helper) - self.notebook.append_page(self._start_page, tab_label) - self._start_page.show() - - self.notebook.show() - self.notebook.connect("switch-page", self.notebook_tab_changed) - self.window.add(self.notebook) - - self.window.connect("destroy", lambda w: gtk.main_quit()) - - self.current_activity = None - - # Create our owner service - self._owner = ShellOwner() - - self._presence_window = PresenceWindow(self) - self._presence_window.set_transient_for(self.window) - self._presence_window.set_decorated(False) - self._presence_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK) - self._presence_window.set_skip_taskbar_hint(True) - - wm = WindowManager(self._presence_window) - - wm.set_width(170, WindowManager.ABSOLUTE) - wm.set_height(1.0, WindowManager.SCREEN_RELATIVE) - wm.set_position(WindowManager.LEFT) - wm.manage() - - self._chat_window = ChatWindow() - self._chat_window.set_transient_for(self.window) - self._chat_window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK) - self._chat_window.set_decorated(False) - self._chat_window.set_skip_taskbar_hint(True) - - self._chat_wm = WindowManager(self._chat_window) - - self._chat_wm.set_width(420, WindowManager.ABSOLUTE) - self._chat_wm.set_height(380, WindowManager.ABSOLUTE) - self._chat_wm.set_position(WindowManager.TOP) - self._chat_wm.manage() - - self._mesh_chat = MeshChat() - - def show(self): - self.window.show() - - def set_current_activity(self, activity): - if self.current_activity != None: - self.current_activity.lost_focus() - - self.current_activity = activity - self._presence_window.set_activity(activity) - - if activity: - host_chat = activity.get_chat() - self._chat_window.set_chat(host_chat) - else: - self._chat_window.set_chat(self._mesh_chat) - - # For some reason the substitution screw up window position - self._chat_wm.update() - - if self.current_activity != None: - self.current_activity.got_focus() - - def notebook_tab_changed(self, notebook, page, page_number): - new_activity = notebook.get_nth_page(page_number).get_data("sugar-activity") - self.set_current_activity(new_activity) - - def switch_to_activity(self, activity_id): - found = False - for owner, activity in self.activities: - if activity.get_host_activity_id() == activity_id: - found = True - break - if not found: - return - - # Find the activity in the notebook - activity_page = None - npages = self.notebook.get_n_pages() - for pageno in range(1, npages): - activity = self.notebook.get_nth_page(pageno).get_data("sugar-activity") - if activity and activity.get_host_activity_id() == activity_id: - activity_page = pageno - break - if not activity_page: - return - - print "switching to activity page %d" % activity_page - self.notebook.set_current_page(activity_page) - - def name_owner_changed(self, service_name, old_service_name, new_service_name): - #print "in name_owner_changed: svc=%s oldsvc=%s newsvc=%s"%(service_name, old_service_name, new_service_name) - for owner, activity in self.activities[:]: - if owner == old_service_name: - activity_id = activity.get_host_activity_id() - self._signal_helper.activity_ended(activity_id) - self.activities.remove((owner, activity)) - #self.__print_activities() - - def have_activity(self, activity_id): - for owner, activity in self.activities: - list_activity_id = activity.get_host_activity_id() - if activity_id == list_activity_id: - return True - return False - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer", \ - in_signature="ss", \ - out_signature="s", \ - sender_keyword="sender") - def add_activity(self, activity_name, default_type, sender): - #print "hello world, activity_name = '%s', sender = '%s'"%(activity_name, sender) - activity = ActivityHost(self, activity_name, default_type) - self.activities.append((sender, activity)) - - activity_id = activity.get_host_activity_id() - self._signal_helper.activity_started(activity_id) - - self.current_activity = activity - return activity_id - - @dbus.service.method("com.redhat.Sugar.Shell.ActivityContainer", \ - in_signature="sss", \ - sender_keyword="sender") - def add_activity_with_id(self, activity_name, default_type, activity_id, sender): - activity = ActivityHost(self, activity_name, default_type, activity_id) - self.activities.append((sender, activity)) - activity_id = activity.get_host_activity_id() - self._signal_helper.activity_started(activity_id) - self.current_activity = activity - - def __print_activities(self): - print "__print_activities: %d activities registered" % len(self.activities) - i = 0 - for owner, activity in self.activities: - print " %d: owner=%s activity_object_name=%s" % (i, owner, activity.dbus_object_name) - i += 1 - - def __key_press_event_cb(self, window, event): - if event.keyval == gtk.keysyms.F11: - if self._fullscreen: - window.unfullscreen() - self._fullscreen = False - else: - window.fullscreen() - self._fullscreen = True - -class ConsoleLogger(dbus.service.Object): - def __init__(self): - session_bus = dbus.SessionBus() - bus_name = dbus.service.BusName('com.redhat.Sugar.Logger', bus=session_bus) - object_path = '/com/redhat/Sugar/Logger' - dbus.service.Object.__init__(self, bus_name, object_path) - - self._window = gtk.Window() - self._window.set_title("Console") - self._window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK) - self._window.set_decorated(False) - self._window.set_skip_taskbar_hint(True) - self._window.connect("delete_event", lambda w, e: w.hide_on_delete()) - - self._nb = gtk.Notebook() - self._window.add(self._nb) - self._nb.show() - - self._consoles = {} - - console_wm = WindowManager(self._window) - - console_wm.set_width(0.7, WindowManager.SCREEN_RELATIVE) - console_wm.set_height(0.9, WindowManager.SCREEN_RELATIVE) - console_wm.set_position(WindowManager.BOTTOM) - console_wm.manage() - - def _create_console(self, application): - sw = gtk.ScrolledWindow() - sw.set_policy(gtk.POLICY_AUTOMATIC, - gtk.POLICY_AUTOMATIC) - - console = gtk.TextView() - console.set_wrap_mode(gtk.WRAP_WORD) - - sw.add(console) - console.show() - - self._nb.append_page(sw, gtk.Label(application)) - sw.show() - - return console - - @dbus.service.method('com.redhat.Sugar.Logger') - def log(self, application, message): - if self._consoles.has_key(application): - console = self._consoles[application] - else: - console = self._create_console(application) - self._consoles[application] = console - - buf = console.get_buffer() - buf.insert(buf.get_end_iter(), message) - -class Shell(gobject.GObject): - __gsignals__ = { - 'close': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])), - } - - def __init__(self): - gobject.GObject.__init__(self) - - def start(self): - console = ConsoleLogger() - - log_writer = LogWriter("Shell", False) - log_writer.start() - - session_bus = dbus.SessionBus() - service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus) - - activity_container = ActivityContainer(service, session_bus) - activity_container.window.connect('destroy', self.__activity_container_destroy_cb) - activity_container.show() - - wm = WindowManager(activity_container.window) - wm.set_width(640, WindowManager.ABSOLUTE) - wm.set_height(480, WindowManager.ABSOLUTE) - wm.set_position(WindowManager.CENTER) - wm.show() - wm.manage() - - def __activity_container_destroy_cb(self, activity_container): - self.emit('close') - -if __name__ == "__main__": - shell = Shell() - shell.start() - try: - gtk.main() - except KeyboardInterrupt: - print 'Ctrl+c pressed, exiting...' diff --git a/shell/sugar b/shell/sugar index a4fd0239..9a857dbd 100755 --- a/shell/sugar +++ b/shell/sugar @@ -9,6 +9,9 @@ import pygtk pygtk.require('2.0') import gobject +# FIXME How to pick a good display number +XEPHYR_DISPLAY = 100 + def add_to_python_path(path): sys.path.insert(0, path) if os.environ.has_key('PYTHONPATH'): @@ -23,7 +26,6 @@ def start_dbus(): dbus_file = os.fdopen(dbus_stdout) addr = dbus_file.readline() addr = addr.strip() - print "dbus-daemon pid is %d, session bus address is %s" % (dbus_pid, addr) dbus_file.close() os.environ["DBUS_SESSION_BUS_ADDRESS"] = addr @@ -37,6 +39,20 @@ def stop_dbus(dbus_pid): except OSError: pass +def start_xephyr(): + command = 'Xephyr :%d -ac -screen 640x480' % (XEPHYR_DISPLAY) + xephyr_pid = os.spawnvp(os.P_NOWAIT, 'Xephyr', command.split()) + +def stop_xephyr(): + os.kill(xephyr_pid) + +def start_matchbox(): + command = 'matchbox-window-manager -use_titlebar no' + xephyr_pid = os.spawnvp(os.P_NOWAIT, 'matchbox-window-manager', command.split()) + +def stop_matchbox(): + os.kill(xephyr_pid) + i = 0 dbus_daemon_pid = None for arg in sys.argv: @@ -56,7 +72,6 @@ if not os.environ.has_key("SUGAR_NICK_NAME"): os.environ['SUGAR_NICK_NAME'] = nick os.environ['SUGAR_USER_DIR'] = os.path.expanduser('~/.sugar') - curdir = os.path.abspath(os.path.dirname(__file__)) basedir = os.path.dirname(curdir) @@ -68,6 +83,10 @@ else: import sugar.env add_to_python_path(os.path.join(sugar.env.get_data_dir(), 'shell')) print 'Running the installed sugar...' + +start_xephyr() +os.environ['DISPLAY'] = ":%d" % (XEPHYR_DISPLAY) +start_matchbox() print 'Redirecting output to the console, press Ctrl+Down to open it.' @@ -78,3 +97,6 @@ session.start() if dbus_daemon_pid: stop_dbus(dbus_daemon_pid) + +stop_matchbox() +stop_xephyr() diff --git a/sugar/activity/Activity.py b/sugar/activity/Activity.py index 344e1069..b397d154 100644 --- a/sugar/activity/Activity.py +++ b/sugar/activity/Activity.py @@ -9,6 +9,8 @@ pygtk.require('2.0') import gtk, gobject from sugar.LogWriter import LogWriter +from sugar import keybindings +import sugar.util SHELL_SERVICE_NAME = "com.redhat.Sugar.Shell" SHELL_SERVICE_PATH = "/com/redhat/Sugar/Shell" @@ -16,12 +18,6 @@ SHELL_SERVICE_PATH = "/com/redhat/Sugar/Shell" ACTIVITY_SERVICE_NAME = "com.redhat.Sugar.Activity" ACTIVITY_SERVICE_PATH = "/com/redhat/Sugar/Activity" -ON_CONNECTED_TO_SHELL_CB = "connected_to_shell" -ON_DISCONNECTED_FROM_SHELL_CB = "disconnected_from_shell" -ON_RECONNECTED_TO_SHELL_CB = "reconnected_to_shell" -ON_CLOSE_FROM_USER_CB = "close_from_user" -ON_LOST_FOCUS_CB = "lost_focus" -ON_GOT_FOCUS_CB = "got_focus" ON_PUBLISH_CB = "publish" def get_path(activity_name): @@ -65,14 +61,10 @@ class ActivityFactory(dbus.service.Object): service = Service.deserialize(serialized_service) activity = self._class(args) - gobject.idle_add(self._start_activity_cb, activity, service) @dbus.service.method("com.redhat.Sugar.ActivityFactory") - def create(self, args): - self.create_with_service(None, args) - - def _start_activity_cb(self, activity, service): - activity.connect_to_shell(service) + def create(self): + self.create_with_service(None, []) def create(activity_name, service = None, args = None): """Create a new activity from his name.""" @@ -84,11 +76,21 @@ def create(activity_name, service = None, args = None): proxy_obj = bus.get_object(factory_name, factory_path) factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory") - if service: + if service and args: serialized_service = service.serialize(service) factory.create_with_service(serialized_service, args) else: - factory.create(args) + factory.create() + +def _get_registry(): + bus = dbus.SessionBus() + proxy_obj = bus.get_object("com.redhat.Sugar.ActivityRegistry", + "/com/redhat/Sugar/ActivityRegistry") + return dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityRegistry") + +def list_activities(): + registry = _get_registry() + return registry.list_activities() def main(activity_name, activity_class): """Starts the activity main loop.""" @@ -97,6 +99,9 @@ def main(activity_name, activity_class): factory = ActivityFactory(activity_name, activity_class) + registry = _get_registry() + registry.add(activity_name, activity_name) + gtk.main() class ActivityDbusService(dbus.service.Object): @@ -105,29 +110,19 @@ class ActivityDbusService(dbus.service.Object): The dbus service is separate from the actual Activity object so that we can tightly control what stuff passes through the dbus python bindings.""" - _ALLOWED_CALLBACKS = [ON_CONNECTED_TO_SHELL_CB, ON_DISCONNECTED_FROM_SHELL_CB, \ - ON_RECONNECTED_TO_SHELL_CB, ON_CLOSE_FROM_USER_CB, ON_LOST_FOCUS_CB, \ - ON_GOT_FOCUS_CB, ON_PUBLISH_CB] + _ALLOWED_CALLBACKS = [ON_PUBLISH_CB] - def __init__(self, activity): + def __init__(self, xid, activity): self._activity = activity - self._activity_id = None - self._activity_object = None - self._service = None - self._bus = dbus.SessionBus() - self._bus.add_signal_receiver(self.name_owner_changed, dbus_interface = "org.freedesktop.DBus", signal_name = "NameOwnerChanged") self._callbacks = {} for cb in self._ALLOWED_CALLBACKS: self._callbacks[cb] = None - - def __del__(self): - if self._activity_id: - del self._service - del self._activity_container - del self._activity_conainer_object - del self._activity_object - self._bus.remove_signal_receiver(self.name_owner_changed, dbus_interface="org.freedesktop.DBus", signal_name="NameOwnerChanged") - del self._bus + + bus = dbus.SessionBus() + service_name = ACTIVITY_SERVICE_NAME + "%s" % xid + object_path = ACTIVITY_SERVICE_PATH + "/%s" % xid + service = dbus.service.BusName(service_name, bus=bus) + dbus.service.Object.__init__(self, service, object_path) def register_callback(self, name, callback): if name not in self._ALLOWED_CALLBACKS: @@ -146,97 +141,33 @@ class ActivityDbusService(dbus.service.Object): if name in self._ALLOWED_CALLBACKS and self._callbacks[name]: gobject.idle_add(self._call_callback_cb, self._callbacks[name], *args) - def connect_to_shell(self, service=None): - """Register with the shell via dbus, getting an activity ID and - and XEMBED window ID in which to display the Activity.""" - self._activity_container_object = self._bus.get_object(SHELL_SERVICE_NAME, \ - SHELL_SERVICE_PATH + "/ActivityContainer") - self._activity_container = dbus.Interface(self._activity_container_object, \ - SHELL_SERVICE_NAME + ".ActivityContainer") - - if service is None: - self._activity_id = self._activity_container.add_activity("", self._activity.default_type()) - else: - self._activity_id = service.get_activity_id() - self._activity_container.add_activity_with_id("", self._activity.default_type(), self._activity_id) - - self._object_path = SHELL_SERVICE_PATH + "/Activities/%s" % self._activity_id - - print "ActivityDbusService: object path is '%s'" % self._object_path - - self._activity_object = dbus.Interface(self._bus.get_object(SHELL_SERVICE_NAME, self._object_path), \ - SHELL_SERVICE_NAME + ".ActivityHost") - - # Now let us register a peer service so the Shell can poke it - self._peer_service_name = ACTIVITY_SERVICE_NAME + "%s" % self._activity_id - self._peer_object_path = ACTIVITY_SERVICE_PATH + "/%s" % self._activity_id - self._service = dbus.service.BusName(self._peer_service_name, bus=self._bus) - dbus.service.Object.__init__(self, self._service, self._peer_object_path) - - self._activity_object.set_peer_service_name(self._peer_service_name, self._peer_object_path) - - self._call_callback(ON_CONNECTED_TO_SHELL_CB, self._activity_object, self._activity_id, service) - - def _shutdown_reply_cb(self): - """Shutdown was successful, tell the Activity that we're disconnected.""" - self._call_callback(ON_DISCONNECTED_FROM_SHELL_CB) - - def _shutdown_error_cb(self, error): - print "ActivityDbusService: error during shutdown - '%s'" % error - - def shutdown(self): - """Notify the shell that we are shutting down.""" - self._activity_object.shutdown(reply_handler=self._shutdown_reply_cb, error_handler=self._shutdown_error_cb) - - def name_owner_changed(self, service_name, old_service_name, new_service_name): - """Handle dbus NameOwnerChanged signals.""" - if not self._activity_id: - # Do nothing if we're not connected yet - return - - if service_name == SHELL_SERVICE_NAME and not len(new_service_name): - self._call_callback(ON_DISCONNECTED_FROM_SHELL_CB) - elif service_name == SHELL_SERVICE_NAME and not len(old_service_name): - self._call_callback(ON_RECONNECTED_TO_SHELL_CB) - - @dbus.service.method(ACTIVITY_SERVICE_NAME) - def lost_focus(self): - """Called by the shell to notify us that we've lost focus.""" - self._call_callback(ON_LOST_FOCUS_CB) - - @dbus.service.method(ACTIVITY_SERVICE_NAME) - def got_focus(self): - """Called by the shell to notify us that the user gave us focus.""" - self._call_callback(ON_GOT_FOCUS_CB) - - @dbus.service.method(ACTIVITY_SERVICE_NAME) - def close_from_user(self): - """Called by the shell to notify us that the user closed us.""" - self._call_callback(ON_CLOSE_FROM_USER_CB) - @dbus.service.method(ACTIVITY_SERVICE_NAME) def publish(self): """Called by the shell to request the activity to publish itself on the network.""" self._call_callback(ON_PUBLISH_CB) - @dbus.service.signal(ACTIVITY_SERVICE_NAME) - def ActivityShared(self): - pass + @dbus.service.method(ACTIVITY_SERVICE_NAME) + def get_id(self): + """Get the activity identifier""" + self._activity.get_id() -class Activity(object): + @dbus.service.method(ACTIVITY_SERVICE_NAME) + def get_shared(self): + """Get the activity identifier""" + return self._activity.get_shared() + +class Activity(gtk.Window): """Base Activity class that all other Activities derive from.""" - def __init__(self, default_type): - self._dbus_service = self._get_new_dbus_service() - self._dbus_service.register_callback(ON_CONNECTED_TO_SHELL_CB, self._internal_on_connected_to_shell_cb) - self._dbus_service.register_callback(ON_DISCONNECTED_FROM_SHELL_CB, self._internal_on_disconnected_from_shell_cb) - self._dbus_service.register_callback(ON_RECONNECTED_TO_SHELL_CB, self._internal_on_reconnected_to_shell_cb) - self._dbus_service.register_callback(ON_CLOSE_FROM_USER_CB, self._internal_on_close_from_user_cb) - self._dbus_service.register_callback(ON_PUBLISH_CB, self._internal_on_publish_cb) - self._dbus_service.register_callback(ON_LOST_FOCUS_CB, self._internal_on_lost_focus_cb) - self._dbus_service.register_callback(ON_GOT_FOCUS_CB, self._internal_on_got_focus_cb) - self._has_focus = False - self._plug = None + def __init__(self, default_type, activity_id = None): + gtk.Window.__init__(self) + + if activity_id is None: + self._activity_id = sugar.util.unique_id() + else: + self._activity_id = activity_id + + self._dbus_service = None self._initial_service = None self._activity_object = None self._shared = False @@ -244,10 +175,21 @@ class Activity(object): raise ValueError("Default type must be a valid string.") self._default_type = default_type + keybindings.setup_global_keys(self) + + self.connect('realize', self.__realize) + + self.present() + + def __realize(self, window): + if not self._dbus_service: + self._register_service() + + def _register_service(self): + self._dbus_service = self._get_new_dbus_service() + self._dbus_service.register_callback(ON_PUBLISH_CB, self._internal_on_publish_cb) + def _cleanup(self): - if self._plug: - self._plug.destroy() - self._plug = None if self._dbus_service: del self._dbus_service self._dbus_service = None @@ -258,7 +200,7 @@ class Activity(object): def _get_new_dbus_service(self): """Create and return a new dbus service object for this Activity. Allows subclasses to use their own dbus service object if they choose.""" - return ActivityDbusService(self) + return ActivityDbusService(self.window.xid, self) def default_type(self): return self._default_type @@ -269,119 +211,20 @@ class Activity(object): self._shared = True self._dbus_service.ActivityShared() - def shared(self): + def get_shared(self): return self._shared def has_focus(self): """Return whether or not this Activity is visible to the user.""" return self._has_focus - def connect_to_shell(self, service = None): - """Called by our controller to tell us to initialize and connect - to the shell.""" - self._dbus_service.connect_to_shell(service) - - def _internal_on_connected_to_shell_cb(self, activity_object, activity_id, service=None): - """Callback when the dbus service object has connected to the shell.""" - self._activity_object = activity_object - self._activity_id = activity_id - self._window_id = self._activity_object.get_host_xembed_id() - print "Activity: XEMBED window ID is %s" % self._window_id - self._plug = gtk.Plug(self._window_id) - self._initial_service = service - if service: - self.set_shared() - self.on_connected_to_shell() - - def _internal_on_disconnected_from_shell_cb(self): - """Callback when the dbus service object has disconnected from the shell.""" - self._cleanup() - self.on_disconnected_from_shell() - - def _internal_on_reconnected_to_shell_cb(self): - """Callback when the dbus service object has reconnected to the shell.""" - self.on_reconnected_to_shell() - - def _internal_on_close_from_user_cb(self): - """Callback when the dbus service object tells us the user has closed our activity.""" - self.shutdown() - self.on_close_from_user() - def _internal_on_publish_cb(self): """Callback when the dbus service object tells us the user has closed our activity.""" self.publish() - def _internal_on_lost_focus_cb(self): - """Callback when the dbus service object tells us we have lost focus.""" - self._has_focus = False - self.on_lost_focus() - - def _internal_on_got_focus_cb(self): - """Callback when the dbus service object tells us we have gotten focus.""" - self._has_focus = True - self.set_has_changes(False) - self.on_got_focus() - - def gtk_plug(self): - """Return our GtkPlug widget.""" - return self._plug - - def set_ellipsize_tab(self, ellipsize): - """Sets this Activity's tab text to be ellipsized or not.""" - self._activity_object.set_ellipsize_tab(ellipsize) - - def set_tab_text(self, text): - """Sets this Activity's tab text.""" - self._activity_object.set_tab_text(text) - - def set_can_close(self, can_close): - """Sets whether or not this Activity can be closed by the user.""" - self._activity_object.set_can_close(can_close) - - def set_show_tab_icon(self, show_icon): - """Sets whether or not an icon is shown in this Activity's tab.""" - self._activity_object.set_tab_show_icon(show_icon) - - def set_tab_icon(self, pixbuf=None, name=None): - """Set the Activity's tab icon, either from pixbuf data - or by an icon theme icon name.""" - if name: - icon_theme = gtk.icon_theme_get_default() - icon_info = icon_theme.lookup_icon(name, gtk.ICON_SIZE_MENU, 0) - if icon_info: - orig_pixbuf = icon_info.load_icon() - pixbuf = orig_pixbuf.scale_simple(16, 16, gtk.gdk.INTERP_BILINEAR) - - if pixbuf: - # Dump the pixel data into an array and shove it through dbus - pixarray = [] - pixstr = pixbuf.get_pixels(); - for c in pixstr: - pixarray.append(c) - self._activity_object.set_tab_icon(pixarray, \ - pixbuf.get_colorspace(), \ - pixbuf.get_has_alpha(), \ - pixbuf.get_bits_per_sample(), \ - pixbuf.get_width(), \ - pixbuf.get_height(), \ - pixbuf.get_rowstride()) - - def set_has_changes(self, has_changes): - """Marks this Activity as having changes. This usually means - that this Activity's tab turns a red color or something else - to notify the user that this Activity needs attention.""" - if not self.has_focus() and has_changes: - self._activity_object.set_has_changes(True) - else: - self._activity_object.set_has_changes(False) - def get_id(self): return self._activity_id - def shutdown(self): - """Disconnect from the shell and clean up.""" - self._dbus_service.shutdown() - ############################################################# # Pure Virtual methods that subclasses may/may not implement ############################################################# @@ -390,29 +233,5 @@ class Activity(object): """Called to request the activity to publish itself on the network.""" pass - def on_lost_focus(self): - """Triggered when this Activity loses focus.""" - pass - - def on_got_focus(self): - """Triggered when this Activity gets focus.""" - pass - - def on_disconnected_from_shell(self): - """Triggered when we disconnect from the shell.""" - pass - - def on_reconnected_to_shell(self): - """Triggered when the shell's service comes back.""" - pass - - def on_connected_to_shell(self): - """Triggered when this Activity has successfully connected to the shell.""" - pass - - def on_close_from_user(self): - """Triggered when this Activity is closed by the user.""" - pass - if __name__ == "__main__": main(sys.argv[1], sys.argv[2]) diff --git a/sugar/chat/ChatWindow.py b/sugar/chat/ChatWindow.py deleted file mode 100644 index b06175d9..00000000 --- a/sugar/chat/ChatWindow.py +++ /dev/null @@ -1,18 +0,0 @@ -import pygtk -pygtk.require('2.0') -import gtk - -from sugar.chat.Chat import Chat - -class ChatWindow(gtk.Window): - def __init__(self): - gtk.Window.__init__(self) - self._chat = None - - def set_chat(self, chat): - if self._chat != None: - self.remove(self._chat) - - self._chat = chat - self.add(self._chat) - self._chat.show() diff --git a/sugar/keybindings.py b/sugar/keybindings.py new file mode 100644 index 00000000..fbc215db --- /dev/null +++ b/sugar/keybindings.py @@ -0,0 +1,21 @@ +import gtk +import dbus + +# FIXME These should be handled by the wm, but it's incovenient +# to do that with matchbox at the moment + +def setup_global_keys(window, shell = None): + if not shell: + bus = dbus.SessionBus() + proxy_obj = bus.get_object('com.redhat.Sugar.Shell', '/com/redhat/Sugar/Shell') + shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.Shell') + + window.connect("key-press-event", __key_press_event_cb, shell) + +def __key_press_event_cb(window, event, shell): + if event.keyval == gtk.keysyms.F1: + shell.toggle_home() + if event.keyval == gtk.keysyms.F2: + shell.toggle_people() + if event.keyval == gtk.keysyms.F3: + shell.toggle_console()