def api_log(self, network, user, channel, req, qs, posted): # FIXME: Choose between bots based on network grep = qs.get('grep', [''])[0] after = qs.get('seen', [None])[0] limit = int(qs.get('limit', [0])[0]) timeout = int(qs.get('timeout', [0])[0]) if timeout: timeout += time.time() bot = self.networks[network] rules = bot.irc_parsed_mode(channel) if (rules.get('secret', False) or rules.get('key', False) or rules.get('invite_only', False)): if not (user and channel in user['channels']): # This will hide the channel key and other parameters for r in rules: if rules[r]: rules[r] = True rules['event'] = 'pleasejoin' return 'application/json', HttpdLite.json_encode([ [get_timed_uid(), rules] ]) data = [] try: while not data: data = bot.irc_channel_log(channel) if after or grep: data = [x for x in data if (x[0] > after) and (not grep or grep in x[1]['nick'].lower() or grep in x[1].get('text', ''))] if timeout and not data: cond = threading.Condition() ev = self.event_loop.add_sleeper(timeout, cond, 'API request') bot.irc_watch_channel(channel, ev) cond.acquire() cond.wait() cond.release() self.event_loop.remove_sleeper(ev) if time.time() >= timeout: break except SelectAborted: pass if limit: data = data[-limit:] return 'application/json', HttpdLite.json_encode(data)
def api_log(self, network, user, channel, req, qs, posted): # FIXME: Choose between bots based on network grep = qs.get('grep', [''])[0] after = qs.get('seen', [None])[0] limit = int(qs.get('limit', [0])[0]) timeout = int(qs.get('timeout', [0])[0]) if timeout: timeout += time.time() bot = self.networks[network] rules = bot.irc_parsed_mode(channel) if (rules.get('secret', False) or rules.get('key', False) or rules.get('invite_only', False)): if not (user and channel in user['channels']): # This will hide the channel key and other parameters for r in rules: if rules[r]: rules[r] = True rules['event'] = 'pleasejoin' return 'application/json', HttpdLite.json_encode( [[get_timed_uid(), rules]]) data = [] try: while not data: data = bot.irc_channel_log(channel) if after or grep: data = [ x for x in data if (x[0] > after) and ( not grep or grep in x[1]['nick'].lower() or grep in x[1].get('text', '')) ] if timeout and not data: cond = threading.Condition() ev = self.event_loop.add_sleeper(timeout, cond, 'API request') bot.irc_watch_channel(channel, ev) cond.acquire() cond.wait() cond.release() self.event_loop.remove_sleeper(ev) if time.time() >= timeout: break except SelectAborted: pass if limit: data = data[-limit:] return 'application/json', HttpdLite.json_encode(data)
def Configuration(): if '--version' in sys.argv: print '%s' % VERSION sys.exit(0) config = { 'work_dir': DEFAULT_PATH, 'http_port': 4950, 'http_host': 'localhost', 'lang': 'en', 'skin': 'default', 'debug': False, 'irc': {}, # These are ignored, but picked up by sockschain 'nossl': None, 'nopyopenssl': None, } # Set work dir before loading config, all other command-line arguments # will override the config file. for arg in sys.argv[1:]: if arg.startswith('--work_dir='): config['work_dir'] = arg.split('=', 1)[1] try: fd = open(os.path.join(config['work_dir'], 'config.json'), 'rb') config.update(HttpdLite.json_decode(fd.read())) except ValueError, e: print 'Failed to parse config: %s' % e sys.exit(1)
def api_logout(self, network, user, channel, req, qs, posted): del self.networks[network].users[user.uid] req.setCookie('muid-%s' % network, '', delete=True) sockfd = self.event_loop.fds_by_uid[user.uid] self.event_loop.sendall(sockfd, 'QUIT :Logged off\r\n') return 'application/json', HttpdLite.json_encode(['ok'])
def handleUserLogin(self, req, page_prefix, path, qs): state = qs.get('state', ['/'])[0] network, channel = self.get_channel_from_path(state[1:]) provider, token = req.auth_info profile = {'source': provider, 'home': 'The Internet'} # First, pull details from their on-line profile, if possible... try: if provider == 'Facebook': fbp = req.server.auth_handler.getFacebookProfile(token) profile.update({ 'name': fbp.get('name'), 'home': fbp.get('hometown', {}).get('name', 'The Internet'), 'uid': 'fb%s' % fbp.get('id', ''), 'pic': 'https://graph.facebook.com/%s/picture' % fbp.get('id', ''), 'url': 'https://www.facebook.com/%s' % fbp.get('username', fbp.get('id', '')) }) elif provider == 'Google': profile = req.server.auth_handler.getGoogleProfile(token) else: print '*** Not sure how to get profiles from %s' % provider except (IOError, OSError): # Network problems...? pass # Create a nick-name for them nickname = profile.get('name', 'Guest %x' % random.randint(0, 10000)) while (len(nickname) > 15 and ' ' in nickname): nickname = nickname.rsplit(' ', 1)[0] profile['nick'] = self.dumb_down(nickname) # Create an IRC client, start the connection. client = IrcClient().irc_profile(profile).irc_channels([channel]) self.networks[network].users[client.uid] = client self.connect_client(network, client) # Finally, set a cookie with their client's UID. req.setCookie('muid-%s' % network, '%s,pending' % client.uid) print 'Logged in: %s' % HttpdLite.json_encode(profile, indent=2) return req.sendRedirect(page_prefix + state)
def handleUserLogin(self, req, page_prefix, path, qs): state = qs.get('state', ['/'])[0] network, channel = self.get_channel_from_path(state[1:]) provider, token = req.auth_info profile = { 'source': provider, 'home': 'The Internet' } # First, pull details from their on-line profile, if possible... try: if provider == 'Facebook': fbp = req.server.auth_handler.getFacebookProfile(token) profile.update({ 'name': fbp.get('name'), 'home': fbp.get('hometown', {}).get('name', 'The Internet'), 'uid': 'fb%s' % fbp.get('id', ''), 'pic': 'https://graph.facebook.com/%s/picture' % fbp.get('id', ''), 'url': 'https://www.facebook.com/%s' % fbp.get('username', fbp.get('id', '')) }) elif provider == 'Google': profile = req.server.auth_handler.getGoogleProfile(token) else: print '*** Not sure how to get profiles from %s' % provider except (IOError, OSError): # Network problems...? pass # Create a nick-name for them nickname = profile.get('name', 'Guest %x' % random.randint(0, 10000)) while (len(nickname) > 15 and ' ' in nickname): nickname = nickname.rsplit(' ', 1)[0] profile['nick'] = self.dumb_down(nickname) # Create an IRC client, start the connection. client = IrcClient().irc_profile(profile).irc_channels([channel]) self.networks[network].users[client.uid] = client self.connect_client(network, client) # Finally, set a cookie with their client's UID. req.setCookie('muid-%s' % network, '%s,pending' % client.uid) print 'Logged in: %s' % HttpdLite.json_encode(profile, indent=2) return req.sendRedirect(page_prefix + state)
def api_say(self, network, user, channel, req, qs, posted): sockfd = self.event_loop.fds_by_uid[user.uid] privmsg = 'PRIVMSG %s :%s\r\n' % (channel, posted['msg'][0].decode('utf-8')) self.event_loop.sendall(sockfd, privmsg.encode('utf-8')) return 'application/json', HttpdLite.json_encode(['ok'])
def handleHttpRequest(self, req, scheme, netloc, path, params, query, frag, qs, posted, cookies, user=None): if path.startswith('/'): path = path[1:] path_url = path path = urllib.unquote(path).decode('utf-8') # Defaults, will probably be overridden by individual response pages headers = [] cachectrl = 'max-age=3600, public' mime_type = 'text/html' code = 200 data = None # Clear any expired cookies, update others, record credentials credentials = {} for c, v in cookies.items(): try: prefix, network = c.split('-', 1) muid = v.value.split(',')[0] if (not network in self.networks or muid not in self.networks[network].users): req.setCookie(c, '', delete=True) else: log_id = v.value.split(',', 1)[1] user = self.networks[network].users[muid] if log_id != user.log_id: req.setCookie(c, '%s,%s' % (muid, user.log_id)) else: user.seen = time.time() credentials[network] = user except (ValueError, KeyError): pass # Shared values for rendering templates page_url = req.absolute_url() page_prefix = '/'.join(page_url.split('/', 4)[:3]) host = req.header('Host', 'unknown').lower() template = '' page = { 'templates': os.path.join(self.work_dir, 'html'), 'version': VERSION, 'skin': host, 'host': host, 'page_path': '/' + path_url, 'page_url': page_url, } # Get the actual content. try: if req.command == 'GET': if path == '': template = self.load_template('index.html', config=page) page.update( {'linked_channel_list': self.renderChannelList()}) elif path.startswith('_api/v1/'): return self.handleApiRequest(req, path, qs, posted) elif path.startswith('join/'): template, page = self.prepareChannelPage( path, page, credentials) elif (path.startswith('_skin/') or path in ('favicon.ico', )): template = self.load_template(path.split('/')[-1], config=page) mime_type = HttpdLite.GuessMimeType(path) if mime_type != 'application/octet-stream' and path.endswith( '.gz'): headers.append(('Content-Encoding', 'gzip')) elif path.startswith('_authlite/') and req.auth_info: return self.handleUserLogin(req, page_prefix, path, qs) elif path == 'robots.txt': # FIXME: Have an explicit search-engine policy in settings? raise NotFoundException('FIXME') else: raise NotFoundException() elif req.command == 'POST': if path.startswith('_api/v1/'): return self.handleApiRequest(req, path, qs, posted) else: raise NotFoundException() except NotFoundException: cachectrl, code, data = 'no-cache', 404, '<h1>404 Not found</h1>\n' if not data: if '__Mutiny_Template__' in template: data = (template.decode('utf-8') % page).encode('utf-8') cachectrl = 'no-cache' else: data = template return req.sendResponse(data, code=code, mimetype=mime_type, header_list=headers, cachectrl=cachectrl)
} if len(sys.argv) > 1: arg = sys.argv.pop(1) config['irc']['irc']['servers'] = [arg.rsplit('/', 1)[0]] config['irc']['irc']['channels'] = channels = {} for channel in arg.rsplit('/', 1)[1].split(',', 1): channels[channel] = { 'description': 'IRC channel', 'access': 'open' } if len(sys.argv) > 1: arg = sys.argv.pop(1) for channel in channels: channels[channel]['access'] = arg print 'Config is: %s' % HttpdLite.json_encode(config, indent=2) return config if __name__ == "__main__": try: mutiny = Mutiny(Configuration()) except (IndexError, ValueError, OSError, IOError): print '%s\n' % traceback.format_exc() print 'Usage: %s <nick> irc://<server:port>/<channel>' % sys.argv[0] print ' %s <nick> ssl://<server:port>/<channel>' % sys.argv[0] print print 'Logs and settings will be stored here: %s' % config['work_dir'] print sys.exit(1) try:
return req.sendResponse(data, code=code, mimetype=mime_type, header_list=headers, cachectrl=cachectrl) if __name__ == "__main__": try: db_path = os.path.expanduser('~/.Unhosted.py') unhosted = Unhosted(db_path) except (IndexError, ValueError, OSError, IOError): print 'Usage: %s' % sys.argv[0] print print 'The file will create an database in: %s' % db_path print sys.exit(1) try: try: print 'This is Unhosted.py, listening on %s:%s' % unhosted.listen_on print 'Fork me on Github: https://github.com/pagekite/plugins-pyUnhosted' print HttpdLite.Server(unhosted.listen_on, unhosted, handler=RequestHandler).serve_forever() except KeyboardInterrupt: unhosted.stop() except: unhosted.stop() raise
'userinfo': 'Mutiny %s' % VERSION } if len(sys.argv) > 1: arg = sys.argv.pop(1) config['irc']['irc']['servers'] = [ arg.rsplit('/', 1)[0] ] config['irc']['irc']['channels'] = channels = {} for channel in arg.rsplit('/', 1)[1].split(',', 1): channels[channel] = {'description': 'IRC channel', 'access': 'open'} if len(sys.argv) > 1: arg = sys.argv.pop(1) for channel in channels: channels[channel]['access'] = arg print 'Config is: %s' % HttpdLite.json_encode(config, indent=2) return config if __name__ == "__main__": try: mutiny = Mutiny(Configuration()) except (IndexError, ValueError, OSError, IOError): print '%s\n' % traceback.format_exc() print 'Usage: %s <nick> irc://<server:port>/<channel>' % sys.argv[0] print ' %s <nick> ssl://<server:port>/<channel>' % sys.argv[0] print print 'Logs and settings will be stored here: %s' % config['work_dir'] print sys.exit(1) try: