#!/usr/bin/env python

# atomblogger.py

import httplib
import base64

from xml.sax import saxutils
from xml.sax import make_parser
from xml.sax import parseString
from xml.sax.handler import feature_namespaces

from xml.sax import ContentHandler
import string

def normalize_whitespace(text):
	"Remove redundantwhitespace from a string"
	return ' '.join(text.split())

class FindBlogs(ContentHandler):
	def __init__(self, search_rel):
		self.search_rel = search_rel
		self.blogIDs = []
		print self.blogIDs
	
	def error(self, exception):
		import sys
		sys.stderr.write("%s\n" % exception)
		
	def startElement(self, name, attrs):
		if name == 'link':
			rel = normalize_whitespace(attrs.get('rel', ""))
			if rel == self.search_rel:
				href = normalize_whitespace(attrs.get('href', ""))
				blogID = href[(href.find('/atom/') + 6):]
				self.blogIDs.append(blogID)

class AtomPost:
	def __init__(self):
		self.serviceEditURL = ''
		self.postID = ''
		self.idString = ''
		self.authorName = ''
		self.issued = ''
		self.modified = ''
		self.created = ''
		self.location = ''
		self.title = ''
		self.content = ''
		self.draft = ''

class AtomBlog:
	def __init__(self):
		self.posts = []
		self.serviceFeed = ''
		self.serviceFeedTitle = ''
		self.servicePost = ''
		self.servicePostTitle = ''
		self.title = ''
		self.tagline = ''
		self.location = ''
		self.modified = ''
		self.generatorURL = ''
		self.generatorName = ''
		self.info = ''
		self.convertLineBreaks = ''
		

class RecentPostsHandler(ContentHandler):
	def __init__(self):
		self.blog = AtomBlog()
		self.inFeed = False
		self.inEntryContent = False
		self.inBlogTitleContent = False
		self.inTaglineContent = False
		self.inModifiedContent = False
		self.inGeneratorContent = False
		self.inInfoContent = False
		self.inConvertLineBreaks = False
		
		self.inPostAuthor = False
		self.inPostAuthorName = False
		self.inPostIssued = False
		self.inPostModified = False
		self.inPostCreated = False
		self.inPostTitle = False
		self.inPostContent = False
		self.inPostDraft = False
		self.inPostID = False
		
		self.currentPostIndex = -1
		
		self.leftoverTags = []
	
	def startElement(self, name, attrs):
		if self.inFeed:
			# only parse if we're inside the feed
			if self.inEntryContent:
				# do this stuff if we're in the entry content
				if self.currentPostIndex > -1:
					# only parse if we have a post to add to
					
					if name == 'link':
						rel = normalize_whitespace(attrs.get('rel', ""))
						href = normalize_whitespace(attrs.get('href', ""))
						if rel == 'alternate':
							self.currentPost.location = href
						elif rel == 'service.edit':
							self.currentPost.serviceEditURL = href
							temp = href[(href.find('/atom/') + 6):]
							postID = temp[(temp.find('/') + 1):]
							self.currentPost.postID = postID
					
					elif name == 'author':
						self.inPostAuthor = True
					
					elif name == 'name':
						if self.inPostAuthor:
							self.inPostAuthorName = True
					
					elif name == 'issued':
						self.inPostIssued = True
					
					elif name == 'modified':
						self.inPostModified = True
					
					elif name == 'created':
						self.inPostCreated = True
					
					elif name == 'title':
						self.inPostTitle = True
					
					elif name == 'content':
						self.inPostContent = True
					
					elif name == 'draft':
						self.inPostDraft = True
					
					elif name == 'id':
						self.inPostID = True
					
					else:
						self.leftoverTags.append("(entry)/" + name)
					
				else:
					print "error! no post!"
			else:
				# do this stuff otherwise
				if name == 'link':
					rel = normalize_whitespace(attrs.get('rel', ""))
					href = normalize_whitespace(attrs.get('href', ""))
					title = normalize_whitespace(attrs.get('title', ""))
					if rel == 'service.feed':
						self.blog.serviceFeed = href
						self.blog.serviceFeedTitle = title
					elif rel == 'service.post':
						self.blog.servicePost = href
						self.blog.servicePostTitle = title
					
					elif rel == 'alternate':
						self.blog.location = href
				
				elif name == 'title':
					self.inBlogTitleContent = True
				
				elif name == 'tagline':
					self.inTaglineContent = True
				
				elif name == 'modified':
					self.inModifiedContent = True
				
				elif name == 'generator':
					self.inGeneratorContent = True
					self.blog.generatorURL = normalize_whitespace(attrs.get('url', ""))
				
				elif name == 'info':
					self.inInfoContent = True
				
				elif name == 'convertLineBreaks':
					self.inConvertLineBreaks = True
				
				elif name == 'entry':
					self.inEntryContent = True
					self.currentPostIndex = self.currentPostIndex + 1
					self.blog.posts.append(AtomPost()) # create a new post
					self.currentPost = self.blog.posts[self.currentPostIndex]
				
				else:
					self.leftoverTags.append(name)
				
			# end if ( inEntryContent )
		if name == 'feed':
			# now we're in a feed, so parsing is OK
			self.inFeed = True
	
	def characters(self, ch):
		if self.inBlogTitleContent:
			self.blog.title = self.blog.title + ch
			
		elif self.inTaglineContent:
			self.blog.tagline = self.blog.tagline + ch
			
		elif self.inModifiedContent:
			self.blog.modified = self.blog.modified + ch
			
		elif self.inGeneratorContent:
			self.blog.generatorName = self.blog.generatorName + ch
			
		elif self.inInfoContent:
			self.blog.info = self.blog.info + ch
			
		elif self.inConvertLineBreaks:
			self.blog.convertLineBreaks = self.blog.convertLineBreaks + ch
			
		elif self.inPostAuthor:
			if self.inPostAuthorName:
				self.currentPost.authorName = self.currentPost.authorName + ch
		
		elif self.inPostIssued:
			self.currentPost.issued = self.currentPost.issued + ch
		
		elif self.inPostModified:
			self.currentPost.modified = self.currentPost.modified + ch
		
		elif self.inPostCreated:
			self.currentPost.created = self.currentPost.created + ch
		
		elif self.inPostTitle:
			self.currentPost.title = self.currentPost.title + ch
		
		elif self.inPostContent:
			self.currentPost.content = self.currentPost.content + ch
		
		elif self.inPostDraft:
			self.currentPost.draft = self.currentPost.draft + ch
		
		elif self.inPostID:
			self.currentPost.idString = self.currentPost.idString + ch
	
	def endElement(self, name):
		if self.inEntryContent:
			# in entry content
			if name == 'author':
				if self.inPostAuthor:
					self.inPostAuthor = False
			
			elif name == 'name':
				if self.inPostAuthorName:
					self.inPostAuthorName = False
			
			elif name == 'issued':
				if self.inPostIssued:
					self.inPostIssued = False
			
			elif name == 'modified':
				if self.inPostModified:
					self.inPostModified = False
			
			elif name == 'created':
				if self.inPostCreated:
					self.inPostCreated = False
			
			elif name == 'title':
				if self.inPostTitle:
					self.inPostTitle = False
			
			elif name == 'content':
				if self.inPostContent:
					self.inPostContent = False
			
			elif name == 'draft':
				if self.inPostDraft:
					self.inPostDraft = False
			
			elif name == 'id':
				if self.inPostID:
					self.inPostID = False
			
		else:
			if name == 'feed':
				self.inFeed = False
			elif name == 'title':
				if self.inBlogTitleContent:
					self.inBlogTitleContent = False
				elif self.inPostTitleContent:
					self.inPostTitleContent = False
			elif name == 'tagline':
				if self.inTaglineContent:
					self.inTaglineContent = False
			elif name == 'modified':
				if self.inModifiedContent:
					self.inModifiedContent = False
			elif name == 'generator':
				if self.inGeneratorContent:
					self.inGeneratorContent = False
			elif name == 'info':
				if self.inInfoContent:
					self.inInfoContent = False
			elif name == 'convertLineBreaks':
				if self.inConvertLineBreaks:
					self.inConvertLineBreaks = False
			elif name == 'entry':
				if self.inEntryContent:
					self.inEntryContent = False
		
class PostHandler:
	def __init__(self):
		self.inAuthor = False
		self.inAuthorName = False
		self.inIssued = False
		self.inModified = False
		self.inCreated = False
		self.inTitle = False
		self.inContent = False
		self.inDraft = False
	
	#def startElement(self, name, attrs):
		
	
	#def characters(self, ch):
	
	#def endElement(self, name):
		

class BloggerConnection:
	def __init__(self, host, port = 443):
		self.hostname = host
		self.serverport = port
	
	def getb64string(self, username, password):
		b64string = base64.encodestring('%s:%s' % (username, password))[:-1]
		return b64string
	
	def createAuthHeader(self, base64string):
		return "BASIC %s" % base64string
	
	def connect(self, name, passwd):
		self.connection = httplib.HTTPSConnection(self.hostname, self.serverport)
		self.authHeader = self.createAuthHeader(self.getb64string(name, passwd))
	
	def getData(self, selector):
		headers = {"Authorization": self.authHeader}
		self.connection.request("GET", selector, "", headers)
		self.lastResponse = self.connection.getresponse()
		return self.lastResponse.read()
	
	def getBlogListXML(self):
		return self.getData("/atom")
	
	def getRecentPostsXML(self, blogID):
		return self.getData("/atom/%s" % blogID)
	
	def getPostXML(self, blogID, postID):
		return self.getData("/atom/%s/%s" % (blogID, postID))
	
	def sendNewEntryXML(self, blogID, xmlbody):
		headers = {"Content-type": "application/xml", "Authorization": self.authHeader}
		self.connection.request("POST", "/atom/%s" % blogID, xmlbody)
		self.lastResponse = self.connection.getresponse()
		return self.lastResponse.read()
	
	def editEntryXML(self, blogID, postID, xmlbody):
		headers = {"Content-type": "application/xml", "Authorization": self.authHeader}
		self.connection.request("PUT", "/atom/%s/%s" % (blogID, postID), xmlbody)
		self.lastResponse = self.connection.getresponse()
		return self.lastResponse.read()
	
	def deletePost(self, blogID, postID):
		headers = {"Authorization": self.authHeader}
		self.connection.request("DELETE", "/atom/%s/%s" % (blogID, postID))
		self.lastResponse = self.connection.getResponse()
		
		# for testing purposes
		return self.lastResponse.read()
	
	def closeConnection(self):
		self.connection.close()
