# File: outbox.py # Purpose: outbox. duh import utils import gtk import time import os.path import string import cPickle import boxformats from boxformats import * import boxformats.configbox from pyneheaders import * import boxtypes import pynemsg from boxtypes.superbox import * from ptk.big_edit_box import * import ptk.misc_widgets class outbox(superbox): """ General outbox for nntp or smtp outgoing mail. """ def get_flags(self): return BOX_DEL_MESSAGE | BOX_UPDATE_METHOD def __init__(self, user, uid): self.load(user, uid) def save(self, user): tosave = {} def _saveme(key, tosave=tosave, self=self): tosave[key] = self.__dict__[key] _saveme("uid") _saveme("name") _saveme("opts") _saveme("export_format") f = open(self.uid+".fol", "w") cPickle.dump(ver_stamp, f, 1) cPickle.dump("OUTBOX", f, 1) cPickle.dump(tosave, f, 1) f.close() def load(self, user, uid): self.load_box (user, uid) def _ifmissing(key, value, self=self): if not self.__dict__.has_key(key): self.__dict__[key] = value _ifmissing("uid", uid) _ifmissing("name", _("Outbox")) _ifmissing("opts", 0) _ifmissing("export_format", (boxformats.BF_FLATFILEBOX, None)) self.startup(user) def startup(self, user): """ Get an io module for loading/saving articles """ i = self.export_format[0] # this is a pretty piece of code :-) bf_class = boxformats.__dict__[boxformats.__all__[i]]. \ __dict__[boxformats.__all__[i]] self._io = bf_class(user, self) self.messages = self._io.get_contents() def new_message(self, user, from_folder, type, to=""): """ Create and edit a new outgoing message from 'folder'. """ new = pynemsg.pynemsg() persn = from_folder.get_default_personality(user) new.date = int(time.time()) # send with this folder's settings new.senduid = from_folder.uid # create some basic headers: new.headers = {} if type == REPLY_GROUP: # news postings: new.headers["newsgroups"] = to if persn.replyto != "": new.headers["reply-to"] = persn.replyto else: # email message new.headers["to"] = to if persn.replyto != "": new.headers["reply-to"] = persn.replyto new.headers["cc"] = "" # universal headers new.headers["subject"] = "" if persn.org != "": new.headers["organization"] = persn.org if persn.realname != "": new.headers["from"] = "\""+persn.realname+"\" <"+persn.emailaddr+">" else: new.headers["from"] = persn.emailaddr if persn.sigfile == "": new.parts_text.append("\n") else: try: # Open and append sigfile f = open(os.path.expanduser(persn.sigfile), "r") except IOError: # Failure ptk.misc_widgets.InfoBox (_("Error"), _("Error opening sigfile..."), gtk.STOCK_OK) new.parts_text.append("\n") else: new.parts_text.append("\n\n\n-- \n") while 1: line = f.readline() if line == "": break else: new.parts_text[0] = new.parts_text[0] + line new.parts_header.append(new.headers) # Shove in outbox self.save_new_article(new) new.make_source() self.save_article(new) self.changed = 1 user.update() new.edit(self, user, is_new_msg=1) def do_reply(self, user, msg, from_folder, type=REPLY_AUTO, parent_win=None): # Compare folders with this to make sure they are # suitable boxes to send mail with def compare_mailbox(box): if isinstance(box, boxtypes.mailbox.mailbox): if box.send_type != boxtypes.mail_send.MAIL_SEND_NONE: return 1 return 0 if type==REPLY_AUTO: # Select most appropriate option for message type if isinstance(from_folder, boxtypes.nntpbox.newsgroup): # need to find the nntpbox parent box parent_folder = user.parent_of(from_folder) self._reply(user, from_folder, parent_folder, msg, REPLY_GROUP) return elif isinstance(from_folder, boxtypes.mailbox.mailbox): self._reply(user, from_folder, from_folder, msg, REPLY_EMAIL) return if type==REPLY_EMAIL: if isinstance(from_folder, boxtypes.nntpbox.newsgroup): # Replying to an NNTP message by mail. Get a mailbox to use def ok_click(folder, self=self, _from_folder=from_folder, user=user, msg=msg): self._reply(user, _from_folder, folder, msg, REPLY_EMAIL) utils.UserChooseBox(user, compare_mailbox, ok_click, _("Pyne - Select a mailbox"), parent_win=parent_win) return elif isinstance(from_folder, boxtypes.mailbox.mailbox): self._reply(user, from_folder, from_folder, msg, REPLY_EMAIL) return if type==REPLY_GROUP: if isinstance(from_folder, boxtypes.nntpbox.newsgroup): # need to find the nntpbox parent box parent_folder = user.parent_of(from_folder) self._reply(user, from_folder, parent_folder, msg, REPLY_GROUP) return elif isinstance(from_folder, boxtypes.mailbox.mailbox): self._reply(user, from_folder, from_folder, msg, REPLY_CC) return if type==REPLY_FORWARD: if isinstance(from_folder, boxtypes.nntpbox.newsgroup): # Forwarding an NNTP message by mail. Get a mailbox to use def ok_click(folder, _from_folder=from_folder, self=self, user=user, msg=msg): self._reply(user, _from_folder, folder, msg, REPLY_FORWARD) utils.UserChooseBox(user, compare_mailbox, ok_click, _("Pyne - Select a mailbox"), parent_win=parent_win) elif isinstance(from_folder, boxtypes.mailbox.mailbox): self._reply(user, from_folder, from_folder, msg, REPLY_FORWARD) def _reply(self, user, from_folder, reply_folder, msg, type, send_to="", dont_open_editbox=0): """ Reply to message. type is method of reply. see REPLY_ in message.py. Original message is in from_folder, send using reply_folder. """ # Mark message as replied (not forwarded) if type != REPLY_FORWARD: msg.opts = msg.opts | MSG_ISREPLIED from_folder.save_article(msg) from_folder.changed = 1 # Create new message old = msg new = pynemsg.pynemsg() # send with this folder's settings new.senduid = reply_folder.uid persn = reply_folder.get_default_personality(user) # create some basic headers: new.headers = {} new.date = int(time.time()) if persn.replyto != "": new.headers["reply-to"] = persn.replyto if old.headers.has_key("newsgroups") and type == REPLY_GROUP: new.headers["newsgroups"] = old.headers["newsgroups"] # email message else: if type != REPLY_FORWARD: if old.headers.has_key("reply-to"): new.headers["to"] = old.headers["reply-to"] else: try: new.headers["to"] = old.headers["from"] except KeyError: new.headers["to"] = send_to else: new.headers["to"] = send_to if type == REPLY_CC and old.headers.has_key("cc"): new.headers["cc"] = old.headers["cc"] else: new.headers["cc"] = "" # universal headers if type == REPLY_FORWARD: new.headers["subject"] = "Fwd: "+old.headers["subject"] else: if string.lower(old.headers["subject"][:3]) == "re:": new.headers["subject"] = old.headers["subject"] else: new.headers["subject"] = "Re: "+old.headers["subject"] if persn.org != "": new.headers["organization"] = persn.org if persn.realname != "": new.headers["from"] = "\""+persn.realname+"\" <"+persn.emailaddr+">" else: new.headers["from"] = persn.emailaddr if old.headers.has_key("references"): new.headers["references"] = old.headers["references"]+" "+old.headers["message-id"] else: new.headers["references"] = old.headers["message-id"] # Copy body new.body = "" new.parts_text.append(old.external_parser(user, 0)) # Strip other guys sig (if not forwarding) if type != REPLY_FORWARD: x = string.find(new.parts_text[0], "\n-- \n") if x != -1: new.parts_text[0] = new.parts_text[0][0:x] new.parts_header.append(old.headers) # Put quote '> ' things in lines = string.split(new.parts_text[0], "\n") # check no lines are i = 0 for x in lines: if len(x) > user.linewrap*2: y = string.find(x[:user.linewrap], " ") # break here lines.insert(i+1, x[user.linewrap+y+1:]) lines[i] = lines[i][:user.linewrap+y] i = i + 1 new.parts_text[0] = "> "+string.join(lines, "\n> ") if persn.sigfile != "": try: # Open and append sigfile f = open(os.path.expanduser(persn.sigfile), "r") except IOError: # Failure ptk.misc_widgets.InfoBox (_("Error"), _("Error opening sigfile:\n%s") % persn.sigfile, gtk.STOCK_OK) else: new.parts_text[0] = new.parts_text[0]+"\n\n\n-- \n" while 1: line = f.readline() if line == "": break else: new.parts_text[0] = new.parts_text[0] + line # Reply note reply_note = string.replace(user.replyhead, "$DATE", time.strftime("%d %b %Y, %H:%M:%S", time.localtime(old.date))) reply_note = string.replace(reply_note, "$FROM", utils.split_address(old.headers["from"])[0]) new.parts_text[0] = reply_note + "\n" + new.parts_text[0] if type == REPLY_FORWARD: if old.headers.has_key("newsgroups"): new.parts_text[0] = "To: "+old.headers["newsgroups"]+"\n"+new.parts_text[0] new.parts_text[0] = "From: "+old.headers["from"]+"\n"+new.parts_text[0] new.parts_text[0] = "---------- "+_("Forwarded Message")+" ----------\n"+new.parts_text[0] new.date = int(time.time()) # Shove in outbox self.save_new_article(new) new.make_source() self.save_article(new) self.changed = 1 user.update() if dont_open_editbox == 0: if type == REPLY_FORWARD: new.edit(self, user, is_new_msg=1) else: new.edit(self, user, is_new_msg=1) def getstats(self): """ Returns total unsend msgs. """ return (len(self.messages),) def get_menu(self): """ This box type specific options. """ return [] def menu_chosen(self, _a, choice): """ Activate the chosen menu option. """ return def update(self, user, progressbar): """ Sends all the messages in the outbox. """ self.locked = 1 progressbar.bar_msg(_("0/%d outgoing messages posted") % len(self.messages)) # load messages msgs = [] for x in self.messages: # rebuild body without dummy message id msg = self.load_article(x) msg.make_source(presend=1) msgs.append(msg) # sort messages by uid into seperate dict entries msg_by_box = {} for x in msgs: if msg_by_box.has_key(x.senduid): msg_by_box[x.senduid].append(x) else: msg_by_box[x.senduid] = [] msg_by_box[x.senduid].append(x) # send them self.__post_msgs(msg_by_box, user, progressbar) progressbar.finished() del self.locked def send_one(self, user, progressbar, msg_id): """ Only send messagee 'msg_id'. """ self.locked = 1 progressbar.bar_msg(_("%d/%d outgoing messages posted") % (0,1)) # load message msg = self.load_article(msg_id) msg.make_source(presend=1) # sort messages by uid into seperate dict entries msg_by_box = { msg.senduid: [ msg ] } # send it self.__post_msgs(msg_by_box, user, progressbar) progressbar.finished() del self.locked def __post_msgs(self, msg_by_box, user, progressbar): """ Sends all the messages in the outbox. msg_by_box is a dictionary. keys are uids of sending boxes, contents of each a list of message bodies to be sent with that box. """ sent_list = [] # list of message-ids sucessfully sent # go through box uids sending messages. i = 0 total = 0 for x in msg_by_box.keys(): for y in msg_by_box[x]: total = total + 1 sendbox = user.get_folder_by_uid(x) # The sendbox has been deleted since this message was created :-( if sendbox == None: usermsg = _("Send box not found posting message \'%s\'") % y.headers["subject"] progressbar.log_msg(usermsg) progressbar.bar_msg(usermsg) continue # should check for failure... progressbar.bar_msg(_("Outgoing: Connecting to server (%s)...") % sendbox.name) try: c = sendbox.get_connection() except (timeout_exception, connect_exception), e: # Dirty catch-all msg = _("Outgoing: Error:")+" <%s> " % sendbox.name + str_net_error(e) progressbar.log_msg(msg) continue # send messages for y in msg_by_box[x]: try: c.post_message(y) except timeout_exception, e: # Connection screwed on these errors pagermsg = _("Send error posting message \'%s\'") % y.headers["subject"] fullmsg = pagermsg+": "+str_net_error(e) progressbar.log_msg(fullmsg) progressbar.bar_msg(pagermsg) break except send_exception, e: pagermsg = _("Send error posting message \'%s\'") % y.headers["subject"] fullmsg = pagermsg+": "+str_net_error(e) progressbar.log_msg(fullmsg) progressbar.bar_msg(pagermsg) else: sent_list.append(y.headers["message-id"]) i = i + 1 progressbar.bar_pos(i/float(len(self.messages))) progressbar.bar_msg(_("%d/%d outgoing messages posted") % (i, len(self.messages))) c.close() usermsg = _("%d/%d outgoing messages posted") % (len(sent_list), total) progressbar.log_msg(usermsg) progressbar.bar_enable(False) progressbar.bar_pos(0.0) progressbar.bar_msg(usermsg) # Remove sent messages, appending to sent mail and setting # date as when sent. sentbox = user.get_folder_by_uid("sent") for x in sent_list: msg = self.load_article(x) self.delete_article(x) msg.opts = msg.opts | MSG_SENT msg.date = int(time.time()) sentbox.save_new_article(msg) sentbox.changed = 1 self.changed = 1 user.queue_action(ACT_UPDATE) def setup(self, user, parent_win): """ Configure an outbox. """ win = gtk.Window() win.set_title(_("%s Setup") % self.name) win.set_transient_for(parent_win) box = gtk.VBox() win.add(box) box.show() notebook = gtk.Notebook() notebook.set_tab_pos(gtk.POS_TOP) box.pack_start(notebook) notebook.show() # First page: Misc settings settings_box = big_edit_box(self, ( ("name", "Name:", VAR_TYPE_STRING, 0, 0), ) ) label = gtk.Label(_("Settings")) notebook.append_page(settings_box, label) settings_box.show() # 2nd page: storage method label = gtk.Label(_("Mailbox Format")) formatbox = boxformats.configbox.configbox(self, user, win) notebook.append_page(formatbox, label) formatbox.show() def save_changes(_button, self=self, user=user, win=win, \ settings_box=settings_box): # Extract info settings_box.apply_changes() # save changes to disk self.save(user) # update folder list self.changed = 1 user.update() def save_changes_close(_button, win=win, save_changes=save_changes): save_changes(_button) win.destroy() def _cancel(_button, win=win): win.destroy() # Seperator between entry boxes and buttons separator = gtk.HSeparator() box.pack_start(separator, expand=False) separator.show() # Buttons at bottom buttonbox = ptk.misc_widgets.MyButtonBox() box.pack_start(buttonbox, expand=False) buttonbox.show() button = gtk.Button(stock="gtk-ok") button.connect("clicked", save_changes_close) buttonbox.pack_end(button, expand=False) button.show() button = gtk.Button(stock="gtk-apply") button.connect("clicked", save_changes) buttonbox.pack_end(button, expand=False) button.show() button = gtk.Button(stock="gtk-cancel") button.connect("clicked", _cancel) buttonbox.pack_end(button, expand=False) button.show() win.show()