def send_alert(self): """ Notifies user according to requirements. """ # check if user wants notifications if self.config['notify'] == False: return # check whether conditions are met if self.account['diff'] <= 0: return # check whether user wants an effect on the icon if self.config['anim'] == True: self.icon.DemandsAttention(True, self.config['how']) # check whether user wants a dialogue if self.config['dia'] == True: # checking our grammar ;) # we have at least one new email if self.account['count'] > 1: message = _("You have %s new emails") % (str( self.account['count'])) else: message = _("You have 1 new email") self.icon.ShowDialog(message, 3) # check whether user wants a sound if self.config['sound'] == True: try: os.popen('aplay ' + self.wav) except: # restore default sound file if custom is corrupted self.wav = os.path.abspath("./snd/pop.wav")
def add_subscription(self, request=None): """ Prompts user to add subscription details. """ # if requesting new username: if request == 'username': # set dialogue flag to 'username' self.flag = 'username' # prompt for username message = _("Please, enter your Gmail username:"******"message" : message, "buttons" : "gtk-go-forward-ltr;cancel"}, {"widget-type" : "text-entry"}) # if requesting new password: elif request == 'password': # set dialogue flag to 'password' self.flag = 'password' # prompt for password message = _("Please, enter your Gmail password:"******"message" : message, "buttons" : "ok;cancel"}, {"widget-type" : "text-entry", "visible" : False}) # default request is to encrypt username and password else: # open, encode and write to subscription file file = open(self.subpath, 'wb') file.write(base64.b64encode(str(self.account['username']+ \ '\n'+self.account['password']).encode('ascii'))) file.close() # run subscription check as double check self.check_subscription()
def send_alert(self): """ Notifies user according to requirements. """ # check if user wants notifications if self.config['notify'] == False: return # check whether conditions are met if self.account['diff'] <= 0: return # check whether user wants an effect on the icon if self.config['anim'] == True: self.icon.DemandsAttention(True, self.config['how']) # check whether user wants a dialogue if self.config['dia'] == True: # checking our grammar ;) # we have at least one new email if self.account['count'] > 1: message = _("You have %s new emails") % (str(self.account['count'])) else: message = _("You have 1 new email") self.icon.ShowDialog(message, 3) # check whether user wants a sound if self.config['sound'] == True: try: os.popen('aplay ' + self.wav) except: # restore default sound file if custom is corrupted self.wav = os.path.abspath("./snd/pop.wav")
def request_gmail(self): """ Authenticates and requests inbox content from Gmail. """ gmailfeed = 'https://mail.google.com/mail/feed/atom/' request = _urllib.Request(gmailfeed) # connect to Gmail error = None try: handle = _urllib.urlopen(request) except IOError as err: # here we will need "fail" as we receive a 401 error to get access error = err if not hasattr(error, 'code') or error.code != 401: # we got an error - but not a 401 error message = _("WARNING: Gmail applet failed to connect to Gmail atom feed.") self.error(message) return None # get the www-authenticate line from the headers authline = error.headers['www-authenticate'] # from this header we extract scheme and realm authobject = re.compile( r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"]([^'"]+)['"]''', re.IGNORECASE) matchobject = authobject.match(authline) # make sure scheme and realm was found if not matchobject: message = _("WARNING: Gmail atom feed is badly formed: ") m = message + authline self.error(m) return None # check what scheme we have scheme = matchobject.group(1) if scheme.lower() != 'basic': message = _("WARNING: Gmail Applet is not equipped for authentication other than BASIC.") return self.error(message) # authenticate and get inbox content account = ('%s:%s' % (self.account['username'], self.account['password'])).encode('ascii') base64string = base64.encodestring(account)[:-1].decode() authheader = "Basic %s" % base64string request.add_header("Authorization", authheader) try: handle = _urllib.urlopen(request) except IOError as error: # here we shouldn't fail if the username/password is right message = _("WARNING: Gmail username or password may be wrong.") self.error(message) return None return handle
def error(self, message): """ Warns the user if an error occured. """ # no need to update the icon if self.flag == 'error': return if self.config['info'] != 'quickinfo': # remove previous quickinfo if needed: self.icon.SetQuickInfo('') # get size from config: size = self.config['info'].split()[0] # pass size to filename: file = './img/gmail-error-'+size+'.svg' # set icon with error emblem self.icon.SetIcon(os.path.abspath(file)) else: # reset icon in case needed self.icon.SetIcon(os.path.abspath('./icon')) # set quickinfo: self.icon.SetQuickInfo(_('Error!')) # check if any error is already known # or if the user is changing subscription details if self.flag != None: return # set error flag self.flag = 'error' self.account['count'] = -99 # to be sure that the icon will be updated # show dialogue self.icon.ShowDialog(message, 4)
def on_answer_dialog(self, key, content): """ Processes dialogue input for username and password. """ # check user pressed the first button (OK) or Enter if key == 0 or key == CDApplet.DIALOG_KEY_ENTER: # check user entered something if len(content) > 0: # check if requesting username if self.flag == 'username': # append account with username self.account['username'] = format(content) # request password self.add_subscription('password') # check if requesting password elif self.flag == 'password': # append account with password self.account['password'] = format(content) # finish up registration process self.add_subscription() else: # should not happen (kept in case another dialogue needs # to be made in future). pass else: message = _("Sorry, there was no input!") self.error(message)
def error(self, message): """ Warns the user if an error occured. """ # no need to update the icon if self.flag == 'error': return if self.config['info'] != 'quickinfo': # remove previous quickinfo if needed: self.icon.SetQuickInfo('') # get size from config: size = self.config['info'].split()[0] # pass size to filename: file = './img/gmail-error-' + size + '.svg' # set icon with error emblem self.icon.SetIcon(os.path.abspath(file)) else: # reset icon in case needed self.icon.SetIcon(os.path.abspath('./icon')) # set quickinfo: self.icon.SetQuickInfo(_('Error!')) # check if any error is already known # or if the user is changing subscription details if self.flag != None: return # set error flag self.flag = 'error' self.account['count'] = -99 # to be sure that the icon will be updated # show dialogue self.icon.ShowDialog(message, 4)
def __init__(self, inbox): gtk.Menu.__init__(self) # get all mail from inbox for mail in inbox: # create label menu with markups string = '<b>' + html_escape(mail['author']) + ':</b>\n' # check if mail has subject / title if mail['title'] == None or len(mail['title']) == 0: string += '<i>(' + _('No Subject') + ')</i>' elif len(mail['title']) > 80: string += html_escape(mail['title'][:77]) + '...' else: string += html_escape(mail['title']) menu_item = gtk.ImageMenuItem() # the true label is set after with set_markup() menu_item.set_label('') try: menu_item.set_image(gtk.image_new_from_file('./img/menu-gmail.png')) except: menu_item.set_image(gtk.Image.new_from_file('./img/menu-gmail.png')) menu_item.get_children()[0].set_markup(string) menu_item.url = mail['link'] menu_item.connect('activate', self.open_mail, mail) self.append(menu_item) menu_item.show() # add a separator if mail is not last in list if inbox.index(mail) != len(inbox) - 1: sep = gtk.SeparatorMenuItem() self.append(sep) sep.show() self.show()
def check_subscription(self): """ Checks which accounts the user subsribed to, gets usernames and passwords as well as how often the account should be checked. """ # reset flag in case of prior error: self.flag = None # open subscription file and read data try: file = open(self.subpath, 'r') sub = file.read() file.close() except: message = _("Please fill in your Gmail account.") self.error(message) return # check if there was any data if len(sub) < 1: message = _("Please fill in your Gmail account.") self.error(message) return # if so process the data account = base64.b64decode( sub.strip('\n').encode('ascii')).decode().split('\n') # check if the data is correct if len(account) != 2: message = _("Please fill in your Gmail account.") self.error(message) return # then process the data into account self.account = { 'username': account[0], 'password': account[1], 'count': 0, 'diff': 0 } self.check_mail() self.repeat()
def check_subscription(self): """ Checks which accounts the user subsribed to, gets usernames and passwords as well as how often the account should be checked. """ # reset flag in case of prior error: self.flag = None # open subscription file and read data try: file = open(self.subpath, 'r') sub = file.read() file.close() except: message = _("Please fill in your Gmail account.") self.error(message) return # check if there was any data if len(sub) < 1: message = _("Please fill in your Gmail account.") self.error(message) return # if so process the data account = base64.b64decode(sub.strip('\n').encode('ascii')).decode().split('\n') # check if the data is correct if len(account) != 2: message = _("Please fill in your Gmail account.") self.error(message) return # then process the data into account self.account = {'username': account[0], 'password': account[1], 'count': 0, 'diff': 0} self.check_mail() self.repeat()
def on_build_menu(self): """ Appends items to right-click menu. """ message_add_label = _("Add or change subscription") message_add_tooltip = _("Use this to add or change your Gmail account details.") message_middle_click = _("middle-click") message_check_label = _("Check inbox now") message_check_tooltip = _("Check Gmail inbox now if you can't wait.") message_new_mail = _("Write a mail") self.icon.AddMenuItems([{"widget-type" : CDApplet.MENU_ENTRY, "label": message_add_label, "icon" : "gtk-add", "menu" : CDApplet.MAIN_MENU_ID, "id" : 1, "tooltip" : message_add_tooltip}, {"widget-type" : CDApplet.MENU_ENTRY, "label": message_check_label + " (" + message_middle_click + ")", "icon" : "gtk-refresh", "menu" : CDApplet.MAIN_MENU_ID, "id" : 2, "sensitive" : (len(self.account) > 1), # at least 'count' => -99 "tooltip" : message_check_tooltip}, {"widget-type" : CDApplet.MENU_ENTRY, "label": message_new_mail, "icon" : "gtk-new", "menu" : CDApplet.MAIN_MENU_ID, "id" : 3}])
def __init__(self): super(TransparentPostIt, self).__init__() self.set_title(_('GTG PostIt')) self.resize(200, 100) # Tell GTK+ that we want to draw the windows background ourself. # If we don't do this then GTK+ will clear the window to the # opaque theme default color, which isn't what we want. self.set_app_paintable(True) # The X server sends us an expose event when the window becomes # visible on screen. It means we need to draw the contents. On a # composited desktop expose is normally only sent when the window # is put on the screen. On a non-composited desktop it can be # sent whenever the window is uncovered by another. # # The screen-changed event means the display to which we are # drawing changed. GTK+ supports migration of running # applications between X servers, which might not support the # same features, so we need to check each time. self.connect('expose-event', self.expose) self.connect('screen-changed', self.screen_changed) # toggle title bar on click - we add the mask to tell # X we are interested in this event self.set_decorated(False) self.add_events(gdk.BUTTON_PRESS_MASK) self.connect('button-press-event', self.clicked) self.supports_alpha = False # initialize for the current display self.screen_changed(self) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) self.textview = gtk.TextView() self.textview.set_wrap_mode(gtk.WRAP_WORD) self.textview.set_editable(False) self.textbuffer = self.textview.get_buffer() sw.add(self.textview) # to make it transparent: # 1 - be called when the textview widget gets realized: # textview.connect("expose-event", textview_expose) self.textview.connect("expose-event", self.textview_expose) # 2 - in the expose callback, get the window corresponding to the drawing: # textview_window = textview.get_window(gtk.TEXT_WINDOW_WIDGET) or # textview_window = textview.get_window(gtk.TEXT_WINDOW_TEXT) # 3 - use cairo (like in expose) to draw the background of the gdk.Window self.add(sw) self.show_all()
def add_subscription(self, request=None): """ Prompts user to add subscription details. """ # if requesting new username: if request == 'username': # set dialogue flag to 'username' self.flag = 'username' # prompt for username message = _("Please, enter your Gmail username:"******"message": message, "buttons": "gtk-go-forward-ltr;cancel" }, {"widget-type": "text-entry"}) # if requesting new password: elif request == 'password': # set dialogue flag to 'password' self.flag = 'password' # prompt for password message = _("Please, enter your Gmail password:"******"message": message, "buttons": "ok;cancel" }, { "widget-type": "text-entry", "visible": False }) # default request is to encrypt username and password else: # open, encode and write to subscription file file = open(self.subpath, 'wb') file.write(base64.b64encode(str(self.account['username']+ \ '\n'+self.account['password']).encode('ascii'))) file.close() # run subscription check as double check self.check_subscription()
def get_inbox(self, xml_data): """ Counts the unread messages from the XML inbox content. """ inbox = [] try: try: tree = libxml2.parseDoc(xml_data) path = tree.xpathNewContext() path.xpathRegisterNs('purl', 'http://purl.org/atom/ns#') entries = path.xpathEval('//purl:entry') if len(entries) > 0: for entry in entries: path.setContextNode(entry) mail = {} mail['title'] = path.xpathEval('purl:title')[0].content mail['summary'] = path.xpathEval( 'purl:summary')[0].content mail['link'] = path.xpathEval('purl:link')[0].prop( 'href') mail['author'] = path.xpathEval( 'purl:author/purl:name')[0].content inbox.append(mail) except: tree = etree.fromstring(xml_data) namespaces = {'purl': 'http://purl.org/atom/ns#'} entries = tree.xpath('purl:entry', namespaces=namespaces) if len(entries) > 0: for entry in entries: mail = {} mail['title'] = entry.xpath( 'purl:title', namespaces=namespaces)[0].text mail['summary'] = entry.xpath( 'purl:summary', namespaces=namespaces)[0].text mail['link'] = entry.xpath( 'purl:link', namespaces=namespaces)[0].get('href') mail['author'] = entry.xpath( 'purl:author/purl:name', namespaces=namespaces)[0].text inbox.append(mail) return inbox except: message = _("WARNING: there was an error reading XML content.") self.error(message) return None
def get_inbox(self, xml_data): """ Counts the unread messages from the XML inbox content. """ inbox = [] try: try: tree = libxml2.parseDoc(xml_data) path = tree.xpathNewContext() path.xpathRegisterNs('purl', 'http://purl.org/atom/ns#') entries = path.xpathEval('//purl:entry') if len(entries) > 0: for entry in entries: path.setContextNode(entry) mail = {} mail['title'] = path.xpathEval('purl:title')[0].content mail['summary'] = path.xpathEval('purl:summary')[0].content mail['link'] = path.xpathEval('purl:link')[0].prop('href') mail['author'] = path.xpathEval('purl:author/purl:name')[0].content inbox.append(mail) except: tree = etree.fromstring(xml_data) namespaces = {'purl':'http://purl.org/atom/ns#'} entries = tree.xpath('purl:entry', namespaces = namespaces) if len(entries) > 0: for entry in entries: mail = {} mail['title'] = entry.xpath('purl:title', namespaces = namespaces)[0].text mail['summary'] = entry.xpath('purl:summary', namespaces = namespaces)[0].text mail['link'] = entry.xpath('purl:link', namespaces = namespaces)[0].get('href') mail['author'] = entry.xpath('purl:author/purl:name', namespaces = namespaces)[0].text inbox.append(mail) return inbox except: message = _("WARNING: there was an error reading XML content.") self.error(message) return None
def __init__(self, inbox): gtk.Menu.__init__(self) # get all mail from inbox for mail in inbox: # create label menu with markups string = '<b>' + html_escape(mail['author']) + ':</b>\n' # check if mail has subject / title if mail['title'] == None or len(mail['title']) == 0: string += '<i>(' + _('No Subject') + ')</i>' elif len(mail['title']) > 80: string += html_escape(mail['title'][:77]) + '...' else: string += html_escape(mail['title']) menu_item = gtk.ImageMenuItem() # the true label is set after with set_markup() menu_item.set_label('') try: menu_item.set_image( gtk.image_new_from_file('./img/menu-gmail.png')) except: menu_item.set_image( gtk.Image.new_from_file('./img/menu-gmail.png')) menu_item.get_children()[0].set_markup(string) menu_item.url = mail['link'] menu_item.connect('activate', self.open_mail, mail) self.append(menu_item) menu_item.show() # add a separator if mail is not last in list if inbox.index(mail) != len(inbox) - 1: sep = gtk.SeparatorMenuItem() self.append(sep) sep.show() self.show()
def on_build_menu(self): """ Appends items to right-click menu. """ message_add_label = _("Add or change subscription") message_add_tooltip = _( "Use this to add or change your Gmail account details.") message_middle_click = _("middle-click") message_check_label = _("Check inbox now") message_check_tooltip = _("Check Gmail inbox now if you can't wait.") message_new_mail = _("Write a mail") self.icon.AddMenuItems([ { "widget-type": CDApplet.MENU_ENTRY, "label": message_add_label, "icon": "gtk-add", "menu": CDApplet.MAIN_MENU_ID, "id": 1, "tooltip": message_add_tooltip }, { "widget-type": CDApplet.MENU_ENTRY, "label": message_check_label + " (" + message_middle_click + ")", "icon": "gtk-refresh", "menu": CDApplet.MAIN_MENU_ID, "id": 2, "sensitive": (len(self.account) > 1), # at least 'count' => -99 "tooltip": message_check_tooltip }, { "widget-type": CDApplet.MENU_ENTRY, "label": message_new_mail, "icon": "gtk-new", "menu": CDApplet.MAIN_MENU_ID, "id": 3 } ])
def request_gmail(self): """ Authenticates and requests inbox content from Gmail. """ gmailfeed = 'https://mail.google.com/mail/feed/atom/' request = _urllib.Request(gmailfeed) # connect to Gmail error = None try: handle = _urllib.urlopen(request) except IOError as err: # here we will need "fail" as we receive a 401 error to get access error = err if not hasattr(error, 'code') or error.code != 401: # we got an error - but not a 401 error message = _( "WARNING: Gmail applet failed to connect to Gmail atom feed.") self.error(message) return None # get the www-authenticate line from the headers authline = error.headers['www-authenticate'] # from this header we extract scheme and realm authobject = re.compile( r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"]([^'"]+)['"]''', re.IGNORECASE) matchobject = authobject.match(authline) # make sure scheme and realm was found if not matchobject: message = _("WARNING: Gmail atom feed is badly formed: ") m = message + authline self.error(m) return None # check what scheme we have scheme = matchobject.group(1) if scheme.lower() != 'basic': message = _( "WARNING: Gmail Applet is not equipped for authentication other than BASIC." ) return self.error(message) # authenticate and get inbox content account = ('%s:%s' % (self.account['username'], self.account['password'])).encode('ascii') base64string = base64.encodestring(account)[:-1].decode() authheader = "Basic %s" % base64string request.add_header("Authorization", authheader) try: handle = _urllib.urlopen(request) except IOError as error: # here we shouldn't fail if the username/password is right message = _("WARNING: Gmail username or password may be wrong.") self.error(message) return None return handle