<commit_before>"""Understands the floobits protocol"""

import os
import json
import hashlib
import collections
import Queue

from lib import diff_match_patch as dmp

import msg
import shared as G
import utils
import sublime

def buf_populated(func):
    def wrapped(self, data):
        if data.get('id') is None:
            msg.debug('no buf id in data')
            return
        buf = self.FLOO_BUFS.get(data['id'])
        if buf is None or 'buf' not in buf:
            msg.debug('buf is not populated yet')
            return
        func(self, data)
    return wrapped

class BaseProtocol(object):
    BUFS_CHANGED = []
    SELECTION_CHANGED = []
    MODIFIED_EVENTS = Queue.Queue()
    SELECTED_EVENTS = Queue.Queue()
    FLOO_BUFS = {}

    def __init__(self, agent):
        self.agent = agent
        self.perms = []
        self.follow_mode = False
        self.chat_deck = collections.deque(maxlen=10)
        self.ignored_names = ['node_modules']

    def get_view(self, data):
        raise NotImplemented()

    def update_view(self, data):
        raise NotImplemented()

    def get_buf(self, data):
        raise NotImplemented()

    def get_buf_by_path(self, path):
        rel_path = utils.to_rel_path(path)
        for buf_id, buf in self.FLOO_BUFS.iteritems():
            if rel_path == buf['path']:
                return buf
        return None

    def save_buf(self, data):
        raise NotImplemented()

    def chat(self, data):
        raise NotImplemented()

    def maybe_buffer_changed(self):
        raise NotImplemented()

    def maybe_selection_changed(self):
        raise NotImplemented()

    def on_msg(self, data):
        raise NotImplemented()

    def is_shared(self, p):
        if not self.agent.is_ready():
            msg.debug('agent is not ready. %s is not shared' % p)
            return False
        p = utils.unfuck_path(p)
        # TODO: tokenize on path seps and then look for ..
        if utils.to_rel_path(p).find("../") == 0:
            return False
        return True

    def follow(self, follow_mode=None):
        if follow_mode is None:
            follow_mode = not self.follow_mode
        self.follow_mode = follow_mode
        msg.log('follow mode is %s' % {True: 'enabled', False: 'disabled'}[self.follow_mode])

    def create_buf(self, path, force=False):
        if G.SPARSE_MODE and not force:
            msg.debug("Skipping %s because user enabled sparse mode." % path)
            return
        if 'create_buf' not in self.perms:
            msg.error("Skipping %s. You don't have permission to create buffers in this room." % path)
            return
        if not self.is_shared(path):
            msg.error('Skipping adding %s because it is not in shared path %s.' % (path, G.PROJECT_PATH))
            return
        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                # Don't care about hidden stuff
                dirnames[:] = [d for d in dirnames if d[0] != '.']
                for f in filenames:
                    f_path = os.path.join(dirpath, f)
                    if f[0] == '.':
                        msg.log('Not creating buf for hidden file %s' % f_path)
                        continue
                    if f in self.ignored_names:
                        # TODO: prompt instead of being lame
                        msg.log('Not creating buf for ignored file %s' % f_path)
                        continue
                    sublime.set_timeout(self.create_buf, 0, f_path, force)
            return

        if self.get_buf_by_path(path):
            msg.debug('Buf %s already exists in room. Skipping adding.' % path)
            return

        try:
            buf_fd = open(path, 'rb')
            buf = buf_fd.read().decode('utf-8')
            rel_path = utils.to_rel_path(path)
            msg.debug('creating buffer ', rel_path)
            event = {
                'name': 'create_buf',
                'buf': buf,
                'path': rel_path,
            }
            self.agent.put(event)
        except (IOError, OSError):
            msg.error('Failed to open %s.' % path)
        except Exception as e:
            msg.error('Failed to create buffer %s: %s' % (path, str(e)))

    def handle(self, data):
        name = data.get('name')
        if not name:
            return msg.error('no name in data?!?')
        func = getattr(self, "on_%s" % (name), None)
        if not func:
            return msg.debug('unknown name', name, 'data:', data)
        func(data)

    def push(self):
        reported = set()
        while self.BUFS_CHANGED:
            buf_id = self.BUFS_CHANGED.pop()
            view = self.get_view(buf_id)
            buf = view.buf
            if view.is_loading():
                msg.debug('View for buf %s is not ready. Ignoring change event' % buf['id'])
                continue
            if 'patch' not in self.perms:
                continue
            vb_id = view.native_id
            if vb_id in reported:
                continue
            if 'buf' not in buf:
                msg.debug('No data for buf %s %s yet. Skipping sending patch' % (buf['id'], buf['path']))
                continue

            reported.add(vb_id)
            patch = utils.FlooPatch(view)
            # Update the current copy of the buffer
            buf['buf'] = patch.current
            buf['md5'] = hashlib.md5(patch.current.encode('utf-8')).hexdigest()
            self.agent.put(patch.to_json())

        while self.SELECTION_CHANGED:
            view, ping = self.SELECTION_CHANGED.pop()
            # consume highlight events to avoid leak
            if 'highlight' not in self.perms:
                continue
            vb_id = view.native_id
            if vb_id in reported:
                continue

            reported.add(vb_id)
            highlight_json = {
                'id': view.buf['id'],
                'name': 'highlight',
                'ranges': view.get_selections(),
                'ping': ping,
            }
            self.agent.put(highlight_json)

    def on_create_buf(self, data):
        self.on_get_buf(data)

    def on_get_buf(self, data):
        buf_id = data['id']
        self.FLOO_BUFS[buf_id] = data
        view = self.get_view(buf_id)
        if view:
            self.update_view(data, view)
        else:
            self.save_buf(data)

    def on_rename_buf(self, data):
        new = utils.get_full_path(data['path'])
        old = utils.get_full_path(data['old_path'])
        new_dir = os.path.dirname(new)
        if new_dir:
            utils.mkdir(new_dir)
        view = self.get_view(data['id'])
        self.FLOO_BUFS[data['id']]['path'] = data['path']
        if view:
            view.rename(new)
        else:
            os.rename(old, new)

    def on_room_info(self, data):
        # Success! Reset counter
        self.room_info = data
        self.perms = data['perms']

        if 'patch' not in data['perms']:
            msg.log('We don\'t have patch permission. Setting buffers to read-only')

        utils.mkdir(G.PROJECT_PATH)

        floo_json = {
            'url': utils.to_room_url({
                'host': self.agent.host,
                'owner': self.agent.owner,
                'port': self.agent.port,
                'room': self.agent.room,
                'secure': self.agent.secure,
            })
        }
        with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
            floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

        for buf_id, buf in data['bufs'].iteritems():
            buf_id = int(buf_id)  # json keys must be strings
            buf_path = utils.get_full_path(buf['path'])
            new_dir = os.path.dirname(buf_path)
            utils.mkdir(new_dir)
            self.FLOO_BUFS[buf_id] = buf
            try:
                buf_fd = open(buf_path, 'r')
                buf_buf = buf_fd.read().decode('utf-8')
                md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest()
                if md5 == buf['md5']:
                    msg.debug('md5 sums match. not getting buffer')
                    buf['buf'] = buf_buf
                else:
                    raise Exception('different md5')
            except Exception:
                try:
                    open(buf_path, "a").close()
                except Exception as e:
                    msg.debug("couldn't touch file: %s becuase %s" % (buf_path, e))
                self.agent.send_get_buf(buf_id)

        msg.debug(G.PROJECT_PATH)

        self.agent.on_auth()

    def on_join(self, data):
        msg.log('%s joined the room' % data['username'])

    def on_part(self, data):
        msg.log('%s left the room' % data['username'])
        region_key = 'floobits-highlight-%s' % (data['user_id'])
        for window in sublime.windows():
            for view in window.views():
                view.erase_regions(region_key)

    @buf_populated
    def on_patch(self, data):
        buf_id = data['id']
        buf = self.FLOO_BUFS[buf_id]
        view = self.get_view(buf_id)
        DMP = dmp.diff_match_patch()
        if len(data['patch']) == 0:
            msg.error('wtf? no patches to apply. server is being stupid')
            return
        dmp_patches = DMP.patch_fromText(data['patch'])
        # TODO: run this in a separate thread
        if view:
            old_text = view.get_text()
        else:
            old_text = buf.get('buf', '')
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.debug('maybe vim is lame and discarded a trailing newline')
            old_text += '\n'
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.warn('starting md5s don\'t match for %s. ours: %s patch: %s this is dangerous!' %
                    (buf['path'], md5_before, data['md5_before']))

        t = DMP.patch_apply(dmp_patches, old_text)

        clean_patch = True
        for applied_patch in t[1]:
            if not applied_patch:
                clean_patch = False
                break

        if G.DEBUG:
            if len(t[0]) == 0:
                msg.debug('OMG EMPTY!')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])
            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])

        if not clean_patch:
            msg.error('failed to patch %s cleanly. re-fetching buffer' % buf['path'])
            return self.agent.send_get_buf(buf_id)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != data['md5_after']:
            msg.warn(
                '%s new hash %s != expected %s. re-fetching buffer...' %
                (buf['path'], cur_hash, data['md5_after'])
            )
            return self.agent.send_get_buf(buf_id)

        buf['buf'] = t[0]
        buf['md5'] = cur_hash

        if not view:
            self.save_buf(buf)
            return
        view.apply_patches(buf, t)

    def delete_buf(self, path):
        """deletes a path"""

        if not path:
            return

        path = utils.get_full_path(path)

        if not self.is_shared(path):
            msg.error('Skipping deleting %s because it is not in shared path %s.' % (path, G.PROJECT_PATH))
            return

        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                # Don't care about hidden stuff
                dirnames[:] = [d for d in dirnames if d[0] != '.']
                for f in filenames:
                    f_path = os.path.join(dirpath, f)
                    if f[0] == '.':
                        msg.log('Not deleting buf for hidden file %s' % f_path)
                    else:
                        self.delete_buf(f_path)
            return
        buf_to_delete = None
        rel_path = utils.to_rel_path(path)
        for buf_id, buf in self.FLOO_BUFS.items():
            if rel_path == buf['path']:
                buf_to_delete = buf
                break
        if buf_to_delete is None:
            msg.error('%s is not in this room' % path)
            return
        msg.log('deleting buffer ', rel_path)
        event = {
            'name': 'delete_buf',
            'id': buf_to_delete['id'],
        }
        self.agent.put(event)

    @buf_populated
    def on_delete_buf(self, data):
        # TODO: somehow tell the user about this. maybe delete on disk too?
        del self.FLOO_BUFS[data['id']]
        path = utils.get_full_path(data['path'])
        if not G.DELETE_LOCAL_FILES:
            msg.log('Not deleting %s because delete_local_files is disabled' % path)
            return
        utils.rm(path)
        msg.warn('deleted %s because %s told me to.' % (path, data.get('username', 'the internet')))

    @buf_populated
    def on_highlight(self, data):
        #     floobits.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False))
        #buf_id, region_key, username, ranges, ping=False):
        ping = data.get('ping', False)
        if self.follow_mode:
            ping = True
        buf = self.FLOO_BUFS[data['id']]
        view = self.get_view(data['id'])
        if not view:
            if not ping:
                return
            view = self.create_view(buf)
            if not view:
                return
        if ping:
            try:
                offset = data['ranges'][0][0]
            except IndexError as e:
                msg.debug('could not get offset from range %s' % e)
            else:
                msg.log('You have been summoned by %s' % (data.get('username', 'an unknown user')))
                view.focus()
                view.set_cursor_position(offset)
        if G.SHOW_HIGHLIGHTS:
            view.highlight(data['ranges'], data['user_id'])

    def on_error(self, data):
        message = 'Floobits: Error! Message: %s' % str(data.get('msg'))
        msg.error(message)

    def on_disconnect(self, data):
        message = 'Floobits: Disconnected! Reason: %s' % str(data.get('reason'))
        msg.error(message)
        msg.error(message)
        self.agent.stop()
<commit_msg>Remove the newline if we tacked one on and the md5sum still doesn't match.<commit_after>"""Understands the floobits protocol"""

import os
import json
import hashlib
import collections
import Queue

from lib import diff_match_patch as dmp

import msg
import shared as G
import utils
import sublime

def buf_populated(func):
    def wrapped(self, data):
        if data.get('id') is None:
            msg.debug('no buf id in data')
            return
        buf = self.FLOO_BUFS.get(data['id'])
        if buf is None or 'buf' not in buf:
            msg.debug('buf is not populated yet')
            return
        func(self, data)
    return wrapped

class BaseProtocol(object):
    BUFS_CHANGED = []
    SELECTION_CHANGED = []
    MODIFIED_EVENTS = Queue.Queue()
    SELECTED_EVENTS = Queue.Queue()
    FLOO_BUFS = {}

    def __init__(self, agent):
        self.agent = agent
        self.perms = []
        self.follow_mode = False
        self.chat_deck = collections.deque(maxlen=10)
        self.ignored_names = ['node_modules']

    def get_view(self, data):
        raise NotImplemented()

    def update_view(self, data):
        raise NotImplemented()

    def get_buf(self, data):
        raise NotImplemented()

    def get_buf_by_path(self, path):
        rel_path = utils.to_rel_path(path)
        for buf_id, buf in self.FLOO_BUFS.iteritems():
            if rel_path == buf['path']:
                return buf
        return None

    def save_buf(self, data):
        raise NotImplemented()

    def chat(self, data):
        raise NotImplemented()

    def maybe_buffer_changed(self):
        raise NotImplemented()

    def maybe_selection_changed(self):
        raise NotImplemented()

    def on_msg(self, data):
        raise NotImplemented()

    def is_shared(self, p):
        if not self.agent.is_ready():
            msg.debug('agent is not ready. %s is not shared' % p)
            return False
        p = utils.unfuck_path(p)
        # TODO: tokenize on path seps and then look for ..
        if utils.to_rel_path(p).find("../") == 0:
            return False
        return True

    def follow(self, follow_mode=None):
        if follow_mode is None:
            follow_mode = not self.follow_mode
        self.follow_mode = follow_mode
        msg.log('follow mode is %s' % {True: 'enabled', False: 'disabled'}[self.follow_mode])

    def create_buf(self, path, force=False):
        if G.SPARSE_MODE and not force:
            msg.debug("Skipping %s because user enabled sparse mode." % path)
            return
        if 'create_buf' not in self.perms:
            msg.error("Skipping %s. You don't have permission to create buffers in this room." % path)
            return
        if not self.is_shared(path):
            msg.error('Skipping adding %s because it is not in shared path %s.' % (path, G.PROJECT_PATH))
            return
        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                # Don't care about hidden stuff
                dirnames[:] = [d for d in dirnames if d[0] != '.']
                for f in filenames:
                    f_path = os.path.join(dirpath, f)
                    if f[0] == '.':
                        msg.log('Not creating buf for hidden file %s' % f_path)
                        continue
                    if f in self.ignored_names:
                        # TODO: prompt instead of being lame
                        msg.log('Not creating buf for ignored file %s' % f_path)
                        continue
                    sublime.set_timeout(self.create_buf, 0, f_path, force)
            return

        if self.get_buf_by_path(path):
            msg.debug('Buf %s already exists in room. Skipping adding.' % path)
            return

        try:
            buf_fd = open(path, 'rb')
            buf = buf_fd.read().decode('utf-8')
            rel_path = utils.to_rel_path(path)
            msg.debug('creating buffer ', rel_path)
            event = {
                'name': 'create_buf',
                'buf': buf,
                'path': rel_path,
            }
            self.agent.put(event)
        except (IOError, OSError):
            msg.error('Failed to open %s.' % path)
        except Exception as e:
            msg.error('Failed to create buffer %s: %s' % (path, str(e)))

    def handle(self, data):
        name = data.get('name')
        if not name:
            return msg.error('no name in data?!?')
        func = getattr(self, "on_%s" % (name), None)
        if not func:
            return msg.debug('unknown name', name, 'data:', data)
        func(data)

    def push(self):
        reported = set()
        while self.BUFS_CHANGED:
            buf_id = self.BUFS_CHANGED.pop()
            view = self.get_view(buf_id)
            buf = view.buf
            if view.is_loading():
                msg.debug('View for buf %s is not ready. Ignoring change event' % buf['id'])
                continue
            if 'patch' not in self.perms:
                continue
            vb_id = view.native_id
            if vb_id in reported:
                continue
            if 'buf' not in buf:
                msg.debug('No data for buf %s %s yet. Skipping sending patch' % (buf['id'], buf['path']))
                continue

            reported.add(vb_id)
            patch = utils.FlooPatch(view)
            # Update the current copy of the buffer
            buf['buf'] = patch.current
            buf['md5'] = hashlib.md5(patch.current.encode('utf-8')).hexdigest()
            self.agent.put(patch.to_json())

        while self.SELECTION_CHANGED:
            view, ping = self.SELECTION_CHANGED.pop()
            # consume highlight events to avoid leak
            if 'highlight' not in self.perms:
                continue
            vb_id = view.native_id
            if vb_id in reported:
                continue

            reported.add(vb_id)
            highlight_json = {
                'id': view.buf['id'],
                'name': 'highlight',
                'ranges': view.get_selections(),
                'ping': ping,
            }
            self.agent.put(highlight_json)

    def on_create_buf(self, data):
        self.on_get_buf(data)

    def on_get_buf(self, data):
        buf_id = data['id']
        self.FLOO_BUFS[buf_id] = data
        view = self.get_view(buf_id)
        if view:
            self.update_view(data, view)
        else:
            self.save_buf(data)

    def on_rename_buf(self, data):
        new = utils.get_full_path(data['path'])
        old = utils.get_full_path(data['old_path'])
        new_dir = os.path.dirname(new)
        if new_dir:
            utils.mkdir(new_dir)
        view = self.get_view(data['id'])
        self.FLOO_BUFS[data['id']]['path'] = data['path']
        if view:
            view.rename(new)
        else:
            os.rename(old, new)

    def on_room_info(self, data):
        # Success! Reset counter
        self.room_info = data
        self.perms = data['perms']

        if 'patch' not in data['perms']:
            msg.log('We don\'t have patch permission. Setting buffers to read-only')

        utils.mkdir(G.PROJECT_PATH)

        floo_json = {
            'url': utils.to_room_url({
                'host': self.agent.host,
                'owner': self.agent.owner,
                'port': self.agent.port,
                'room': self.agent.room,
                'secure': self.agent.secure,
            })
        }
        with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
            floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

        for buf_id, buf in data['bufs'].iteritems():
            buf_id = int(buf_id)  # json keys must be strings
            buf_path = utils.get_full_path(buf['path'])
            new_dir = os.path.dirname(buf_path)
            utils.mkdir(new_dir)
            self.FLOO_BUFS[buf_id] = buf
            try:
                buf_fd = open(buf_path, 'r')
                buf_buf = buf_fd.read().decode('utf-8')
                md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest()
                if md5 == buf['md5']:
                    msg.debug('md5 sums match. not getting buffer')
                    buf['buf'] = buf_buf
                else:
                    raise Exception('different md5')
            except Exception:
                try:
                    open(buf_path, "a").close()
                except Exception as e:
                    msg.debug("couldn't touch file: %s becuase %s" % (buf_path, e))
                self.agent.send_get_buf(buf_id)

        msg.debug(G.PROJECT_PATH)

        self.agent.on_auth()

    def on_join(self, data):
        msg.log('%s joined the room' % data['username'])

    def on_part(self, data):
        msg.log('%s left the room' % data['username'])
        region_key = 'floobits-highlight-%s' % (data['user_id'])
        for window in sublime.windows():
            for view in window.views():
                view.erase_regions(region_key)

    @buf_populated
    def on_patch(self, data):
        added_newline = False
        buf_id = data['id']
        buf = self.FLOO_BUFS[buf_id]
        view = self.get_view(buf_id)
        DMP = dmp.diff_match_patch()
        if len(data['patch']) == 0:
            msg.error('wtf? no patches to apply. server is being stupid')
            return
        dmp_patches = DMP.patch_fromText(data['patch'])
        # TODO: run this in a separate thread
        if view:
            old_text = view.get_text()
        else:
            old_text = buf.get('buf', '')
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.debug('maybe vim is lame and discarded a trailing newline')
            old_text += '\n'
            added_newline = True
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != data['md5_before']:
            msg.warn('starting md5s don\'t match for %s. ours: %s patch: %s this is dangerous!' %
                    (buf['path'], md5_before, data['md5_before']))
            if added_newline:
                old_text = old_text[:-1]
                md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()

        t = DMP.patch_apply(dmp_patches, old_text)

        clean_patch = True
        for applied_patch in t[1]:
            if not applied_patch:
                clean_patch = False
                break

        if G.DEBUG:
            if len(t[0]) == 0:
                msg.debug('OMG EMPTY!')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])
            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', data['patch'])

        if not clean_patch:
            msg.error('failed to patch %s cleanly. re-fetching buffer' % buf['path'])
            return self.agent.send_get_buf(buf_id)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != data['md5_after']:
            msg.warn(
                '%s new hash %s != expected %s. re-fetching buffer...' %
                (buf['path'], cur_hash, data['md5_after'])
            )
            return self.agent.send_get_buf(buf_id)

        buf['buf'] = t[0]
        buf['md5'] = cur_hash

        if not view:
            self.save_buf(buf)
            return
        view.apply_patches(buf, t)

    def delete_buf(self, path):
        """deletes a path"""

        if not path:
            return

        path = utils.get_full_path(path)

        if not self.is_shared(path):
            msg.error('Skipping deleting %s because it is not in shared path %s.' % (path, G.PROJECT_PATH))
            return

        if os.path.isdir(path):
            for dirpath, dirnames, filenames in os.walk(path):
                # Don't care about hidden stuff
                dirnames[:] = [d for d in dirnames if d[0] != '.']
                for f in filenames:
                    f_path = os.path.join(dirpath, f)
                    if f[0] == '.':
                        msg.log('Not deleting buf for hidden file %s' % f_path)
                    else:
                        self.delete_buf(f_path)
            return
        buf_to_delete = None
        rel_path = utils.to_rel_path(path)
        for buf_id, buf in self.FLOO_BUFS.items():
            if rel_path == buf['path']:
                buf_to_delete = buf
                break
        if buf_to_delete is None:
            msg.error('%s is not in this room' % path)
            return
        msg.log('deleting buffer ', rel_path)
        event = {
            'name': 'delete_buf',
            'id': buf_to_delete['id'],
        }
        self.agent.put(event)

    @buf_populated
    def on_delete_buf(self, data):
        # TODO: somehow tell the user about this. maybe delete on disk too?
        del self.FLOO_BUFS[data['id']]
        path = utils.get_full_path(data['path'])
        if not G.DELETE_LOCAL_FILES:
            msg.log('Not deleting %s because delete_local_files is disabled' % path)
            return
        utils.rm(path)
        msg.warn('deleted %s because %s told me to.' % (path, data.get('username', 'the internet')))

    @buf_populated
    def on_highlight(self, data):
        #     floobits.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False))
        #buf_id, region_key, username, ranges, ping=False):
        ping = data.get('ping', False)
        if self.follow_mode:
            ping = True
        buf = self.FLOO_BUFS[data['id']]
        view = self.get_view(data['id'])
        if not view:
            if not ping:
                return
            view = self.create_view(buf)
            if not view:
                return
        if ping:
            try:
                offset = data['ranges'][0][0]
            except IndexError as e:
                msg.debug('could not get offset from range %s' % e)
            else:
                msg.log('You have been summoned by %s' % (data.get('username', 'an unknown user')))
                view.focus()
                view.set_cursor_position(offset)
        if G.SHOW_HIGHLIGHTS:
            view.highlight(data['ranges'], data['user_id'])

    def on_error(self, data):
        message = 'Floobits: Error! Message: %s' % str(data.get('msg'))
        msg.error(message)

    def on_disconnect(self, data):
        message = 'Floobits: Disconnected! Reason: %s' % str(data.get('reason'))
        msg.error(message)
        msg.error(message)
        self.agent.stop()
<|endoftext|>


<commit_before>import discord
import discord.voice_client

import asyncio

import permissions
import commands

import time
import re
from youtube_dl import YoutubeDL, DownloadError
import json
import subprocess
import functools

cd = 2
lastTime = time.time() - cd # for cooldowns

def voiceCommandExclusive(func):
    global voice
    async def command(triggerMessage):
        global voice
        global lastTime
        if voice is None:
            channel = triggerMessage.author.voice.channel

            if channel is not None:
                voice = await channel.connect() # there is no better way to do this
            else:
                await triggerMessage.channel.send("Error: Couldn't find your voice channel")
                return
        #print(str(time.time()) + ">" + str(lastTime) + "+" + str(cd))
        if time.time() > lastTime + cd:
            if not voice.is_connected():
                await triggerMessage.channel.send("Error: not connected to voice")
            else:
                if voice is not None:
                    if hasattr(voice, "is_playing"):
                        if voice.is_playing():
                            await triggerMessage.channel.send("Fuck you")
                            return
                lastTime = time.time()
                await func(triggerMessage)
    return command

"""A plugin for playing various sound clips.
    !setcd : Changes the cooldown between commands. Default is 30 seconds.
    !sound, !play <soundname> : plays <soundname> sound clip from our library
    !listsounds : lists the sounds in our library
    !addsound <name> <link> : downloads the sound from a youtube_dl compatible <link> and puts it in our library as <name>
    !youtube <link> : plays the audio from any youtube_dl compatible page
    """
client = None
voice = None
message = None

if not discord.opus.is_loaded():
    discord.opus.load_opus('/usr/lib/x86_64-linux-gnu/libopus.so')

audiobank = {}
with open('audiobank.json', 'r') as database:
    audiobank = json.loads(database.read())
    #asyncio.ensure_future(join())

@commands.registerEventHandler(name="join")
async def join(triggerMessage):
    global voice
    channel = None
    if len(triggerMessage.content.split()) < 2:
        for c in client.get_all_channels():
            if c.type==discord.ChannelType.voice and triggerMessage.author in c.members:
                channel = c
                print("Got channel " + c.name)
                break

        if channel is not None:
            if voice is not None:
                await voice.disconnect()
            voice = await channel.connect() # there is no better way to do this
    else:
        channel = discord.utils.get(triggerMessage.guild.voice_channels, name=triggerMessage.content[6:])
        if channel is not None:
            if voice is not None:
                await voice.disconnect()
            voice = await channel.connect() # there is no better way to do this
        else:
            await triggerMessage.channel.send( "That's not a valid voice channel on this server!")

@commands.registerEventHandler(name="disconnect")
async def disconnect(triggerMessage):
    if voice is not None:
        await voice.disconnect()

@commands.registerEventHandler(name="addsound")
@permissions.needs_admin
async def addSound(triggerMessage):
    async with triggerMessage.channel.typing():
        name = triggerMessage.content.split()[1]
        link = triggerMessage.content.split()[2]
        try:
            filename = re.sub('[-.() ]', '', name)
            filepath = "/home/seve/discordbot/sounds/" + filename + ".mp3"

            ydlopts = { "outtmpl" : filepath[:-4] + ".%(ext)s",
                        'format': 'webm[abr>0]/bestaudio/best',
                        'postprocessors': [{
                            'key': 'FFmpegExtractAudio',
                            'preferredcodec': 'mp3',
                            'preferredquality': '192',
                        }]
                        }
            with YoutubeDL(ydlopts) as ydl:
                ydl.download([link])

            audiobank.update({name : filepath})
            with open('audiobank.json', 'w') as database:
                    database.write(json.dumps(audiobank, indent=4))
                    await triggerMessage.channel.send( "Added sound " + name)
        except:
            name, link = link, name
            filename = re.sub('[-.() ]', '', name)
            filepath = "/home/seve/discordbot/sounds/" + filename + ".mp3"

            ydlopts = { "outtmpl" : filepath[:-4],
                        'format': 'webm[abr>0]/bestaudio/best',
                        'postprocessors': [{
                            'key': 'FFmpegExtractAudio',
                            'preferredcodec': 'mp3',
                            'preferredquality': '192',
                        }]
                        }
            with YoutubeDL(ydlopts) as ydl:
                ydl.download([link])

            audiobank.update({name : filepath})
            with open('audiobank.json', 'w') as database:
                    database.write(json.dumps(audiobank, indent=4))
                    await triggerMessage.channel.send( "Added sound " + name)

@commands.registerEventHandler(name="listsounds")
async def listSounds(triggerMessage):
    result = "Available audio clips: \n"
    for key in sorted(audiobank.keys()):
        result = result + key + "\t"
    await triggerMessage.channel.send( result)

@commands.registerEventHandler(name="sound")
@commands.registerEventHandler(name="play")
@voiceCommandExclusive
async def sound(triggerMessage):
    sound = triggerMessage.content.split()[-1]

    if sound in audiobank:
        print("Sound " + sound + " started by user " + str(triggerMessage.author.id))
        voice.play(discord.FFmpegPCMAudio(audiobank[sound]))
    else:
        await triggerMessage.channel.send( "Sound not found")

@commands.registerEventHandler(name="radio")
@voiceCommandExclusive
async def radio(triggerMessage):
    if len(triggerMessage.content.split()) < 2:
        triggerMessage.channel.send( "Not enough args for !radio")
        return

    freq = triggerMessage.content.split()[1]

    if len(triggerMessage.content.split()) > 2:
        modulation = triggerMessage.content.split()[2]
    else:
        modulation = "wbfm"

    voice.encoder_options(sample_rate=48000, channels=1)
    rtlfm = subprocess.Popen(["rtl_fm", "-f", freq, "-p","100", "-M", modulation, "-r", "48k", "-l", "0"], stdout=subprocess.PIPE)

    discord.voice_client.ProcessPlayer(rtlfm, voice, None)

    triggerMessage.channel.send( "Radio Started")

@commands.registerEventHandler(name="youtube")
@commands.registerEventHandler(name="websound")
@voiceCommandExclusive
async def youtube(triggerMessage):
    global message
    global repeating
    repeating = False
    if triggerMessage.channel.type != discord.ChannelType.text:
        await triggerMessage.channel.send( "Go fuck yourself")
        await discord.utils.get(client.get_all_channels(), server__name="IAA-Official", name='genearl', type=discord.ChannelType.text).send(triggerMessage.author.name + " should go fuck themselves")
        return

    await triggerMessage.channel.send( "Loading song at the request of " + triggerMessage.author.name)

    url=triggerMessage.content.split()[1]

    ydlopts = { 'format': 'webm[abr>0]/bestaudio/best',
                'verbose': True,
                'no_color': True
                }
    ydl = YoutubeDL(ydlopts)
    try:
        info = ydl.extract_info(url, download=False)
        if "entries" in info:
            info = info['entries'][0]

        source = discord.FFmpegPCMAudio(info['url'], before_options="-probesize 42M")

        def playAgain(e):
            if repeating:
                print("Play me again!", info['url'])
                source = discord.FFmpegPCMAudio(info['url'], before_options="-probesize 42M")
                voice.play(source, after=playAgain)

        voice.play(source, after=playAgain)
    except DownloadError as err:
        await triggerMessage.channel.send(err)
        return

    await client.change_presence(activity=discord.Game(name=info['title'], url=url, type=2))
    message = await triggerMessage.channel.send( "Now playing " + info['title'])

    await message.add_reaction('\u23ef')
    await message.add_reaction('\u23f9')
    await message.add_reaction('\u1F501')

    print("User " + str(triggerMessage.author.id) + " started video: " + url)

    try:
        await triggerMessage.delete()
    except discord.Forbidden:
        pass

@commands.registerEventHandler(triggerType="\\reactionAdded", name="soundControl")
async def soundControl(triggerMessage, reaction, user):
    global message
    global repeating
    if message is None:
        return

    if message.id is not None:
        print(voice.is_connected())
        if(type(reaction.emoji) is str) and voice.is_connected() and client.user != user and message.id == triggerMessage.id:
            if(reaction.emoji == '\u25b6') and voice.is_paused():
                print("Play Music")
                voice.resume()
            elif(reaction.emoji == '\u23ef') and (voice.is_playing() or voice.is_paused()):
                print("Play/pause")
                if not voice.is_paused():
                    voice.pause()
                else:
                    voice.resume()
            elif(reaction.emoji == '\u23f8') and voice.is_playing():
                print("Pause")
                voice.pause()
            elif(reaction.emoji == '\u23f9') and (voice.is_playing() or voice.is_paused()):
                print("Stop")
                repeating = False
                voice.stop()
                await triggerMessage.clear_reactions()
            elif(reaction.emoji == '\u1F501') and (voice.is_playing() or voice.is_paused()):
                if not repeating:
                    print("Begin Looping")
                    repeating = True
                else:
                    print("Stop Looping")
                    repeating = False
    #print(user.name.encode('unicode_escape').decode('ascii'))

@commands.registerEventHandler(name="stop")
async def stop(triggerMessage):
    if voice is not None:
        if voice.is_playing():
            voice.stop()
            await client.change_presence()

@commands.registerEventHandler(name="setcd")
@permissions.needs_moderator
async def setcd(triggerMessage):
    global cd
    try:
        cd = int(triggerMessage.content[7:])
        await triggerMessage.channel.send( "Cooldown is now " + triggerMessage.content[7:] + " seconds")
    except ValueError:
        await triggerMessage.channel.send( "Invalid input")
<commit_msg>Unicode is weird<commit_after>import discord
import discord.voice_client

import asyncio

import permissions
import commands

import time
import re
from youtube_dl import YoutubeDL, DownloadError
import json
import subprocess
import functools

cd = 2
lastTime = time.time() - cd # for cooldowns

def voiceCommandExclusive(func):
    global voice
    async def command(triggerMessage):
        global voice
        global lastTime
        if voice is None:
            channel = triggerMessage.author.voice.channel

            if channel is not None:
                voice = await channel.connect() # there is no better way to do this
            else:
                await triggerMessage.channel.send("Error: Couldn't find your voice channel")
                return
        #print(str(time.time()) + ">" + str(lastTime) + "+" + str(cd))
        if time.time() > lastTime + cd:
            if not voice.is_connected():
                await triggerMessage.channel.send("Error: not connected to voice")
            else:
                if voice is not None:
                    if hasattr(voice, "is_playing"):
                        if voice.is_playing():
                            await triggerMessage.channel.send("Fuck you")
                            return
                lastTime = time.time()
                await func(triggerMessage)
    return command

"""A plugin for playing various sound clips.
    !setcd : Changes the cooldown between commands. Default is 30 seconds.
    !sound, !play <soundname> : plays <soundname> sound clip from our library
    !listsounds : lists the sounds in our library
    !addsound <name> <link> : downloads the sound from a youtube_dl compatible <link> and puts it in our library as <name>
    !youtube <link> : plays the audio from any youtube_dl compatible page
    """
client = None
voice = None
message = None

if not discord.opus.is_loaded():
    discord.opus.load_opus('/usr/lib/x86_64-linux-gnu/libopus.so')

audiobank = {}
with open('audiobank.json', 'r') as database:
    audiobank = json.loads(database.read())
    #asyncio.ensure_future(join())

@commands.registerEventHandler(name="join")
async def join(triggerMessage):
    global voice
    channel = None
    if len(triggerMessage.content.split()) < 2:
        for c in client.get_all_channels():
            if c.type==discord.ChannelType.voice and triggerMessage.author in c.members:
                channel = c
                print("Got channel " + c.name)
                break

        if channel is not None:
            if voice is not None:
                await voice.disconnect()
            voice = await channel.connect() # there is no better way to do this
    else:
        channel = discord.utils.get(triggerMessage.guild.voice_channels, name=triggerMessage.content[6:])
        if channel is not None:
            if voice is not None:
                await voice.disconnect()
            voice = await channel.connect() # there is no better way to do this
        else:
            await triggerMessage.channel.send( "That's not a valid voice channel on this server!")

@commands.registerEventHandler(name="disconnect")
async def disconnect(triggerMessage):
    if voice is not None:
        await voice.disconnect()

@commands.registerEventHandler(name="addsound")
@permissions.needs_admin
async def addSound(triggerMessage):
    async with triggerMessage.channel.typing():
        name = triggerMessage.content.split()[1]
        link = triggerMessage.content.split()[2]
        try:
            filename = re.sub('[-.() ]', '', name)
            filepath = "/home/seve/discordbot/sounds/" + filename + ".mp3"

            ydlopts = { "outtmpl" : filepath[:-4] + ".%(ext)s",
                        'format': 'webm[abr>0]/bestaudio/best',
                        'postprocessors': [{
                            'key': 'FFmpegExtractAudio',
                            'preferredcodec': 'mp3',
                            'preferredquality': '192',
                        }]
                        }
            with YoutubeDL(ydlopts) as ydl:
                ydl.download([link])

            audiobank.update({name : filepath})
            with open('audiobank.json', 'w') as database:
                    database.write(json.dumps(audiobank, indent=4))
                    await triggerMessage.channel.send( "Added sound " + name)
        except:
            name, link = link, name
            filename = re.sub('[-.() ]', '', name)
            filepath = "/home/seve/discordbot/sounds/" + filename + ".mp3"

            ydlopts = { "outtmpl" : filepath[:-4],
                        'format': 'webm[abr>0]/bestaudio/best',
                        'postprocessors': [{
                            'key': 'FFmpegExtractAudio',
                            'preferredcodec': 'mp3',
                            'preferredquality': '192',
                        }]
                        }
            with YoutubeDL(ydlopts) as ydl:
                ydl.download([link])

            audiobank.update({name : filepath})
            with open('audiobank.json', 'w') as database:
                    database.write(json.dumps(audiobank, indent=4))
                    await triggerMessage.channel.send( "Added sound " + name)

@commands.registerEventHandler(name="listsounds")
async def listSounds(triggerMessage):
    result = "Available audio clips: \n"
    for key in sorted(audiobank.keys()):
        result = result + key + "\t"
    await triggerMessage.channel.send( result)

@commands.registerEventHandler(name="sound")
@commands.registerEventHandler(name="play")
@voiceCommandExclusive
async def sound(triggerMessage):
    sound = triggerMessage.content.split()[-1]

    if sound in audiobank:
        print("Sound " + sound + " started by user " + str(triggerMessage.author.id))
        voice.play(discord.FFmpegPCMAudio(audiobank[sound]))
    else:
        await triggerMessage.channel.send( "Sound not found")

@commands.registerEventHandler(name="radio")
@voiceCommandExclusive
async def radio(triggerMessage):
    if len(triggerMessage.content.split()) < 2:
        triggerMessage.channel.send( "Not enough args for !radio")
        return

    freq = triggerMessage.content.split()[1]

    if len(triggerMessage.content.split()) > 2:
        modulation = triggerMessage.content.split()[2]
    else:
        modulation = "wbfm"

    voice.encoder_options(sample_rate=48000, channels=1)
    rtlfm = subprocess.Popen(["rtl_fm", "-f", freq, "-p","100", "-M", modulation, "-r", "48k", "-l", "0"], stdout=subprocess.PIPE)

    discord.voice_client.ProcessPlayer(rtlfm, voice, None)

    triggerMessage.channel.send( "Radio Started")

@commands.registerEventHandler(name="youtube")
@commands.registerEventHandler(name="websound")
@voiceCommandExclusive
async def youtube(triggerMessage):
    global message
    global repeating
    repeating = False
    if triggerMessage.channel.type != discord.ChannelType.text:
        await triggerMessage.channel.send( "Go fuck yourself")
        await discord.utils.get(client.get_all_channels(), server__name="IAA-Official", name='genearl', type=discord.ChannelType.text).send(triggerMessage.author.name + " should go fuck themselves")
        return

    await triggerMessage.channel.send( "Loading song at the request of " + triggerMessage.author.name)

    url=triggerMessage.content.split()[1]

    ydlopts = { 'format': 'webm[abr>0]/bestaudio/best',
                'verbose': True,
                'no_color': True
                }
    ydl = YoutubeDL(ydlopts)
    try:
        info = ydl.extract_info(url, download=False)
        if "entries" in info:
            info = info['entries'][0]

        source = discord.FFmpegPCMAudio(info['url'], before_options="-probesize 42M")

        def playAgain(e):
            if repeating:
                print("Play me again!", info['url'])
                source = discord.FFmpegPCMAudio(info['url'], before_options="-probesize 42M")
                voice.play(source, after=playAgain)

        voice.play(source, after=playAgain)
    except DownloadError as err:
        await triggerMessage.channel.send(err)
        return

    await client.change_presence(activity=discord.Game(name=info['title'], url=url, type=2))
    message = await triggerMessage.channel.send( "Now playing " + info['title'])

    await message.add_reaction('\u23ef')
    await message.add_reaction('\u23f9')
    await message.add_reaction('\U0001F501')

    print("User " + str(triggerMessage.author.id) + " started video: " + url)

    try:
        await triggerMessage.delete()
    except discord.Forbidden:
        pass

@commands.registerEventHandler(triggerType="\\reactionAdded", name="soundControl")
async def soundControl(triggerMessage, reaction, user):
    global message
    global repeating
    if message is None:
        return

    if message.id is not None:
        print(voice.is_connected())
        if(type(reaction.emoji) is str) and voice.is_connected() and client.user != user and message.id == triggerMessage.id:
            if(reaction.emoji == '\u25b6') and voice.is_paused():
                print("Play Music")
                voice.resume()
            elif(reaction.emoji == '\u23ef') and (voice.is_playing() or voice.is_paused()):
                print("Play/pause")
                if not voice.is_paused():
                    voice.pause()
                else:
                    voice.resume()
            elif(reaction.emoji == '\u23f8') and voice.is_playing():
                print("Pause")
                voice.pause()
            elif(reaction.emoji == '\u23f9') and (voice.is_playing() or voice.is_paused()):
                print("Stop")
                repeating = False
                voice.stop()
                await triggerMessage.clear_reactions()
            elif(reaction.emoji == '\U0001F501') and (voice.is_playing() or voice.is_paused()):
                if not repeating:
                    print("Begin Looping")
                    repeating = True
                else:
                    print("Stop Looping")
                    repeating = False
    #print(user.name.encode('unicode_escape').decode('ascii'))

@commands.registerEventHandler(name="stop")
async def stop(triggerMessage):
    if voice is not None:
        if voice.is_playing():
            voice.stop()
            await client.change_presence()

@commands.registerEventHandler(name="setcd")
@permissions.needs_moderator
async def setcd(triggerMessage):
    global cd
    try:
        cd = int(triggerMessage.content[7:])
        await triggerMessage.channel.send( "Cooldown is now " + triggerMessage.content[7:] + " seconds")
    except ValueError:
        await triggerMessage.channel.send( "Invalid input")
<|endoftext|>


<commit_before>#!/usr/bin/env python

"""
Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""

import ntpath
import re

from lib.core.common import Backend
from lib.core.common import hashDBWrite
from lib.core.common import isStackingAvailable
from lib.core.common import normalizePath
from lib.core.common import ntToPosixSlashes
from lib.core.common import posixToNtSlashes
from lib.core.common import readInput
from lib.core.common import unArrayizeValue
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS
from lib.core.enums import OS
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.request import inject

class Miscellaneous:
    """
    This class defines miscellaneous functionalities for plugins.
    """

    def __init__(self):
        pass

    def getRemoteTempPath(self):
        if not conf.tmpPath and Backend.isDbms(DBMS.MSSQL):
            _ = unArrayizeValue(inject.getValue("SELECT SERVERPROPERTY('ErrorLogFileName')", safeCharEncode=False))
            if _:
                conf.tmpPath = ntpath.dirname(_)

        if not conf.tmpPath:
            if Backend.isOs(OS.WINDOWS):
                if conf.direct:
                    conf.tmpPath = "%TEMP%"
                else:
                    self.checkDbmsOs(detailed=True)

                    if Backend.getOsVersion() in ("2000", "NT"):
                        conf.tmpPath = "C:/WINNT/Temp"
                    elif Backend.isOs("XP"):
                        conf.tmpPath = "C:/Documents and Settings/All Users/Application Data/Temp"
                    else:
                        conf.tmpPath = "C:/Windows/Temp"
            else:
                conf.tmpPath = "/tmp"

        if re.search(r"\A[\w]:[\/\\]+", conf.tmpPath, re.I):
            Backend.setOs(OS.WINDOWS)

        conf.tmpPath = normalizePath(conf.tmpPath)
        conf.tmpPath = ntToPosixSlashes(conf.tmpPath)

        hashDBWrite(HASHDB_KEYS.CONF_TMP_PATH, conf.tmpPath)

        return conf.tmpPath

    def getVersionFromBanner(self):
        if "dbmsVersion" in kb.bannerFp:
            return

        infoMsg = "detecting back-end DBMS version from its banner"
        logger.info(infoMsg)

        if Backend.isDbms(DBMS.MYSQL):
            first, last = 1, 6

        elif Backend.isDbms(DBMS.PGSQL):
            first, last = 12, 6

        elif Backend.isDbms(DBMS.MSSQL):
            first, last = 29, 9

        else:
            raise SqlmapUnsupportedFeatureException("unsupported DBMS")

        query = queries[Backend.getIdentifiedDbms()].substring.query % (queries[Backend.getIdentifiedDbms()].banner.query, first, last)

        if conf.direct:
            query = "SELECT %s" % query

        kb.bannerFp["dbmsVersion"] = unArrayizeValue(inject.getValue(query))
        kb.bannerFp["dbmsVersion"] = (kb.bannerFp["dbmsVersion"] or "").replace(",", "").replace("-", "").replace(" ", "")

    def delRemoteFile(self, filename):
        if not filename:
            return

        self.checkDbmsOs()

        if Backend.isOs(OS.WINDOWS):
            filename = posixToNtSlashes(filename)
            cmd = "del /F /Q %s" % filename
        else:
            cmd = "rm -f %s" % filename

        self.execCmd(cmd, silent=True)

    def createSupportTbl(self, tblName, tblField, tblType):
        inject.goStacked("DROP TABLE %s" % tblName, silent=True)
        inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType))

    def cleanup(self, onlyFileTbl=False, udfDict=None, web=False):
        """
        Cleanup file system and database from sqlmap create files, tables
        and functions
        """

        if web and self.webBackdoorFilePath:
            logger.info("cleaning up the web files uploaded")

            self.delRemoteFile(self.webStagerFilePath)
            self.delRemoteFile(self.webBackdoorFilePath)

        if not isStackingAvailable() and not conf.direct:
            return

        if Backend.isOs(OS.WINDOWS):
            libtype = "dynamic-link library"

        elif Backend.isOs(OS.LINUX):
            libtype = "shared object"

        else:
            libtype = "shared library"

        if onlyFileTbl:
            logger.debug("cleaning up the database management system")
        else:
            logger.info("cleaning up the database management system")

        logger.debug("removing support tables")
        inject.goStacked("DROP TABLE %s" % self.fileTblName, silent=True)
        inject.goStacked("DROP TABLE %shex" % self.fileTblName, silent=True)

        if not onlyFileTbl:
            inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True)

            if Backend.isDbms(DBMS.MSSQL):
                return

            if udfDict is None:
                udfDict = self.sysUdfs

            for udf, inpRet in udfDict.items():
                message = "do you want to remove UDF '%s'? [Y/n] " % udf
                output = readInput(message, default="Y")

                if not output or output in ("y", "Y"):
                    dropStr = "DROP FUNCTION %s" % udf

                    if Backend.isDbms(DBMS.PGSQL):
                        inp = ", ".join(i for i in inpRet["input"])
                        dropStr += "(%s)" % inp

                    logger.debug("removing UDF '%s'" % udf)
                    inject.goStacked(dropStr, silent=True)

            logger.info("database management system cleanup finished")

            warnMsg = "remember that UDF %s files " % libtype

            if conf.osPwn:
                warnMsg += "and Metasploit related files in the temporary "
                warnMsg += "folder "

            warnMsg += "saved on the file system can only be deleted "
            warnMsg += "manually"
            logger.warn(warnMsg)

    def likeOrExact(self, what):
        message = "do you want sqlmap to consider provided %s(s):\n" % what
        message += "[1] as LIKE %s names (default)\n" % what
        message += "[2] as exact %s names" % what

        choice = readInput(message, default='1')

        if not choice or choice == '1':
            choice = '1'
            condParam = " LIKE '%%%s%%'"
        elif choice == '2':
            condParam = "='%s'"
        else:
            errMsg = "invalid value"
            raise SqlmapNoneDataException(errMsg)

        return choice, condParam
<commit_msg>added two debug messages for clarity<commit_after>#!/usr/bin/env python

"""
Copyright (c) 2006-2013 sqlmap developers (http://sqlmap.org/)
See the file 'doc/COPYING' for copying permission
"""

import ntpath
import re

from lib.core.common import Backend
from lib.core.common import hashDBWrite
from lib.core.common import isStackingAvailable
from lib.core.common import normalizePath
from lib.core.common import ntToPosixSlashes
from lib.core.common import posixToNtSlashes
from lib.core.common import readInput
from lib.core.common import unArrayizeValue
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
from lib.core.data import queries
from lib.core.enums import DBMS
from lib.core.enums import HASHDB_KEYS
from lib.core.enums import OS
from lib.core.exception import SqlmapNoneDataException
from lib.core.exception import SqlmapUnsupportedFeatureException
from lib.request import inject

class Miscellaneous:
    """
    This class defines miscellaneous functionalities for plugins.
    """

    def __init__(self):
        pass

    def getRemoteTempPath(self):
        if not conf.tmpPath and Backend.isDbms(DBMS.MSSQL):
            debugMsg = "identifying Microsoft SQL Server error log directory "
            debugMsg += "that sqlmap will use to store temporary files with "
            debugMsg += "commands' output"
            logger.debugMsg(debugMsg)

            _ = unArrayizeValue(inject.getValue("SELECT SERVERPROPERTY('ErrorLogFileName')", safeCharEncode=False))

            if _:
                conf.tmpPath = ntpath.dirname(_)

        if not conf.tmpPath:
            if Backend.isOs(OS.WINDOWS):
                if conf.direct:
                    conf.tmpPath = "%TEMP%"
                else:
                    self.checkDbmsOs(detailed=True)

                    if Backend.getOsVersion() in ("2000", "NT"):
                        conf.tmpPath = "C:/WINNT/Temp"
                    elif Backend.isOs("XP"):
                        conf.tmpPath = "C:/Documents and Settings/All Users/Application Data/Temp"
                    else:
                        conf.tmpPath = "C:/Windows/Temp"
            else:
                conf.tmpPath = "/tmp"

        if re.search(r"\A[\w]:[\/\\]+", conf.tmpPath, re.I):
            Backend.setOs(OS.WINDOWS)

        conf.tmpPath = normalizePath(conf.tmpPath)
        conf.tmpPath = ntToPosixSlashes(conf.tmpPath)

        debugMsg = "going to use %s as temporary files directory" % conf.tmpPath
        logger.debug(debugMsg)

        hashDBWrite(HASHDB_KEYS.CONF_TMP_PATH, conf.tmpPath)

        return conf.tmpPath

    def getVersionFromBanner(self):
        if "dbmsVersion" in kb.bannerFp:
            return

        infoMsg = "detecting back-end DBMS version from its banner"
        logger.info(infoMsg)

        if Backend.isDbms(DBMS.MYSQL):
            first, last = 1, 6

        elif Backend.isDbms(DBMS.PGSQL):
            first, last = 12, 6

        elif Backend.isDbms(DBMS.MSSQL):
            first, last = 29, 9

        else:
            raise SqlmapUnsupportedFeatureException("unsupported DBMS")

        query = queries[Backend.getIdentifiedDbms()].substring.query % (queries[Backend.getIdentifiedDbms()].banner.query, first, last)

        if conf.direct:
            query = "SELECT %s" % query

        kb.bannerFp["dbmsVersion"] = unArrayizeValue(inject.getValue(query))
        kb.bannerFp["dbmsVersion"] = (kb.bannerFp["dbmsVersion"] or "").replace(",", "").replace("-", "").replace(" ", "")

    def delRemoteFile(self, filename):
        if not filename:
            return

        self.checkDbmsOs()

        if Backend.isOs(OS.WINDOWS):
            filename = posixToNtSlashes(filename)
            cmd = "del /F /Q %s" % filename
        else:
            cmd = "rm -f %s" % filename

        self.execCmd(cmd, silent=True)

    def createSupportTbl(self, tblName, tblField, tblType):
        inject.goStacked("DROP TABLE %s" % tblName, silent=True)
        inject.goStacked("CREATE TABLE %s(%s %s)" % (tblName, tblField, tblType))

    def cleanup(self, onlyFileTbl=False, udfDict=None, web=False):
        """
        Cleanup file system and database from sqlmap create files, tables
        and functions
        """

        if web and self.webBackdoorFilePath:
            logger.info("cleaning up the web files uploaded")

            self.delRemoteFile(self.webStagerFilePath)
            self.delRemoteFile(self.webBackdoorFilePath)

        if not isStackingAvailable() and not conf.direct:
            return

        if Backend.isOs(OS.WINDOWS):
            libtype = "dynamic-link library"

        elif Backend.isOs(OS.LINUX):
            libtype = "shared object"

        else:
            libtype = "shared library"

        if onlyFileTbl:
            logger.debug("cleaning up the database management system")
        else:
            logger.info("cleaning up the database management system")

        logger.debug("removing support tables")
        inject.goStacked("DROP TABLE %s" % self.fileTblName, silent=True)
        inject.goStacked("DROP TABLE %shex" % self.fileTblName, silent=True)

        if not onlyFileTbl:
            inject.goStacked("DROP TABLE %s" % self.cmdTblName, silent=True)

            if Backend.isDbms(DBMS.MSSQL):
                return

            if udfDict is None:
                udfDict = self.sysUdfs

            for udf, inpRet in udfDict.items():
                message = "do you want to remove UDF '%s'? [Y/n] " % udf
                output = readInput(message, default="Y")

                if not output or output in ("y", "Y"):
                    dropStr = "DROP FUNCTION %s" % udf

                    if Backend.isDbms(DBMS.PGSQL):
                        inp = ", ".join(i for i in inpRet["input"])
                        dropStr += "(%s)" % inp

                    logger.debug("removing UDF '%s'" % udf)
                    inject.goStacked(dropStr, silent=True)

            logger.info("database management system cleanup finished")

            warnMsg = "remember that UDF %s files " % libtype

            if conf.osPwn:
                warnMsg += "and Metasploit related files in the temporary "
                warnMsg += "folder "

            warnMsg += "saved on the file system can only be deleted "
            warnMsg += "manually"
            logger.warn(warnMsg)

    def likeOrExact(self, what):
        message = "do you want sqlmap to consider provided %s(s):\n" % what
        message += "[1] as LIKE %s names (default)\n" % what
        message += "[2] as exact %s names" % what

        choice = readInput(message, default='1')

        if not choice or choice == '1':
            choice = '1'
            condParam = " LIKE '%%%s%%'"
        elif choice == '2':
            condParam = "='%s'"
        else:
            errMsg = "invalid value"
            raise SqlmapNoneDataException(errMsg)

        return choice, condParam
<|endoftext|>


<commit_before>import os
import re
import logging
import sqlite3

NOTE_REGEX = re.compile(r'!([^\s]+.*)')

class NotesPlugin(object):
    logger = None

    def __init__(self, cardinal, config):
        # Initialize logging
        self.logger = logging.getLogger(__name__)

        if config is not None:
            if config['shout_nick_notes_on_join']:
                self.callback_id = cardinal.event_manager.register_callback(
                    'irc.join', self.join_callback
                )

        # Connect to or create the note database
        self._connect_or_create_db(cardinal)

    def join_callback(self, cardinal, user, channel):
        content = self._get_note_from_db(user.group(1))
        if content:
            cardinal.sendMsg(channel, "[%s] %s" % (user.group(1), content))

    def _connect_or_create_db(self, cardinal):
        try:
            self.conn = sqlite3.connect(os.path.join(
                cardinal.storage_path,
                'notes-%s.db' % cardinal.network
            ))
        except Exception:
            self.conn = None
            self.logger.exception("Unable to access local notes database")
            return

        c = self.conn.cursor()
        c.execute("CREATE TABLE IF NOT EXISTS notes ("
                  "title text collate nocase PRIMARY KEY, "
                  "content text collate nocase)")
        self.conn.commit()

    def add_note(self, cardinal, user, channel, msg):
        if not self.conn:
            cardinal.sendMsg("Unable to access notes database.")
            return

        message = msg.split('=', 1)
        title_message = message[0].split(' ', 1)
        if len(message) < 2 or len(title_message) < 2:
            cardinal.sendMsg(channel, "Syntax: .addnote <title>=<content>")
            return

        title = title_message[1].strip()
        content = message[1].strip()
        if len(title) == 0 or len(content) == 0:
            cardinal.sendMsg(channel, "Syntax: .addnote <title>=<content>")
            return

        c = self.conn.cursor()
        c.execute("INSERT OR REPLACE INTO notes (title, content) VALUES(?, ?)",
                  (title, content))
        self.conn.commit()

        cardinal.sendMsg(channel, "Saved note '%s'." % title)

    add_note.commands = ['addnote']
    add_note.help = ["Saves a note to the database for retrieval later.",
                     "Syntax: .addnote <title>=<content>"]

    def delete_note(self, cardinal, user, channel, msg):
        if not self.conn:
            cardinal.sendMsg(channel, "Unable to access notes database.")
            return

        msg = msg.split(' ', 1)
        if len(msg) < 2 or len(msg[1]) == 0:
            cardinal.sendMsg(channel, "Syntax: .delnote <title>")
            return

        title = msg[1]
        c = self.conn.cursor()
        c.execute("SELECT COUNT(title) FROM notes WHERE title=?", (title,))
        result = c.fetchone()

        if not result[0]:
            cardinal.sendMsg(channel, "No note found under '%s'." % title)
            return

        c.execute("DELETE FROM notes WHERE title = ?", (title,))
        self.conn.commit()

        cardinal.sendMsg(channel, "Deleted note saved under '%s'." % title)

    delete_note.commands = ['delnote']
    delete_note.help = ["Deletes a note from the database.",
                        "Syntax: .delnote <title>"]

    def get_note(self, cardinal, user, channel, msg):
        if not self.conn:
            cardinal.sendMsg(channel, "Unable to access notes database.")
            return

        message = msg.split(' ', 1)
        # Check if they are using ! syntax.
        if message[0][0] == '!':
            title = ' '.join(message)[1:]
        else:
            if len(message) != 2:
                cardinal.sendMsg(channel, "Syntax: .note <title>")
                return

            # Grab title for .note syntax.
            title = message[1]

        content = self._get_note_from_db(title)
        if not content:
            cardinal.sendMsg(channel, "No note found under '%s'." % title)
            return

        cardinal.sendMsg(channel, "%s: %s" % (title, content))

    get_note.commands = ["note"]
    get_note.regex = NOTE_REGEX
    get_note.help  = ["Retrieve a saved note.",
                       "Syntax: .note <title>"]

    def _get_note_from_db(self, title):
        c = self.conn.cursor()
        c.execute("SELECT content FROM notes WHERE title=?", (title,))
        result = c.fetchone()
        if not result:
            return False
        else:
            return bytes(result[0])

    def close(self, cardinal):
        if hasattr(self, 'callback_id'):
            cardinal.event_manager.remove_callback('irc.join',
                                                   self.callback_id)

def setup(cardinal, config):
    return NotesPlugin(cardinal, config)
<commit_msg>Fix note plugin regex<commit_after>import os
import re
import logging
import sqlite3

NOTE_REGEX = re.compile(r'^!([^\s]+.*)')

class NotesPlugin(object):
    logger = None

    def __init__(self, cardinal, config):
        # Initialize logging
        self.logger = logging.getLogger(__name__)

        if config is not None:
            if config['shout_nick_notes_on_join']:
                self.callback_id = cardinal.event_manager.register_callback(
                    'irc.join', self.join_callback
                )

        # Connect to or create the note database
        self._connect_or_create_db(cardinal)

    def join_callback(self, cardinal, user, channel):
        content = self._get_note_from_db(user.group(1))
        if content:
            cardinal.sendMsg(channel, "[%s] %s" % (user.group(1), content))

    def _connect_or_create_db(self, cardinal):
        try:
            self.conn = sqlite3.connect(os.path.join(
                cardinal.storage_path,
                'notes-%s.db' % cardinal.network
            ))
        except Exception:
            self.conn = None
            self.logger.exception("Unable to access local notes database")
            return

        c = self.conn.cursor()
        c.execute("CREATE TABLE IF NOT EXISTS notes ("
                  "title text collate nocase PRIMARY KEY, "
                  "content text collate nocase)")
        self.conn.commit()

    def add_note(self, cardinal, user, channel, msg):
        if not self.conn:
            cardinal.sendMsg("Unable to access notes database.")
            return

        message = msg.split('=', 1)
        title_message = message[0].split(' ', 1)
        if len(message) < 2 or len(title_message) < 2:
            cardinal.sendMsg(channel, "Syntax: .addnote <title>=<content>")
            return

        title = title_message[1].strip()
        content = message[1].strip()
        if len(title) == 0 or len(content) == 0:
            cardinal.sendMsg(channel, "Syntax: .addnote <title>=<content>")
            return

        c = self.conn.cursor()
        c.execute("INSERT OR REPLACE INTO notes (title, content) VALUES(?, ?)",
                  (title, content))
        self.conn.commit()

        cardinal.sendMsg(channel, "Saved note '%s'." % title)

    add_note.commands = ['addnote']
    add_note.help = ["Saves a note to the database for retrieval later.",
                     "Syntax: .addnote <title>=<content>"]

    def delete_note(self, cardinal, user, channel, msg):
        if not self.conn:
            cardinal.sendMsg(channel, "Unable to access notes database.")
            return

        msg = msg.split(' ', 1)
        if len(msg) < 2 or len(msg[1]) == 0:
            cardinal.sendMsg(channel, "Syntax: .delnote <title>")
            return

        title = msg[1]
        c = self.conn.cursor()
        c.execute("SELECT COUNT(title) FROM notes WHERE title=?", (title,))
        result = c.fetchone()

        if not result[0]:
            cardinal.sendMsg(channel, "No note found under '%s'." % title)
            return

        c.execute("DELETE FROM notes WHERE title = ?", (title,))
        self.conn.commit()

        cardinal.sendMsg(channel, "Deleted note saved under '%s'." % title)

    delete_note.commands = ['delnote']
    delete_note.help = ["Deletes a note from the database.",
                        "Syntax: .delnote <title>"]

    def get_note(self, cardinal, user, channel, msg):
        if not self.conn:
            cardinal.sendMsg(channel, "Unable to access notes database.")
            return

        message = msg.split(' ', 1)
        # Check if they are using ! syntax.
        if message[0][0] == '!':
            title = ' '.join(message)[1:]
        else:
            if len(message) != 2:
                cardinal.sendMsg(channel, "Syntax: .note <title>")
                return

            # Grab title for .note syntax.
            title = message[1]

        content = self._get_note_from_db(title)
        if not content:
            cardinal.sendMsg(channel, "No note found under '%s'." % title)
            return

        cardinal.sendMsg(channel, "%s: %s" % (title, content))

    get_note.commands = ["note"]
    get_note.regex = NOTE_REGEX
    get_note.help  = ["Retrieve a saved note.",
                       "Syntax: .note <title>"]

    def _get_note_from_db(self, title):
        c = self.conn.cursor()
        c.execute("SELECT content FROM notes WHERE title=?", (title,))
        result = c.fetchone()
        if not result:
            return False
        else:
            return bytes(result[0])

    def close(self, cardinal):
        if hasattr(self, 'callback_id'):
            cardinal.event_manager.remove_callback('irc.join',
                                                   self.callback_id)

def setup(cardinal, config):
    return NotesPlugin(cardinal, config)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# vim: set ts=4 et

import sqlite3

from plugin import *

class Plugin(BasePlugin):
    def on_load(self, reloading):
        self.db = sqlite3.connect('data/notes.db')
        c = self.db.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS notes
                     (channel text, sender text, recipient text, message text)''')
        self.db.commit()

    def on_unload(self, reloading):
        self.db.close()

    @hook
    def note_trigger(self, msg, args, argstr):
        if not msg.channel:
            return

        data = {'channel': msg.param[0], 'sender': msg.nick}
        data['recipient'], data['message'] = argstr.split(None, 1)

        c = self.db.cursor()
        c.execute('INSERT INTO notes VALUES (:channel, :sender, :recipient, :message)', data)
        self.db.commit()

    @hook
    def privmsg_command(self, msg):
        if not msg.channel:
            return

        c = self.db.cursor()
        criteria = {'channel': msg.param[0], 'recipient': msg.nick}
        c.execute('SELECT sender, message FROM notes WHERE channel=:channel AND recipient=:recipient', criteria)
        rows = c.fetchall()
        if rows:
            for row in rows:
                msg.reply("Note: <%s> %s" % row)
            c.execute('DELETE FROM notes WHERE channel=:channel AND recipient=:recipient', criteria)
            self.db.commit()

<commit_msg>Change note trigger to tell, and make it reply<commit_after># -*- coding: utf-8 -*-
# vim: set ts=4 et

import sqlite3

from plugin import *

class Plugin(BasePlugin):
    def on_load(self, reloading):
        self.db = sqlite3.connect('data/notes.db')
        c = self.db.cursor()
        c.execute('''CREATE TABLE IF NOT EXISTS notes
                     (channel text, sender text, recipient text, message text)''')
        self.db.commit()

    def on_unload(self, reloading):
        self.db.close()

    @hook
    def tell_trigger(self, msg, args, argstr):
        if not msg.channel:
            return

        data = {'channel': msg.param[0], 'sender': msg.nick}
        data['recipient'], data['message'] = argstr.split(None, 1)

        c = self.db.cursor()
        c.execute('INSERT INTO notes VALUES (:channel, :sender, :recipient, :message)', data)
        self.db.commit()

        msg.reply("Aye aye!")

    @hook
    def privmsg_command(self, msg):
        if not msg.channel:
            return

        c = self.db.cursor()
        criteria = {'channel': msg.param[0], 'recipient': msg.nick}
        c.execute('SELECT sender, message FROM notes WHERE channel=:channel AND recipient=:recipient', criteria)
        rows = c.fetchall()
        if rows:
            for row in rows:
                msg.reply("Note: <%s> %s" % row)
            c.execute('DELETE FROM notes WHERE channel=:channel AND recipient=:recipient', criteria)
            self.db.commit()

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

"""Pluralize word according French grammar rules

Special cases are based on:
http://fr.wiktionary.org/wiki/Annexe:Pluriels_irr%C3%A9guliers_en_fran%C3%A7ais
"""

def pluralize(word):
    for GRAMMAR_RULE in (_ail_word, _al_word, _au_word, _eil_word, _eu_word, _ou_word, _s_word, _x_word, _z_word,
            _default):
        plural = GRAMMAR_RULE(word)
        if plural:
            return plural

def _ail_word(word):
    if word.endswith("ail"):
        if word == "ail":
            return "aulx"
        elif word in ("bail", "corail", u"émail", "fermail", "soupirail", "travail", "vantail", "ventail", "vitrail"):
            return word[:-3] + "aux"
        return word + "s"

def _al_word(word):
    if word.endswith("al"):
        if word in (
            "bal", "carnaval", "chacal", "festival", u"récital", u"régal",
            "bancal", "fatal", "fractal", "final", "morfal", "natal", "naval",
            u"aéronaval",
            u"anténatal", u"néonatal", u"périnatal", u"postnatal", u"prénatal",
            "tonal", "atonal", "bitonal", "polytonal",
            "corral", "deal", "goal", "autogoal", "revival", "serial", "spiritual", "trial",
            "caracal", "chacal", "gavial", "gayal", "narval", "quetzal", "rorqual", "serval",
            "metical", "rial", "riyal", "ryal",
            "cantal", "emmental", "emmenthal",
            u"floréal", "germinal", "prairial",
            ):
            return word + "s"
        return word[:-2] + "aux"

def _au_word(word):
    if word.endswith("au"):
        if word in ("berimbau", "donau", "karbau", "landau", "pilau", "sarrau", "unau"):
            return word + "s"
        return word + "x"

def _eil_word(word):
    if word.endswith("eil"):
        return "vieux" if word == "vieil" else word + "s"

def _eu_word(word):
    if word.endswith("eu"):
        if word in ("bleu", u"émeu", "enfeu", "pneu", "rebeu"):
            return word + "s"
        return word + "x"

def _ou_word(word):
    if word.endswith("ou"):
        if word in ("bijou", "caillou", "chou", "genou", "hibou", "joujou", "pou"):
            return word + "x"
        return word + "s"

def _s_word(word):
    if word[-1] == "s":
        return word

def _x_word(word):
    if word[-1] == "x":
        return word

def _z_word(word):
    if word[-1] == "z":
        return word

def _default(word):
    return word + "s"

#SINGULARIZE

def Singularize(word):
    for GRAMMAR_RULE in (_ail_word_sing, _ou_word_sing, _s_word_sing, _default_sing):
        singular = GRAMMAR_RULE(word)
        if singular:
            return singular

def _ail_word_sing(word):
    if word.endswith("aux"):
        if word in ("baux", "coraux", u"émaux", "fermaux", "soupiraux", "travaux", "vantaux", "ventaux", "vitraux"):
            return word[:-3] + "ail"
        else:
            return word[:-3] + "al"

def _ou_word_sing(word):
    if word.endswith("oux"):
        if word in ("bijoux", "cailloux", "choux", "genoux", "hiboux", "joujoux", "poux"):
            return word[:-1]
        else:
            return word

def _s_word_sing(word):
    if word.endswith("s"):
        if word in (u"abcès", u"accès", "abus", "albatros", "anchois", "anglais", "autobus", "brebis", "carquois", "cas", "chas", "colis", "concours", "corps", "cours", u"cyprès", u"décès", "devis", "discours", "dos", "embarras", "engrais", "entrelacs", u"excès", "fois", "fonds", u"gâchis", "gars", "glas", "guet-apens", u"héros", "intrus", "jars", "jus", u"kermès", "lacis", "legs", "lilas", "marais", "matelas", u"mépris", "mets", "mois", "mors", "obus", "os", "palais", "paradis", "parcours", "pardessus", "pays", "plusieurs", "poids", "pois", "pouls", "printemps", "processus", u"progrès", "puits", "pus", "rabais", "radis", "recors", "recours", "refus", "relais", "remords", "remous", u"rhinocéros", "repas", "rubis", "sas", "secours", "souris", u"succès", "talus", "tapis", "taudis", "temps", "tiers", "univers", "velours", "verglas", "vernis", "virus", "accordailles", "affres", "aguets", "alentours", "ambages", "annales", "appointements", "archives", "armoiries", u"arrérages", "arrhes", "calendes", "cliques", "complies", u"condoléances", "confins", u"dépens", u"ébats", "entrailles", u"épousailles", "errements", "fiançailles", "frais", u"funérailles", "gens", "honoraires", "matines", "mœurs", u"obsèques", u"pénates", "pierreries", u"préparatifs", "relevailles", "rillettes", u"sévices", u"ténèbres", "thermes", "us", u"vêpres", "victuailles"):
            return word
        else:
            return word[:-1]

def _default_sing(word):
    return word

<commit_msg>remove trailing spaces<commit_after># -*- coding: utf-8 -*-

"""Pluralize word according French grammar rules

Special cases are based on:
http://fr.wiktionary.org/wiki/Annexe:Pluriels_irr%C3%A9guliers_en_fran%C3%A7ais
"""

def pluralize(word):
    for GRAMMAR_RULE in (_ail_word, _al_word, _au_word, _eil_word, _eu_word, _ou_word, _s_word, _x_word, _z_word,
            _default):
        plural = GRAMMAR_RULE(word)
        if plural:
            return plural

def _ail_word(word):
    if word.endswith("ail"):
        if word == "ail":
            return "aulx"
        elif word in ("bail", "corail", u"émail", "fermail", "soupirail", "travail", "vantail", "ventail", "vitrail"):
            return word[:-3] + "aux"
        return word + "s"

def _al_word(word):
    if word.endswith("al"):
        if word in (
            "bal", "carnaval", "chacal", "festival", u"récital", u"régal",
            "bancal", "fatal", "fractal", "final", "morfal", "natal", "naval",
            u"aéronaval",
            u"anténatal", u"néonatal", u"périnatal", u"postnatal", u"prénatal",
            "tonal", "atonal", "bitonal", "polytonal",
            "corral", "deal", "goal", "autogoal", "revival", "serial", "spiritual", "trial",
            "caracal", "chacal", "gavial", "gayal", "narval", "quetzal", "rorqual", "serval",
            "metical", "rial", "riyal", "ryal",
            "cantal", "emmental", "emmenthal",
            u"floréal", "germinal", "prairial",
            ):
            return word + "s"
        return word[:-2] + "aux"

def _au_word(word):
    if word.endswith("au"):
        if word in ("berimbau", "donau", "karbau", "landau", "pilau", "sarrau", "unau"):
            return word + "s"
        return word + "x"

def _eil_word(word):
    if word.endswith("eil"):
        return "vieux" if word == "vieil" else word + "s"

def _eu_word(word):
    if word.endswith("eu"):
        if word in ("bleu", u"émeu", "enfeu", "pneu", "rebeu"):
            return word + "s"
        return word + "x"

def _ou_word(word):
    if word.endswith("ou"):
        if word in ("bijou", "caillou", "chou", "genou", "hibou", "joujou", "pou"):
            return word + "x"
        return word + "s"

def _s_word(word):
    if word[-1] == "s":
        return word

def _x_word(word):
    if word[-1] == "x":
        return word

def _z_word(word):
    if word[-1] == "z":
        return word

def _default(word):
    return word + "s"

#SINGULARIZE

def Singularize(word):
    for GRAMMAR_RULE in (_ail_word_sing, _ou_word_sing, _s_word_sing, _default_sing):
        singular = GRAMMAR_RULE(word)
        if singular:
            return singular

def _ail_word_sing(word):
    if word.endswith("aux"):
        if word in ("baux", "coraux", u"émaux", "fermaux", "soupiraux", "travaux", "vantaux", "ventaux", "vitraux"):
            return word[:-3] + "ail"
        else:
            return word[:-3] + "al"

def _ou_word_sing(word):
    if word.endswith("oux"):
        if word in ("bijoux", "cailloux", "choux", "genoux", "hiboux", "joujoux", "poux"):
            return word[:-1]
        else:
            return word

def _s_word_sing(word):
    if word.endswith("s"):
        if word in (u"abcès", u"accès", "abus", "albatros", "anchois", "anglais", "autobus", "brebis", "carquois", "cas", "chas", "colis", "concours", "corps", "cours", u"cyprès", u"décès", "devis", "discours", "dos", "embarras", "engrais", "entrelacs", u"excès", "fois", "fonds", u"gâchis", "gars", "glas", "guet-apens", u"héros", "intrus", "jars", "jus", u"kermès", "lacis", "legs", "lilas", "marais", "matelas", u"mépris", "mets", "mois", "mors", "obus", "os", "palais", "paradis", "parcours", "pardessus", "pays", "plusieurs", "poids", "pois", "pouls", "printemps", "processus", u"progrès", "puits", "pus", "rabais", "radis", "recors", "recours", "refus", "relais", "remords", "remous", u"rhinocéros", "repas", "rubis", "sas", "secours", "souris", u"succès", "talus", "tapis", "taudis", "temps", "tiers", "univers", "velours", "verglas", "vernis", "virus", "accordailles", "affres", "aguets", "alentours", "ambages", "annales", "appointements", "archives", "armoiries", u"arrérages", "arrhes", "calendes", "cliques", "complies", u"condoléances", "confins", u"dépens", u"ébats", "entrailles", u"épousailles", "errements", "fiançailles", "frais", u"funérailles", "gens", "honoraires", "matines", "mœurs", u"obsèques", u"pénates", "pierreries", u"préparatifs", "relevailles", "rillettes", u"sévices", u"ténèbres", "thermes", "us", u"vêpres", "victuailles"):
            return word
        else:
            return word[:-1]

def _default_sing(word):
    return word

<|endoftext|>


<commit_before>import importlib
import logging
from volttron.platform.agent import utils

_log = logging.getLogger(__name__)
utils.setup_logging()
__version__ = "0.1"

__all__ = ['Model']

class Model(object):
    def __init__(self, config, **kwargs):
        base_module = "volttron.pnnl.models."
        try:
            model_type = config["model_type"]
        except KeyError as e:
            _log.exception("Missing Model Type key: {}".format(e))
            raise e
        _file, model_type = model_type.split(".")
        module = importlib.import_module(base_module + _file)
        model_class = getattr(module, model_type)
        self.model = model_class(config, self)

    def get_q(self, _set, sched_index, market_index, occupied):
        q = self.model.predict(_set, sched_index, market_index, occupied)
<commit_msg>Add return statement to Model.get_q<commit_after>import importlib
import logging
from volttron.platform.agent import utils

_log = logging.getLogger(__name__)
utils.setup_logging()
__version__ = "0.1"

__all__ = ['Model']

class Model(object):
    def __init__(self, config, **kwargs):
        base_module = "volttron.pnnl.models."
        try:
            model_type = config["model_type"]
        except KeyError as e:
            _log.exception("Missing Model Type key: {}".format(e))
            raise e
        _file, model_type = model_type.split(".")
        module = importlib.import_module(base_module + _file)
        model_class = getattr(module, model_type)
        self.model = model_class(config, self)

    def get_q(self, _set, sched_index, market_index, occupied):
        q = self.model.predict(_set, sched_index, market_index, occupied)
        return q
<|endoftext|>


<commit_before>#
#    ICRAR - International Centre for Radio Astronomy Research
#    (c) UWA - The University of Western Australia, 2019
#    Copyright by UWA (in the framework of the ICRAR)
#    All rights reserved
#
#    This library is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This library is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
#    MA 02111-1307  USA
#

import unittest
import os
import pkg_resources

from dlg.dropmake import pg_generator
import json

lg_dir = pkg_resources.resource_filename(__name__, 'logical_graphs')  # @UndefinedVariable

class LGFillTest(unittest.TestCase):

    def test_fill_lg(self):
        params = {
            'param1': 1,
            'param2': '2',
            'param1.param2': True,
            'param4': {
                'what': 'hi'
            }
        }
        with open(os.path.join(lg_dir, 'chiles_simple.json')) as f:
            lg = pg_generator.fill(json.load(f), params)
        for node_idx, value in zip((20, 21, 22, 23), ('1', '2', 'True', 'hi')):
            self.assertEqual(lg['nodeDataArray'][node_idx]['Arg10'], value)<commit_msg>Updated test to handle moving 'Arg10' attribute from root-level of node to fields array.<commit_after>#
#    ICRAR - International Centre for Radio Astronomy Research
#    (c) UWA - The University of Western Australia, 2019
#    Copyright by UWA (in the framework of the ICRAR)
#    All rights reserved
#
#    This library is free software; you can redistribute it and/or
#    modify it under the terms of the GNU Lesser General Public
#    License as published by the Free Software Foundation; either
#    version 2.1 of the License, or (at your option) any later version.
#
#    This library is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#    Lesser General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public
#    License along with this library; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
#    MA 02111-1307  USA
#

import unittest
import os
import pkg_resources

from dlg.dropmake import pg_generator
import json

lg_dir = pkg_resources.resource_filename(__name__, 'logical_graphs')  # @UndefinedVariable

class LGFillTest(unittest.TestCase):

    def test_fill_lg(self):
        params = {
            'param1': 1,
            'param2': '2',
            'param1.param2': True,
            'param4': {
                'what': 'hi'
            }
        }
        with open(os.path.join(lg_dir, 'chiles_simple.json')) as f:
            lg = pg_generator.fill(json.load(f), params)
        for node_idx, value in zip((20, 21, 22, 23), ('1', '2', 'True', 'hi')):
            node = lg['nodeDataArray'][node_idx]
            found = None
            for field in node['fields']:
                if field['name'] == 'Arg10':
                    found = field['value']
            self.assertEqual(found, value)
<|endoftext|>