#!/usr/bin/env python2.3

#  advodiary, (c) Chad Miller, 2002
#  Mail patches to Chad Miller <patches@chad.org>
#  License: GPL v2
#  
# Requires python 2.2 with (often built-in) xmlrpclib
# Create a .advodiaryrc config file with username = "you"\npassword = "p"\n
# in it, to use this.
#
#  URL: http://people.debian.org/~cmiller/advogato/
#  APT: deb http://people.debian.org/~cmiller/archive private main

import pdb

class advodiary:

	def __init__(self, username, authtoken=None, server=None):
		import xmlrpclib

		self.username = username
		self.password = None

		if authtoken:
			self.authtoken = authtoken
			self.authtokensource = "user"
		else:
			self.authtoken = None
			self.authtokensource = None

		if server:
			self.serverhandle = xmlrpclib.Server(server)
		else:
			self.serverhandle = xmlrpclib.Server("http://www.advogato.org/XMLRPC")

	def authenticate(self, password):
		self.password = password
		cookie = self.serverhandle.authenticate(self.username, password)
		self.authtoken = cookie
		self.authtokensource = "server"
		self.setauth(cookie)
		return(cookie)

	def setauth(self, authtoken):
		"""Fill auth information, for cases where we asked the server for it"""
		self.authtoken = authtoken
		self.authtokensource = "user"

	def get(self, number):
		"""Return the text of an entry, zero-based."""
		return(self.serverhandle.diary.get(self.username, number))

	def get_date(self, number):
		"""(This was unimplemented at time of writing)"""
		return(self.serverhandle.diary.get_date(self.username, number))

	def set(self, entrytext, number=-1):
		"""The entrytext and number arguments are reverse of the xmlrpc func."""
		if self.authtoken == None:
			raise DiaryAuthError
		return(self.serverhandle.diary.set(self.authtoken, number, entrytext))

	def len(self):
		"""Return the number of entries in the diary"""
		return(self.serverhandle.diary.len(self.username))








if __name__ == '__main__':
	import xmlrpclib, xmllib, tempfile, os, re, getopt, sys

# defaults
	comment = "<!-- Advogato diary entry.  This comment will be removed. -->"
	configfile = os.path.join(os.environ['HOME'], ".advodiaryrc")
	authcachepath = os.path.join(os.environ['HOME'], ".advodiary-authcache")
	username = ""
	password = ""
	debuglevel = 1
	if os.environ.has_key('EDITOR'):
		editor = os.environ['EDITOR']
	else:
		editor = "/usr/bin/vi"

	tempfile.tempdir = os.environ['HOME']

	tooShortException = "tooShort"
	noChangeException = "noChange"
	authcachefile = None

	def debuginfo(level, message):
		if level <= debuglevel:
			print ("[%s]" % (message))

	def getauthorization(session):
# try to read auth cookie from cache file
		authcachefile = "%s-%s" % (authcachepath, username)
		if os.path.isfile(authcachefile):
			debuginfo(1, "using cached auth cookie for %s" % (username))
			cookiecachefile = open(authcachefile, "r")
			cookie = re.sub("[\r\n]", "", cookiecachefile.read())
			debuginfo(2, "cookie is [%s]" % (cookie))
			cookiecachefile.close()
			session.setauth(cookie)
		else:
			debuginfo(1, "authenticating to server as %s" % (username))
			cookie = session.authenticate(password)
# save the cookie for future cache hits
			tempcookiefilename = tempfile.mktemp()
			tempcookiefile = open(tempcookiefilename, "w")
			os.chmod(tempcookiefilename, 0600)
			tempcookiefile.write(cookie)
			tempcookiefile.close()
			os.rename(tempcookiefilename, authcachefile)

	def usage():
		print "advodiary [-h] [-u username] [-s sourcefile] [-e entrynumber] [--help]"
		print "          [--username=username] [--source=sourcefile] [--entrynumber=N]"
		print "(special case of sourcefile: '-' and 'stdin' are standard input)"


# read in config file, if it exists
	if os.path.isfile(configfile):
		execfile(configfile)
	else:
		debuginfo(0, "no %s config file!" % (configfile))

	try:
		opts, args = getopt.getopt(sys.argv[1:], "hu:s:e:", ["help", "username=", "source=", "entry="])
	except getopt.GetoptError, desc:
		print "Error: %s" % (desc)
		usage()
		sys.exit(2)

	entrynumberopt = None
	sourcefile = None
	endofoptions = len(opts)
	for o, a in opts:
		if o in ("-h", "--help"):
			usage()
			sys.exit()
		if o in ("-u", "--username"):
			username = a
		if o in ("-s", "--source"):
			sourcefile = a
		if o in ("-e", "--entry"):
			entrynumberopt = a

# complain about not having a username
	if (len(username) == 0) or (len(password) == 0):
		print """Sorry, you must create a ~/.advodiaryrc file, to contain your username"""
		print """and password, like, 'username = "bob"' and 'password = "bob"'"""
		sys.exit()

	try:
		session = advodiary(username)
		getauthorization(session)

# decide what diary entry to edit
		if entrynumberopt:
			if entrynumberopt == "last":
				diarylength = session.len()
				debuginfo(3, "diary length is %d" % (diarylength))
				entrynumber = int(diarylength) - 1   # zero-based array
			else:
				try:
					entrynumber = int(entrynumberopt)
					if entrynumber < -1:
						raise ValueError
				except ValueError:
					usage()
					print "Valid diaries are non-negative numbers or the word 'last'.  You gave '%s'" % (entrynumberopt)
					sys.exit()
		else:
			entrynumber = -1


# assume it's empty.  fill it if we must.
		diaryentry = ""

# if we're told to read from a file or "stdin", then do it
		if sourcefile:
			if (sourcefile == "-") or (sourcefile == "stdin"):
				debuginfo(1, "reading from stdin")
				diaryentry = sys.stdin.read()
			else:
				debuginfo(1, "reading from %s" % (sourcefile))
				diaryentry = open(sourcefile, "r").read()

		if (entrynumber != -1) and not(sourcefile):
			debuginfo(1, "getting entry #%d" % (entrynumber))
			diaryentry = session.get(entrynumber)
			debuginfo(6, "==%s==\n" % (diaryentry))

		debuginfo(3, "using entry #%d" % (entrynumber))

		try:
# remove any CRs from the text, to be Unixy
			diaryentry = re.sub("\r", "", diaryentry)

			try:
# don't edit if we read straight from a file/stream
				if not(sourcefile):
# write out target file
					tempdiaryfilename = tempfile.mktemp('.html')
					tempdiaryfile = open(tempdiaryfilename, 'w')
					tempdiaryfile.write("%s\n%s" % (comment, diaryentry))
					tempdiaryfile.close()

# let the user edit it
					debuginfo(1, "editing entry using %s" % (editor))
					os.system('%s %s' % (editor, tempdiaryfilename))

# read the file back in
					tempdiaryfile = open(tempdiaryfilename, 'r')
					dirtyentry = tempdiaryfile.read()
					tempdiaryfile.close()
					os.unlink(tempdiaryfilename)

# clear out our comment
					newentry = re.sub("%s\n*" % (re.escape(comment)), "", dirtyentry)

# abort if we haven't changed anything
					if newentry == diaryentry:
						raise noChangeException

					diaryentry = newentry

					if len(diaryentry) < 20:
						raise tooShortException

				if debuglevel > 5: 
# don't actually save if we're debugging
					debuginfo(6, "would post (but debuglevel is too high):--%s--\n" % (diaryentry))
				else:
					debuginfo(1, "saving entry #%d" % (entrynumber))
					session.set(diaryentry, entrynumber)


			except noChangeException:
				raise
			except:
# save the file if there's an unhandled exception
				savediaryfile = os.path.join(os.environ['HOME'], "advodiary.save-%d" % (os.getpid()))
				savefd = open(savediaryfile, 'w')
				savefd.write(diaryentry)
				savefd.close()
				print "Exception!  Diary entry saved at '%s'." % (savediaryfile)
				print "Use the '-s %s' option to try sending it again." % (savediaryfile)
				raise

		except tooShortException:
			print "Entry not long enough!  Assuming it's a mistake."

		except noChangeException:
			print "Entry didn't change, so no need to update."


	except xmlrpclib.Fault, mesg:
		print "There was an error talking to Advogato: %s" % (mesg)
# assume the auth cookie is no good.
		if authcachefile and os.path.isfile(authcachefile):
			os.unlink(authcachefile)
		sys.exit()
	except KeyboardInterrupt:
		debuginfo(1, "interrupt")
		sys.exit()


# vim:set tabstop=4 shiftwidth=4 list listchars=tab\:>-:
