''' pop3 plugin for flauxtext delete deletes message off server and local machine alt+del deletes message off server only ctrl+del deletes message off local machine only enter opens text/html in new tab alt+click opens html in firefox ctrl+click opens full eml [headers, etc.] in new tab rightclick saves each attachment to its own [user-defined] file alt+rightclick saves all attachments to a user-defined directory if isinstance(flauxtext.pop3saveallto, str) and flauxtext.os.path.isdir(flauxtext.pop3saveallto): automatically save all messages in pop3saveallto ''' MESSAGES = {} # map header info to Message objects generated by Parser objects DEL_KEYVAL = 65535 SPACE_KEYVAL = 65507 ENTER_KEYVAL = 65293 INVALID_CHARS = '[^\w \-\.;:\\\?!]' def button_press_event_cb(tv, evt): print evt.button, evt.state if evt.button == 1: if evt.state & flauxtext.gtk.gdk.MOD1_MASK: open_eml_in_browser(tv) elif evt.state & flauxtext.gtk.gdk.CONTROL_MASK: open_eml_full(tv) elif evt.state & flauxtext.gtk.gdk.SHIFT_MASK: pass else: pass elif evt.button == 2: if evt.state & flauxtext.gtk.gdk.MOD1_MASK: save_attachs(tv, True) elif evt.state & flauxtext.gtk.gdk.CONTROL_MASK: pass elif evt.state & flauxtext.gtk.gdk.SHIFT_MASK: pass else: save_attachs(tv) def key_press_event_cb(tv, evt): print evt.keyval, evt.state if evt.keyval == DEL_KEYVAL: if evt.state & flauxtext.gtk.gdk.MOD1_MASK: del_msg_off_server(tv) elif evt.state & flauxtext.gtk.gdk.CONTROL_MASK: del_msg_off_local(tv) elif evt.state & flauxtext.gtk.gdk.SHIFT_MASK: pass else: del_msg_off_server(tv) del_msg_off_local(tv) tv.get_model().remove(tv.get_model().get_iter(tv.get_cursor()[0])) elif evt.keyval == SPACE_KEYVAL: if evt.state & flauxtext.gtk.gdk.MOD1_MASK: get_all_email(tv) elif evt.state & flauxtext.gtk.gdk.CONTROL_MASK: get_email(tv, *(get_current_message(tv).info[:3])) elif evt.state & flauxtext.gtk.gdk.SHIFT_MASK: pass else: pass elif evt.keyval == ENTER_KEYVAL: if evt.state & flauxtext.gtk.gdk.MOD1_MASK: open_eml_in_browser(tv) elif evt.state & flauxtext.gtk.gdk.CONTROL_MASK: open_eml_full(tv) elif evt.state & flauxtext.gtk.gdk.SHIFT_MASK: open_eml_full(tv) else: open_eml(tv) # callbacks ################################################################################ # duty code def open_eml(tv): msg = get_current_msg(tv) content_type = msg.get_content_type().lower() if flauxtext.pop3textplainonly and (content_type.startswith('text/plain') or ( content_type.startswith('multipart') and ([subpart for subpart in msg._payload if subpart['Content-Type'].startswith('text/plain')]) )): try: # since list comprehensions expose their iteration variable, subpart will be available if msg is multipart and contains a plain text element self.new_buffer().set_text(str(subpart)) except: self.new_buffer().set_text(msg._payload) else: self.new_buffer().set_text(str(msg)) def open_eml_in_browser(tv): msg = get_current_msg(tv) for m in msg.walk(): if m.get_content_type() == 'text/html': fn = flauxtext.os.path.join(flauxtext.pop3saveallto, flauxtext.re.sub(INVALID_CHARS, '', '__'.join(get_headers_from_msg(msg))) + '.html') f = open(fn, 'w') f.write(m.get_payload()) f.close() flauxtext.Popen(flauxtext.webbrowsercmd + fn) def open_eml_full(tv): self.new_buffer().set_text(str(get_current_msg(tv))) def del_msg_off_server(tv): msg = get_current_msg(tv) p = flauxtext.poplib.POP3(msg.info[0]) p.user(msg.info[1]) p.pass_(msg.info[2]) p.dele(msg.info[3]) p.quit() for k, v in MESSAGES.items(): if (v.info[:3] == msg.info[:3]) and (v.info[3] > msg.info[3]): v.info[3] -= 1 MESSAGES[k] = v def del_msg_off_local(tv): try: flauxtext.os.remove(flauxtext.os.path.join(flauxtext.pop3saveallto, flauxtext.re.sub(INVALID_CHARS, '', '__'.join(get_headers_from_msg(get_current_msg(tv)))) + '.eml')) except: pass def save_attachs(tv, askoncefordir=False): msg = get_current_msg(tv) onedir = [] def save(m): d = flauxtext.gtk.FileChooserDialog("save %s [of type %s] where?" % (m.get_filename('unknown'), m.get_content_type()), self.w, gtk.FILE_CHOOSER_ACTION_SAVE,(gtk.STOCK_CANCEL,0,gtk.STOCK_SAVE,1)) d.set_property("show-hidden", flauxtext.openshowhidden) d.set_default_response(1) d.set_current_name(fn) if askoncefordir: if onedir == []: r = d.run() onedir.append(flauxtext.os.path.dirname(d.get_filename())) else: r = 1 d.set_filename(flauxtext.os.path.join(onedir[0], m.get_filename('unknown'))) if r: f = open(d.get_filename(), 'w') f.write(m.get_payload(None, True)) f.close() d.destroy() def recurs(m): if m.is_multipart(): for subpart in msg.get_payload(): if subpart.is_multipart(): recurs(subpart) else: save(subpart) else: save(subpart) recurs(msg) def get_current_msg(tv): return MESSAGES.get(tuple(tv.get_model()[tv.get_cursor()[0]]), None) def get_headers_from_msg(msg): headers = [] cap = flauxtext.string.capitalize for tkn in flauxtext.pop3format: try: if tkn.lower() == 'n_attachments': headers.append(isinstance(msg._payload, list) and str(len(msg._payload)) or '0') elif tkn.lower() == 'date': s = msg[cap(tkn)] headers.append(flauxtext.time.strftime(flauxtext.pop3dateformat, flauxtext.time.strptime(s[:s.index(' -0')], '%a, %d %b %Y %H:%M:%S'))) elif (tkn.lower() == 'from') and flauxtext.pop3shortfrom: s = msg[cap(tkn)] if s.count('"') == 2: s = s[(s.index('"') + 1):s.rindex('"')] headers.append(s) else: headers.append(msg[cap(tkn)]) except: # ensure L is the right length headers.append('unknown') return headers def get_all_email(tv): for i in range(len(flauxtext.pop3servers)): get_email(tv, flauxtext.pop3servers[i], flauxtext.pop3names[i], flauxtext.pop3passes[i]) def get_email(tv, srvr=None, nym=None, passw=None): ''' login to server srv using name nym and password passw, [prompting if any of them is None or the empty string], download the emails, and put them in gtk.ListStore tv.get_model() ''' if not (srvr and nym and passw): # if any of these is the empty string or None, prompt d = flauxtext.gtk.Dialog("spam?", self.w, flauxtext.gtk.DIALOG_NO_SEPARATOR, (flauxtext.gtk.STOCK_OK, 1, flauxtext.gtk.STOCK_CANCEL, 0)) flauxtext.respond_dialog_on_escape(d) srvrhbox = flauxtext.gtk.HBox() d.vbox.pack_start(srvrhbox) srvrhbox.pack_start(flauxtext.gtk.Label("server:")) srvr_e = flauxtext.gtk.Entry() srvrhbox.pack_start(srvr_e) srvr_e.set_text(srvr) nymhbox = flauxtext.gtk.HBox() d.vbox.pack_start(nymhbox) nymhbox.pack_start(flauxtext.gtk.Label("login name:")) nym_e = flauxtext.gtk.Entry() nymhbox.pack_start(nym_e) nym_e.set_text(nym) passwhbox = flauxtext.gtk.HBox() d.vbox.pack_start(passwhbox) passwhbox.pack_start(flauxtext.gtk.Label("password:")) passw_e = flauxtext.gtk.Entry() passwhbox.pack_start(passw_e) passw_e.set_visibility(False) passw_e.set_invisible_char(flauxtext.passchar) passw_e.set_text(passw) passw_e.connect('activate', lambda x=None,y=None,z=None:d.response(1)) if len(passw_e.get_text()) < 1: passw_e.grab_focus() if len(nym_e.get_text()) < 1: nym_e.grab_focus() if len(srvr_e.get_text()) < 1: srvr_e.grab_focus() d.show_all() r = d.run() passw_e.set_visibility(True) d.destroy() if r == 1: srvr = srvr_e.get_text() nym = nym_e.get_text() passw = passw_e.get_text() else: return msg_list = tv.get_model() try: p = flauxtext.poplib.POP3(srvr) p.user(nym) p.pass_(passw) for i in range(len(p.list()[1])): # loop through the messages msg = flauxtext.email.Parser.Parser().parsestr('\n'.join(p.retr(i+1)[1])) print 'info' in msg.__dict__ msg.info = [srvr, nym, passw, (i + 1)] hdrs = get_headers_from_msg(msg) MESSAGES[tuple(hdrs)] = msg msg_list.append(hdrs) if isinstance(flauxtext.pop3saveallto, str) and flauxtext.os.path.isdir(flauxtext.pop3saveallto): f = open(flauxtext.os.path.join(flauxtext.pop3saveallto, flauxtext.re.sub(INVALID_CHARS, '', '__'.join(hdrs)) + '.eml'), 'w') f.write(str(msg)) f.close() p.quit() except flauxtext.poplib.error_proto, e: self.flash_info('ERROR: ' + str(e), 5e3) def pop3(*a, **kw): ''' flauxtext plugin which grabs email off servers listed in flauxtext.pop3servers using names and passwords in flauxtext.pop3names and flauxtext.pop3passes. self is the current instance of the flauxtext class. self.globals is its global namespace. a is probably a 1-tuple containing a useless gtk.Action. this function prompts for login if necessary, makes a gtk.TreeView in the bottom half of self.vpaned, and populates it with your emails. ''' if not hasattr(self, 'pop3treeview') or (self.pop3treeview == None): flauxtext.imp0rt('poplib', 'email.Parser') msg_list = flauxtext.gtk.ListStore(*[str]*len(flauxtext.pop3format)) msg_list.set_sort_column_id(0, flauxtext.gtk.SORT_ASCENDING) tv = flauxtext.gtk.TreeView(msg_list) tv.connect('button-press-event', button_press_event_cb) tv.connect('key-press-event', key_press_event_cb) tv.columns = map(flauxtext.gtk.TreeViewColumn, map(flauxtext.string.capitalize, flauxtext.pop3format)) map(tv.append_column, tv.columns) for i in range(len(flauxtext.pop3format)): tv.columns[i].cell = flauxtext.gtk.CellRendererText() tv.columns[i].pack_start(tv.columns[i].cell) tv.columns[i].set_attributes(tv.columns[i].cell, text=i) tv.columns[i].set_property('reorderable', True) tv.columns[i].set_property('resizable', True) tv.columns[i].set_property('clickable', True) # http://www.pygtk.org/pygtk2tutorial/sec-TreeModelInterface.html#sec-SortingTreeModelRows for tkn in flauxtext.pop3format: if tkn == tkn.upper(): tv.set_search_column(flauxtext.pop3format.index(tkn)) where = flauxtext.pop3where.lower() sw = flauxtext.gtk.ScrolledWindow() sw.add(tv) tb = flauxtext.gtk.TextBuffer() tb.filename = flauxtext.UNTITLED_FILENAME tb.undostack = flauxtext.UndoStack() tb.get_all_text = lambda: '' tv.get_buffer = lambda: tb tv.get_overwrite = lambda: False if where == 'tab': self.notebk.append_page(sw, flauxtext.gtk.Label('email')) self.notebk.set_show_tabs(True) self.notebk.set_current_page(self.notebk.page_num(sw)) print self.notebk.page_num(sw) # XXX else: pane = self.split_from(where) pane.set_position(flauxtext.pop3pos[0]) eval('pane.add' + ['2', '1'][where in ('left', 'top')], locals())(sw) self.pop3treeview = tv self.w.show_all() else: tv = self.pop3treeview get_all_email(tv)