3 # Uzbl tabbing wrapper using a fifo socket interface
4 # Copyright (c) 2009, Tom Adams <tom@holizz.com>
5 # Copyright (c) 2009, Dieter Plaetinck <dieter AT plaetinck.be>
6 # Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
31 import cStringIO as StringIO
37 # ============================================================================
38 # ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
39 # ============================================================================
41 # Location of the uzbl cache directory.
42 if 'XDG_CACHE_HOME' in os.environ.keys() and os.environ['XDG_CACHE_HOME']:
43 cache_dir = os.path.join(os.environ['XDG_CACHE_HOME'], 'uzbl/')
46 cache_dir = os.path.join(os.environ['HOME'], '.cache/uzbl/')
48 # Location of the uzbl data directory.
49 if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']:
50 data_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'uzbl/')
53 data_dir = os.path.join(os.environ['HOME'], '.local/share/uzbl/')
55 # Create cache dir and data dir if they are missing.
56 for path in [data_dir, cache_dir]:
57 if not os.path.exists(path):
61 cookie_socket = os.path.join(cache_dir, 'cookie_daemon_socket')
62 cookie_jar = os.path.join(data_dir, 'cookies.txt')
66 # ============================================================================
67 # ::: End of configuration section :::::::::::::::::::::::::::::::::::::::::::
68 # ============================================================================
72 '''The uzbl cookie daemon class.'''
74 def __init__(self, cookie_socket, cookie_jar, daemon_timeout):
76 self.cookie_socket = os.path.expandvars(cookie_socket)
77 self.server_socket = None
78 self.cookie_jar = os.path.expandvars(cookie_jar)
80 self.daemon_timeout = daemon_timeout
81 self.last_request = time.time()
85 '''Start the daemon.'''
91 # Create cookie_socket
95 self.open_cookie_jar()
97 # Listen for GET and PULL cookie requests.
102 print "%r" % sys.exc_info()[1]
107 def daemonize(function):
108 '''Daemonize the process using the Stevens' double-fork magic.'''
111 if os.fork(): sys.exit(0)
114 sys.stderr.write("fork #1 failed: %s\n" % e)
122 if os.fork(): sys.exit(0)
125 sys.stderr.write("fork #2 failed: %s\n" % e)
131 devnull = '/dev/null'
132 stdin = file(devnull, 'r')
133 stdout = file(devnull, 'a+')
134 stderr = file(devnull, 'a+', 0)
136 os.dup2(stdin.fileno(), sys.stdin.fileno())
137 os.dup2(stdout.fileno(), sys.stdout.fileno())
138 os.dup2(stderr.fileno(), sys.stderr.fileno())
141 def open_cookie_jar(self):
142 '''Open the cookie jar.'''
145 self.jar = cookielib.MozillaCookieJar(cookie_jar)
147 self.jar.load(ignore_discard=True)
153 def create_socket(self):
154 '''Open socket AF_UNIX socket for uzbl instance <-> daemon
157 if os.path.exists(self.cookie_socket):
158 # Don't you just love racetrack conditions!
161 self.server_socket = socket.socket(socket.AF_UNIX,\
162 socket.SOCK_SEQPACKET)
164 self.server_socket.bind(self.cookie_socket)
168 '''Listen for incoming cookie PUT and GET requests.'''
171 # If you get broken pipe errors increase this listen number.
172 self.server_socket.listen(1)
174 if bool(select.select([self.server_socket],[],[],1)[0]):
175 client_socket, _ = self.server_socket.accept()
176 self.handle_request(client_socket)
177 self.last_request = time.time()
179 if self.daemon_timeout:
180 idle = time.time() - self.last_request
181 if idle > self.daemon_timeout: break
184 def handle_request(self, client_socket):
185 '''Connection made, now to serve a cookie PUT or GET request.'''
187 # Receive full request from client.
188 data = client_socket.recv(4096)
190 argv = data.split("\0")
196 set_cookie = argv[4] if len(argv) > 3 else None
197 uri = urllib2.urlparse.ParseResult(
203 fragment='').geturl()
205 req = urllib2.Request(uri)
208 self.jar.add_cookie_header(req)
209 if req.has_header('Cookie'):
210 client_socket.send(req.get_header('Cookie'))
213 client_socket.send("\0")
215 elif action == "PUT":
216 hdr = urllib2.httplib.HTTPMessage(\
217 StringIO.StringIO('Set-Cookie: %s' % set_cookie))
218 res = urllib2.addinfourl(StringIO.StringIO(), hdr,\
220 self.jar.extract_cookies(res,req)
221 self.jar.save(ignore_discard=True)
223 client_socket.close()
227 '''Called on exit to make sure all loose ends are tied up.'''
229 # Only one loose end so far.
232 # And die gracefully.
236 def del_socket(self):
237 '''Remove the cookie_socket file on exit. In a way the cookie_socket
238 is the daemons pid file equivalent.'''
240 if self.server_socket:
241 self.server_socket.close()
243 if os.path.exists(self.cookie_socket):
244 os.remove(self.cookie_socket)
247 if __name__ == "__main__":
249 if os.path.exists(cookie_socket):
250 print "Error: cookie socket already exists: %r" % cookie_socket
253 CookieMonster(cookie_socket, cookie_jar, daemon_timeout).run()