# 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