# 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()
syntax highlighted by Code2HTML, v. 0.9.1