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/>.
23 # - Setup some option parsing so the daemon can take optional command line
35 from signal import signal, SIGTERM
38 import cStringIO as StringIO
44 # ============================================================================
45 # ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
46 # ============================================================================
48 # Location of the uzbl cache directory.
49 if 'XDG_CACHE_HOME' in os.environ.keys() and os.environ['XDG_CACHE_HOME']:
50 cache_dir = os.path.join(os.environ['XDG_CACHE_HOME'], 'uzbl/')
53 cache_dir = os.path.join(os.environ['HOME'], '.cache/uzbl/')
55 # Location of the uzbl data directory.
56 if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']:
57 data_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'uzbl/')
60 data_dir = os.path.join(os.environ['HOME'], '.local/share/uzbl/')
62 # Create cache dir and data dir if they are missing.
63 for path in [data_dir, cache_dir]:
64 if not os.path.exists(path):
68 cookie_socket = os.path.join(cache_dir, 'cookie_daemon_socket')
69 cookie_jar = os.path.join(data_dir, 'cookies.txt')
71 # Time out after x seconds of inactivity (set to 0 for never time out).
72 # Set to 0 by default until talk_to_socket is doing the spawning.
75 # Enable/disable daemonizing the process (useful when debugging).
76 # Set to False by default until talk_to_socket is doing the spawning.
79 # ============================================================================
80 # ::: End of configuration section :::::::::::::::::::::::::::::::::::::::::::
81 # ============================================================================
85 '''The uzbl cookie daemon class.'''
87 def __init__(self, cookie_socket, cookie_jar, daemon_timeout,\
90 self.cookie_socket = os.path.expandvars(cookie_socket)
91 self.server_socket = None
92 self.cookie_jar = os.path.expandvars(cookie_jar)
93 self.daemon_mode = daemon_mode
95 self.daemon_timeout = daemon_timeout
96 self.last_request = time.time()
100 '''Start the daemon.'''
106 # Cleanup & quit function
107 atexit.register(self.quit)
109 # Tell SIGTERM to act orderly.
110 signal(SIGTERM, lambda signum, stack_frame: sys.exit(1))
113 # Create cookie_socket
117 self.open_cookie_jar()
119 # Listen for GET and PULL cookie requests.
122 except KeyboardInterrupt:
133 def daemonize(function):
134 '''Daemonize the process using the Stevens' double-fork magic.'''
137 if os.fork(): os._exit(0)
140 sys.stderr.write("fork #1 failed: %s\n" % e)
148 if os.fork(): os._exit(0)
151 sys.stderr.write("fork #2 failed: %s\n" % e)
157 devnull = '/dev/null'
158 stdin = file(devnull, 'r')
159 stdout = file(devnull, 'a+')
160 stderr = file(devnull, 'a+', 0)
162 os.dup2(stdin.fileno(), sys.stdin.fileno())
163 os.dup2(stdout.fileno(), sys.stdout.fileno())
164 os.dup2(stderr.fileno(), sys.stderr.fileno())
167 def open_cookie_jar(self):
168 '''Open the cookie jar.'''
171 self.jar = cookielib.MozillaCookieJar(cookie_jar)
173 self.jar.load(ignore_discard=True)
179 def create_socket(self):
180 '''Open socket AF_UNIX socket for uzbl instance <-> daemon
183 if os.path.exists(self.cookie_socket):
184 # Don't you just love racetrack conditions!
187 self.server_socket = socket.socket(socket.AF_UNIX,\
188 socket.SOCK_SEQPACKET)
190 self.server_socket.bind(self.cookie_socket)
192 # Only allow the current user to read and write to the socket.
193 os.chmod(self.cookie_socket, 0600)
197 '''Listen for incoming cookie PUT and GET requests.'''
200 # If you get broken pipe errors increase this listen number.
201 self.server_socket.listen(1)
203 if bool(select.select([self.server_socket],[],[],1)[0]):
204 client_socket, _ = self.server_socket.accept()
205 self.handle_request(client_socket)
206 self.last_request = time.time()
208 if self.daemon_timeout:
209 idle = time.time() - self.last_request
210 if idle > self.daemon_timeout: break
213 def handle_request(self, client_socket):
214 '''Connection made, now to serve a cookie PUT or GET request.'''
216 # Receive cookie request from client.
217 data = client_socket.recv(8192)
218 argv = data.split("\0")
221 print ' '.join(argv[:4])
225 uri = urllib2.urlparse.ParseResult(
231 fragment='').geturl()
233 req = urllib2.Request(uri)
236 self.jar.add_cookie_header(req)
237 if req.has_header('Cookie'):
238 cookie = req.get_header('Cookie')
239 client_socket.send(cookie)
243 client_socket.send("\0")
245 elif action == "PUT":
253 hdr = urllib2.httplib.HTTPMessage(\
254 StringIO.StringIO('Set-Cookie: %s' % set_cookie))
255 res = urllib2.addinfourl(StringIO.StringIO(), hdr,\
257 self.jar.extract_cookies(res,req)
258 self.jar.save(ignore_discard=True)
262 client_socket.close()
265 def quit(self, *args):
266 '''Called on exit to make sure all loose ends are tied up.'''
268 # Only one loose end so far.
274 def del_socket(self):
275 '''Remove the cookie_socket file on exit. In a way the cookie_socket
276 is the daemons pid file equivalent.'''
278 if self.server_socket:
279 self.server_socket.close()
281 if os.path.exists(self.cookie_socket):
282 os.remove(self.cookie_socket)
285 if __name__ == "__main__":
287 if os.path.exists(cookie_socket):
288 print "Error: cookie socket already exists: %r" % cookie_socket
291 CookieMonster(cookie_socket, cookie_jar, daemon_timeout,\