Redirect output to log files.
[feedingit] / src / FeedingIt-Web.py
1 import BaseHTTPServer
2 import sys
3 from rss_sqlite import Listing
4 from xml import sax
5 from cgi import escape
6 from re import sub
7 from htmlentitydefs import name2codepoint
8 from gconf import client_get_default
9 from urllib2 import ProxyHandler
10 from threading import Thread
11 from os.path import isfile, isdir, exists
12 from os import mkdir, remove, stat, environ
13
14 import debugging
15 debugging.init(dot_directory=".feedingit", program_name="feedingit-web")
16
17 CONFIGDIR = environ.get("HOME", "/home/user") + "/.feedingit"
18 #CONFIGDIR = "/home/user/.feedingit/"
19
20 updatingFeeds = []
21 #commands = [("addFeed","httpwww"), ("openFeed", "xxxx"), ("openArticle", ("feedid","artid"))]
22 commands = []
23
24 def unescape(text):
25     def fixup(m):
26         text = m.group(0)
27         if text[:2] == "&#":
28             # character reference
29             try:
30                 if text[:3] == "&#x":
31                     return unichr(int(text[3:-1], 16))
32                 else:
33                     return unichr(int(text[2:-1]))
34             except ValueError:
35                 pass
36         else:
37             # named entity
38             try:
39                 text = unichr(name2codepoint[text[1:-1]])
40             except KeyError:
41                 pass
42         return text # leave as is
43     return sub("&#?\w+;", fixup, text)
44
45 def sanitize(text):
46     from cgi import escape
47     return escape(text).encode('ascii', 'xmlcharrefreplace')
48
49 def start_server():
50     global listing
51     listing = Listing(config, CONFIGDIR)
52     httpd = BaseHTTPServer.HTTPServer(("127.0.0.1", PORT), Handler)
53     httpd.serve_forever()
54
55 class App():
56     def addFeed(self, url):
57         commands.append(("addFeed",url))
58     
59     def getStatus(self):
60         pass
61     
62     def openFeed(self, key):
63         commands.append( ("openFeed", key) )
64     
65     def openArticle(self, key, id):
66         commands.append( ("openArticle", key, id) )
67         
68     def automaticUpdate(self):
69         commands.append(("updateAll",))
70 #        for cat in listing.getListOfCategories():
71 #            for feed in listing.getSortedListOfKeys("Manual", category=cat):
72 #                feeds.append(feed)
73 #        download = Download(listing, feeds)
74 #        download.start()
75     
76 class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
77     def updateAll(self):
78         for cat in listing.getListOfCategories():
79             for feed in listing.getSortedListOfKeys("Manual", category=cat):
80                 listing.updateFeed(feed)
81     
82     def openTaskSwitch(self):
83         import subprocess
84         subprocess.Popen("dbus-send /com/nokia/hildon_desktop com.nokia.hildon_desktop.exit_app_view", shell=True)
85     
86     def getCommands(self):
87         commandXml = "<commands>"
88         for item in commands:
89             if item[0]=="addFeed":
90                 commandXml += "<command c='addFeed'>%s</command>" %(sanitize(item[1]))
91             if item[0]=="openFeed":
92                 key = item[1]
93                 cat = str(listing.getFeedCategory(key))
94                 commandXml += "<command c='openFeed' cat='%s'>%s</command>" % (sanitize(cat), sanitize(key) )
95             if item[0]=="openArticle":
96                 key = item[1]
97                 cat = str(listing.getFeedCategory(key))
98                 articleid = item[2]
99                 commandXml += "<command c='openArticle' cat='%s' key='%s'>%s</command>" %(sanitize(cat), sanitize(key), sanitize(articleid) )
100             if item[0]=="updateAll":
101                 self.updateAll()
102             commands.remove(item)
103         commandXml += "</commands>"
104         return commandXml
105     
106     def getConfigXml(self):
107         xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
108         xml += "<hideReadFeed>True</hideReadFeed>"
109         xml += "<hideReadArticles>True</hideReadArticles>"
110         xml += "</xml>"
111         return xml
112     
113     def generateCategoryXml(self):
114         xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
115         for cat in listing.getListOfCategories():
116             xml += "<category>"
117             xml += "<catname>%s</catname>" %sanitize(listing.getCategoryTitle(cat))
118             xml += "<catid>%s</catid>" % cat
119             xml += "</category>"
120         xml += "</xml>"
121         return xml
122
123     def fix_title(self, title):
124         return escape(unescape(title).replace("<em>","").replace("</em>","").replace("<nobr>","").replace("</nobr>","").replace("<wbr>","").replace("&mdash;","-"))
125     
126     def generateFeedsXml(self, catid):
127         xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
128         for key in listing.getSortedListOfKeys("Manual", category=catid):
129             xml += "<feed>"
130             xml += "<feedname>%s</feedname>" %sanitize(listing.getFeedTitle(key))
131             xml += "<feedid>%s</feedid>" %key
132             xml += "<unread>%s</unread>" %listing.getFeedNumberOfUnreadItems(key)
133             xml += "<updatedDate>%s</updatedDate>" %listing.getFeedUpdateTime(key)
134             xml += "<icon>%s</icon>" %listing.getFavicon(key)
135             if key in updatingFeeds:
136                 xml += "<updating>True</updating>"
137             else:
138                 xml += "<updating>False</updating>"
139             xml += "</feed>"
140         xml += "</xml>"
141         return xml
142     
143     def generateArticlesXml(self, key, onlyUnread, markAllAsRead):
144         feed = listing.getFeed(key)
145         if markAllAsRead=="True":
146             feed.markAllAsRead()
147             listing.updateUnread(key)
148             updateDbusHandler.ArticleCountUpdated()
149         xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
150         if onlyUnread == "False":
151             onlyUnread = False
152         for id in feed.getIds(onlyUnread):
153             xml += "<article>"
154             xml += "<title>%s</title>" %self.fix_title(feed.getTitle(id))
155             xml += "<articleid>%s</articleid>" %id
156             xml += "<unread>%s</unread>" %str(feed.isEntryRead(id))
157             xml += "<updatedDate>%s</updatedDate>" %feed.getDateStamp(id)
158             xml += "<path>%s</path>" %feed.getContentLink(id)
159             xml += "</article>"
160         xml += "</xml>"
161         return xml
162
163     def do_GET(self):
164         (req, sep, arg) = self.path.partition("?")
165         request = req.split("/")
166         arguments = {}
167         if arg != "":
168             args = arg.split("&")
169             for arg in args:
170                 ele = arg.split("=")
171                 arguments[ele[0]] = ele[1]
172         if request[1] == "categories":
173             xml = self.generateCategoryXml()
174         elif request[1] == "feeds":
175             catid = request[2]
176             xml = self.generateFeedsXml(catid)
177         elif request[1] == "articles":
178             key = request[2]
179             onlyUnread = arguments.get("onlyUnread","False")
180             markAllAsRead = arguments.get("markAllAsRead", "False")
181             xml = self.generateArticlesXml(key, onlyUnread, markAllAsRead)
182         elif request[1] == "html":
183             key = request[2]
184             article = request[3]
185             feed = listing.getFeed(key)
186             try:
187                 file = open(feed.getContentLink(article))
188                 html = file.read().replace("body", "body bgcolor='#ffffff'", 1)
189                 file.close()
190             except:
191                 html = "<html><body>Error retrieving article</body></html>"
192             self.send_response(200)
193             self.send_header("Content-type", "text/html")
194             self.end_headers()
195             self.wfile.write(html)
196             #listing.updateUnread(key)
197             return
198         elif request[1] == "isUpdating":
199             xml = "<xml>"
200             key = request[2]
201             if (key in updatingFeeds) or ((key=="") and (len(updatingFeeds)>0)):
202                 xml += "<updating>True</updating>"
203             else:
204                 xml += "<updating>False</updating>"
205             xml += self.getCommands()
206             xml += "</xml>"
207         elif request[1] == "read":
208             key = request[2]
209             article = request[3]
210             feed = listing.getFeed(key)
211             feed.setEntryRead(article)
212             listing.updateUnread(key)
213             updateDbusHandler.ArticleCountUpdated()
214             self.send_response(200)
215             self.send_header("Content-type", "text/html")
216             self.end_headers()
217             self.wfile.write("OK")
218             return
219         elif request[1] == "config":
220             xml = self.getConfigXml()
221         elif request[1] == "home":
222             file = open(self.path)
223             self.send_response(200)
224             self.send_header("Content-type", "text/html")
225             self.end_headers()
226             self.wfile.write(file.read())
227             file.close()
228             return
229         elif request[1] == "task":
230             self.openTaskSwitch()
231             xml = "<xml>OK</xml>"
232         elif request[1] == "deleteCat":
233             key = request[2]
234             listing.removeCategory(key)
235             xml = "<xml>OK</xml>"
236         elif request[1] == "deleteFeed":
237             key = request[3]
238             listing.removeFeed(key)
239             xml = "<xml>OK</xml>"
240         elif request[1] == "addFeed":
241             cat = request[2]
242             name = request[3]
243             url = arguments.get("url","")
244             listing.addFeed(name, url, category=cat)
245             xml = "<xml>OK</xml>"
246         elif request[1] == "updateFeed":
247             key = request[2]
248             download = Download(listing, [key,])
249             download.start()
250             xml = "<xml>OK</xml>"
251         elif request[1]=="updateAll":
252             #app.automaticUpdate()
253             self.updateAll()
254             updateDbusHandler.ArticleCountUpdated()
255             xml = "<xml>OK</xml>"
256         elif request[1] == "addCat":
257             catName = request[2]
258             listing.addCategory(catName)
259             xml = "<xml>OK</xml>"
260         else:
261             self.send_error(404, "File not found")
262             return
263         self.send_response(200)
264         self.send_header("Content-type", "text/xml")
265         self.end_headers()
266         self.wfile.write(xml.encode("utf-8"))
267
268 PORT = 8000
269
270 if not isdir(CONFIGDIR):
271     try:
272         mkdir(CONFIGDIR)
273     except:
274         print "Error: Can't create configuration directory"
275         from sys import exit
276         exit(1)
277
278 from config import Config
279 config = Config(None,CONFIGDIR+"config.ini")
280
281 import thread
282
283
284
285 # Initialize the glib mainloop, for dbus purposes
286 from feedingitdbus import ServerObject
287 from updatedbus import UpdateServerObject, get_lock
288
289 import gobject
290 gobject.threads_init()
291 import dbus.mainloop.glib
292 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
293 import mainthread
294 mainthread.init()
295 from jobmanager import JobManager
296 JobManager(True)
297
298 global updateDbusHandler, dbusHandler
299 app = App()
300 dbusHandler = ServerObject(app)
301 updateDbusHandler = UpdateServerObject(app)
302
303 # Start the HTTP server in a new thread
304 thread.start_new_thread(start_server, ())
305
306 mainloop = gobject.MainLoop()
307 mainloop.run()