def is_process_alive(pid):
def kill_processes(pids):
def check(path):
def write(path, append=False, pid=None):
def remove(path): class Application:
    __metaclass__ = ABCMeta

    name = "NONE"

    def __init__(self):
        self.port = Settings['%s_app' % self.name]['port']
        self.pid_file = os.path.join(Settings['log_path'], '%s.%d.pid' % (self.name, self.port))
        self.log_file = os.path.join(Settings['log_path'], '%s.%d.log' % (self.name, self.port))
        self.log = open(self.log_file, 'a+')
        self.command = self.parse_command()

        self.queue_worker_pids = []
        self.clean_pids()

        if self.command == "stop":
            for worker_pid in self.queue_worker_pids:
                os.kill(worker_pid, signal.SIGKILL)
            sys.exit()

    def parse_command(self):
        usage = "Usage: %prog start|stop"
        parser = OptionParser(usage=usage)
        _, args = parser.parse_args()

        if len(args) != 1 and args[0] not in ('start', 'stop'):
            parser.print_help()
            sys.exit(1)

        return args[0]

    def clean_pids(self):
        if os.path.exists(self.pid_file):
            pid.check(self.pid_file)
            os.unlink(self.pid_file)
            time.sleep(1)

    @abstractmethod
    def start_services(self):
        pass

    def run(self):
        daemon_context = daemon.DaemonContext(stdout=self.log, stderr=self.log, working_directory=os.getcwd())
        with daemon_context:
            pid.write(self.pid_file, append=True)
            try:
                self.start_services()
                pid.write(self.pid_file, append=True)

                # Drop privileges
                uid = pwd.getpwnam(Settings.get("username", "www-data"))[2]
                os.setuid(uid)

                tornado.ioloop.IOLoop.instance().start()
            finally:
                pid.remove(self.pid_file) class MailQueue(object):

    message_queue = None
    worker_process = None
    smtp = None

    @classmethod
    def start_worker(cls):
        if cls.worker_process is not None:
            return []
        cls.message_queue = JoinableQueue()
        cls.worker_process = Process(target=cls.process_queue, name='mail-queue')
        cls.worker_process.daemon = True
        cls.worker_process.start()
        return [cls.worker_process.pid]

    @classmethod
    def process_queue(cls):
        # We double-nest 'while True' blocks here so that we can
        # try to re-use the same SMTP server connection for batches
        # of emails, but not keep it open for long periods without
        # any emails to send.
        while True:
            # Blocks indefinitely
            send_email_args = cls.message_queue.get(True)
            cls.smtp = smtplib.SMTP('127.0.0.1', 25)
            while True:
                cls._send_email(*send_email_args)
                try:
                    # Only blocks for 5 seconds max, raises Empty if still nothing
                    send_email_args = cls.message_queue.get(True, 5)
                except Empty:
                    # Done with this batch, use a blocking call to wait for the next
                    break
            cls.smtp.quit()

    @classmethod
    def _send_email(cls, recipient, message, subject, from_email):
        msg = email.mime.text.MIMEText(message, 'html')
        msg['Subject'] = subject
        msg['From'] = from_email

        notifyonly = Settings['mail']['notifyonly']
        if notifyonly:
            msg['To'] = ', '.join(notifyonly)
            msg.set_payload('Original recipients: %s\n\n%s' % (recipient, msg.get_payload()))
            cls.smtp.sendmail(from_email, notifyonly, msg.as_string())
            cls.message_queue.task_done()
            return

        msg['To'] = recipient
        cls.smtp.sendmail(from_email, [recipient], msg.as_string())
        other_recipients = set(Settings['mail']['notifyall']) - set([recipient])
        if other_recipients:
            msg = email.mime.text.MIMEText(message, 'html')
            msg['Subject'] = '[all] %s' % subject
            msg['From'] = Settings['mail']['from']
            msg['To'] = ', '.join(other_recipients)
            cls.smtp.sendmail(from_email, list(other_recipients), msg.as_string())
        cls.message_queue.task_done()

    @classmethod
    def enqueue_email(cls, recipients, message, subject='', from_email=Settings['mail']['from']):
        if isinstance(recipients, (list, set, tuple)):
            # Flatten non-string iterables
            for recipient in recipients:
                cls.enqueue_email(recipient, message, subject, from_email)
        elif isinstance(recipients, (str, unicode)):
            if cls.message_queue is not None:
                cls.message_queue.put((recipients, message, subject, from_email))
            else:
                logging.error("Failed to enqueue email: MailQueue not initialized")
        else:
            raise ValueError('Recipient(s) must be a string or iterable of strings')

    @classmethod
    def enqueue_user_email(cls, recipients, *args, **kwargs):
        """Transforms a list of 'user' to 'user@default_domain.com', then invokes enqueue_email."""
        domain = Settings['mail']['default_domain']
        recipients = ['%s@%s' % (recipient, domain) if '@' not in recipient else recipient for recipient in recipients]
        return cls.enqueue_email(recipients, *args, **kwargs) class RBQueue(object):

    review_queue = None
    worker_process = None

    @classmethod
    def start_worker(cls):
        if cls.worker_process is not None:
            return []
        cls.review_queue = JoinableQueue()
        cls.worker_process = Process(target=cls.process_queue, name='rb-queue')
        cls.worker_process.daemon = True
        cls.worker_process.start()
        return [cls.worker_process.pid]

    @classmethod
    def process_queue(cls):
        while True:
            time.sleep(1)

            review_id = cls.review_queue.get()
            try:
                cls.mark_review_as_submitted(review_id)
            except Exception:
                logging.error(
                    "ReviewBoard queue worker encountered an error (review_id: %r)",
                    review_id, exc_info=True
                )
            finally:
                cls.review_queue.task_done()

    @classmethod
    def mark_review_as_submitted(cls, review_id):
        credentials = base64.b64encode("%s:%s" % (
            Settings['reviewboard']['username'],
            Settings['reviewboard']['password'],
        ))

        headers = {
            'Accept': 'application/json',
            'Authorization': 'Basic %s' % credentials,
            'Content-Type': 'application/x-www-form-urlencoded',
        }

        data = urlencode({'status': 'submitted'})

        conn = httplib.HTTPSConnection(Settings['reviewboard']['servername'])
        conn.request("PUT", "/api/review-requests/%d/" % review_id, data, headers)
        raw_result = conn.getresponse().read()
        try:
            result = json.loads(raw_result)
        except Exception:
            result = None
        conn.close()

        if not result or result.get('stat') != 'ok':
            logging.error(
                "Unable to mark review %r as submitted (%r)",
                review_id,
                raw_result,
                exc_info=True
            )

    @classmethod
    def enqueue_review(cls, review_id):
        cls.review_queue.put(review_id) class AddRequestServlet(RequestHandler):

    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'push')
        self.request_ids = self.request.arguments.get('request', [])

        insert_queries = [
                InsertIgnore(db.push_pushcontents, ({'request': int(i), 'push': self.pushid}))
                for i in self.request_ids
        ]
        update_query = db.push_requests.update().where(
            db.push_requests.c.id.in_(self.request_ids)).values({'state': 'added'})
        request_query = db.push_requests.select().where(
            db.push_requests.c.id.in_(self.request_ids))

        db.execute_transaction_cb(insert_queries + [update_query, request_query], self.on_db_complete)

    # allow both GET and POST
    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        for req in db_results[-1]:
            if req['watchers']:
                user_string = '%s (%s)' % (req['user'], req['watchers'])
                users = [req['user']] + req['watchers'].split(',')
            else:
                user_string = req['user']
                users = [req['user']]
            msg = (
                """
                <p>
                    %(pushmaster)s has accepted request for %(user)s into a push:
                </p>
                <p>
                    <strong>%(user)s - %(title)s</strong><br />
                    <em>%(repo)s/%(branch)s</em>
                </p>
                <p>
                    Regards,<br />
                    PushManager
                </p>"""
                ) % pushmanager.core.util.EscapedDict({
                    'pushmaster': self.current_user,
                    'user': user_string,
                    'title': req['title'],
                    'repo': req['repo'],
                    'branch': req['branch'],
                })
            subject = "[push] %s - %s" % (user_string, req['title'])
            MailQueue.enqueue_user_email(users, msg, subject)
            msg = '{0} has accepted request "{1}" for {2} into a push:\n{3}/push?id={4}'.format(
                self.current_user,
                req['title'],
                user_string,
                self.get_base_url(),
                self.pushid,
            )
            XMPPQueue.enqueue_user_xmpp(users, msg) class APIServlet(RequestHandler):

    # Regexp part of the URLSpec, to be used in
    # tornado.web.Application initialization with APIServlet handler.
    regexp = r'/api(?:/([^/]+))?'

    def get(self, endpoint):
        if endpoint:
            func = '_api_%s' % endpoint.upper()
            if hasattr(self, func):
                return getattr(self, func)()
        return self.redirect("https://github.com/Yelp/pushmanager/wiki/Pushmanager-API")

    post = get

    def _xjson(self, data):
        self.set_header("Content-Type", "application/json")
        self.write(json.dumps(data))
        return self.finish()

    def _api_USERLIST(self):
        """Returns a JSON list of users who used PushManager for a request at least once."""
        query = db.push_requests.select(
            group_by=db.push_requests.c.user,
        )
        db.execute_cb(query, self._on_USERLIST_db_response)

    def _on_USERLIST_db_response(self, success, db_results):
        self.check_db_results(success, db_results)
        return self._xjson([r['user'] for r in db_results])

    def _api_REQUEST(self):
        """Returns a JSON representation of a push request."""
        request_id = util.get_int_arg(self.request, 'id')
        if not request_id:
            return self.send_error(404)

        query = db.push_requests.select(db.push_requests.c.id == request_id)
        db.execute_cb(query, self._on_REQUEST_db_response)

    def _on_REQUEST_db_response(self, success, db_results):
        self.check_db_results(success, db_results)

        request = db_results.first()
        if not request:
            return self.send_error(404)
        else:
            return self._xjson(util.request_to_jsonable(request))

    def _api_PUSH(self):
        """Returns a JSON representation of a push."""
        push_id = util.get_int_arg(self.request, 'id')
        if not push_id:
            return self.send_error(404)

        query = db.push_pushes.select(db.push_pushes.c.id == push_id)
        db.execute_cb(query, self._on_PUSH_db_response)

    def _on_PUSH_db_response(self, success, db_results):
        if not success:
            return self.send_error(500)

        push = db_results.first()
        if not push:
            return self.send_error(404)
        else:
            return self._xjson(util.push_to_jsonable(push))

    def _api_PUSHDATA(self):
        """Returns all the information on a push in JSON. This is the same data that is shown on the push page"""
        push_id = util.get_int_arg(self.request, 'id')
        if not push_id:
            return self.send_error(404)

        push_info_query = db.push_pushes.select(db.push_pushes.c.id == push_id)
        contents_query = db.push_requests.select(
            SA.and_(
                db.push_requests.c.id == db.push_pushcontents.c.request,
                db.push_pushcontents.c.push == push_id,
            ),
            order_by=(db.push_requests.c.user, db.push_requests.c.title),
        )
        available_query = db.push_requests.select(
            db.push_requests.c.state == 'requested',
        )
        db.execute_transaction_cb([push_info_query, contents_query, available_query], self._on_PUSHDATA_db_response)

    def _on_PUSHDATA_db_response(self, success, db_results):
        self.check_db_results(success, db_results)

        push_info, push_contents, available_requests = db_results
        push_info = push_info.first()
        if not push_info:
            return self.send_error(404)
        push_info = util.push_to_jsonable(push_info)

        available_requests = [util.request_to_jsonable(r) for r in available_requests.fetchall()]
        push_requests = {}
        for request in push_contents:
            request = util.request_to_jsonable(request)
            push_requests.setdefault(request['state'], []).append(request)
            push_requests.setdefault('all', []).append(request)

        return self._xjson([push_info, push_requests, available_requests])

    def _api_PUSHES(self):
        """Returns a JSON representation of pushes."""
        rpp = util.get_int_arg(self.request, 'rpp', 50)
        offset = util.get_int_arg(self.request, 'offset', 0)
        state = util.get_str_arg(self.request, 'state', '')
        user = util.get_str_arg(self.request, 'user', '')

        filters = []
        if state != '':
            filters.append(db.push_pushes.c.state == state)
        if user != '':
            filters.append(db.push_pushes.c.user == user)

        push_query = db.push_pushes.select(
            whereclause=SA.and_(*filters),
            order_by=db.push_pushes.c.modified.desc(),
        )

        pushes_count = push_query.alias('pushes_count').count()

        if offset > 0:
            push_query = push_query.offset(offset)
        if rpp > 0:
            push_query = push_query.limit(rpp)

        db.execute_transaction_cb([push_query, pushes_count, ], self._on_PUSHES_db_response)

    def _on_PUSHES_db_response(self, success, db_results):
        self.check_db_results(success, db_results)

        push_results, pushes_count = db_results

        def accepting_first(current, previous):
            # swap only if current push is accepting and previous push not accepting
            if current['state'] == 'accepting' and previous['state'] != 'accepting':
                return -1
            return 0

        push_results = sorted([util.push_to_jsonable(result) for result in push_results], cmp=accepting_first)
        return self._xjson([push_results, pushes_count.first()[0]])

    def _api_PUSHCONTENTS(self):
        """Returns a set of JSON representations of requests in a given push."""
        push_id = util.get_int_arg(self.request, 'id')
        if not push_id:
            return self.send_error(404)

        query = db.push_requests.select(SA.and_(
            db.push_requests.c.id == db.push_pushcontents.c.request,
            db.push_pushcontents.c.push == push_id,
        ))
        db.execute_cb(query, self._on_PUSHCONTENTS_db_response)

    def _on_PUSHCONTENTS_db_response(self, success, db_results):
        self.check_db_results(success, db_results)

        requests = [util.request_to_jsonable(request) for request in db_results]
        return self._xjson(requests)

    def _api_PUSHBYREQUEST(self):
        """Returns a JSON representation of a PUSH given a request id."""
        request_id = util.get_int_arg(self.request, 'id')
        if not request_id:
            return self.send_error(404)

        query = db.push_pushes.select(SA.and_(
            db.push_pushes.c.state != "discarded",
            db.push_pushcontents.c.push == db.push_pushes.c.id,
            db.push_pushcontents.c.request == request_id,
        ))
        db.execute_cb(query, self._on_PUSHBYREQUEST_db_response)

    def _on_PUSHBYREQUEST_db_response(self, success, db_results):
        self.check_db_results(success, db_results)

        push = db_results.first()
        return self._xjson(util.push_to_jsonable(push))

    def _api_PUSHITEMS(self):
        """Returns a JSON representation of a list of requests given a push id"""
        push_id = util.get_int_arg(self.request, 'push_id')
        if not push_id:
            return self.send_error(404)

        query = db.push_requests.select(
            SA.and_(
                db.push_requests.c.id == db.push_pushcontents.c.request,
                db.push_requests.c.state != 'pickme',
                db.push_pushcontents.c.push == push_id,
            ),
            order_by=(db.push_requests.c.user, db.push_requests.c.title),
        )
        db.execute_cb(query, self._on_PUSHITEMS_db_response)

    def _on_PUSHITEMS_db_response(self, success, db_results):
        self.check_db_results(success, db_results)
        return self._xjson([util.request_to_jsonable(request) for request in db_results])

    def _api_REQUESTSEARCH(self):
        """Returns a list of requests matching a the specified filter(s)."""
        filters = []

        # Tag constraint
        for tag in self.request.arguments.get('tag', []):
            filters.append(db.push_requests.c.tags.op('regexp')('[[:<:]]' + tag + '[[:>:]]'))

        # Timestamp constraint
        mbefore = util.get_int_arg(self.request, 'mbefore')
        mafter = util.get_int_arg(self.request, 'mafter')
        if mbefore:
            filters.append(db.push_requests.c.modified < mbefore)
        if mafter:
            filters.append(db.push_requests.c.modified > mafter)

        cbefore = util.get_int_arg(self.request, 'cbefore')
        cafter = util.get_int_arg(self.request, 'cafter')
        if cbefore:
            filters.append(db.push_requests.c.created < cbefore)
        if cafter:
            filters.append(db.push_requests.c.created > cafter)

        # State constraint
        states = self.request.arguments.get('state', [])
        if states:
            filters.append(db.push_requests.c.state.in_(states))

        # User constraint
        users = self.request.arguments.get('user', [])
        if users:
            filters.append(db.push_requests.c.user.in_(users))

        # Repository constraint
        repos = self.request.arguments.get('repo', [])
        if repos:
            filters.append(db.push_requests.c.repo.in_(repos))

        # Branch constraint
        branches = self.request.arguments.get('branch', [])
        if branches:
            filters.append(db.push_requests.c.branch.in_(branches))

        # Revision constraint
        revisions = self.request.arguments.get('rev', [])
        if revisions:
            filters.append(db.push_requests.c.revision.in_(revisions))

        # Review constraint
        reviews = self.request.arguments.get('review', [])
        if reviews:
            filters.append(db.push_requests.c.reviewid.in_(reviews))

        # Title constraint
        for title in self.request.arguments.get('title', []):
            filters.append(db.push_requests.c.title.like('%' + title + '%'))

        # Only allow searches with at least one constraint (to avoid
        # accidental dumps of the entire table)
        if not filters:
            return self.send_error(409)

        query = db.push_requests.select(SA.and_(*filters))
        query = query.order_by(db.push_requests.c.id.desc())

        limit = util.get_int_arg(self.request, 'limit')
        if limit > 0:
            limit = max(min(1000, limit), 1)
            query = query.limit(limit)

        db.execute_cb(query, self._on_REQUESTSEARCH_db_response)

    def _on_REQUESTSEARCH_db_response(self, success, db_results):
        if not success:
            return self.send_error(500)

        requests = [util.request_to_jsonable(request) for request in db_results]
        return self._xjson(requests) class CommentRequestServlet(RequestHandler):

    def post(self):
        if not self.current_user:
            return self.send_error(403)

        requestid = pushmanager.core.util.get_int_arg(self.request, 'id')
        comment = pushmanager.core.util.get_str_arg(self.request, 'comment')
        self.comment = comment
        if not comment:
            return self.send_error(500)

        update_query = db.push_requests.update().where(
            db.push_requests.c.id == requestid,
        ).values({
            'comments': SA.func.concat(
                db.push_requests.c.comments,
                '\n\n---\n\nComment from %s:\n\n' % self.current_user,
                comment,
            ),
        })
        select_query = db.push_requests.select().where(
            db.push_requests.c.id == requestid,
        )
        db.execute_transaction_cb([update_query, select_query], self.on_db_complete)

    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        if db_results:
            req = db_results[1].first()
            msg = (
                """
                <p>
                    %(pushmaster)s has commented on your request:
                </p>
                <p>
                    <strong>%(user)s - %(title)s</strong><br />
                    <em>%(repo)s/%(branch)s</em>
                </p>
                <pre>
%(comment)s
                </pre>
                <p>
                    Regards,<br />
                    PushManager
                </p>"""
            ) % pushmanager.core.util.EscapedDict({
                    'pushmaster': self.current_user,
                    'user': req['user'],
                    'title': req['title'],
                    'repo': req['repo'],
                    'branch': req['branch'],
                    'comment': self.comment,
                })
            subject = "[push comment] %s - %s" % (req['user'], req['title'])
            MailQueue.enqueue_user_email([req['user']], msg, subject)
            msg = '%(pushmaster)s has commented on your request "%(title)s":\n%(comment)s' % {
                    'pushmaster': self.current_user,
                    'title': req['title'],
                    'comment': self.comment,
                }
            XMPPQueue.enqueue_user_xmpp([req['user']], msg)
            newcomments = req[db.push_requests.c.comments]
            self.write(xhtml_escape(newcomments)) class DelayRequestServlet(RequestHandler):

    def post(self):
        if not self.current_user:
            return self.send_error(403)

        self.requestid = pushmanager.core.util.get_int_arg(self.request, 'id')
        update_query = db.push_requests.update().where(SA.and_(
            db.push_requests.c.id == self.requestid,
            db.push_requests.c.state.in_(('requested', 'pickme')),
        )).values({
            'state': 'delayed',
            })
        delete_query = db.push_pushcontents.delete().where(
            SA.exists([1], SA.and_(
                db.push_pushcontents.c.request == db.push_requests.c.id,
                db.push_requests.c.state == 'delayed',
            )))
        select_query = db.push_requests.select().where(
            db.push_requests.c.id == self.requestid,
        )
        db.execute_transaction_cb([update_query, delete_query, select_query], self.on_db_complete)

    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        _, _, req = db_results
        req = req.first()
        if req['state'] != 'delayed':
            # We didn't actually discard the record, for whatever reason
            return self.redirect("/requests?user=%s" % self.current_user)

        if req['watchers']:
            user_string = '%s (%s)' % (req['user'], req['watchers'])
            users = [req['user']] + req['watchers'].split(',')
        else:
            user_string = req['user']
            users = [req['user']]
        msg = (
            """
            <p>
                Request for %(user)s has been marked as delayed
                by %(pushmaster)s, and will not be accepted into
                pushes until you mark it as requested again:
            </p>
            <p>
                <strong>%(user)s - %(title)s</strong><br />
                <em>%(repo)s/%(branch)s</em>
            </p>
            <p>
                Regards,<br />
                PushManager
            </p>"""
            ) % pushmanager.core.util.EscapedDict({
                'pushmaster': self.current_user,
                'user': user_string,
                'title': req['title'],
                'repo': req['repo'],
                'branch': req['branch'],
            })
        subject = "[push] %s - %s" % (user_string, req['title'])
        MailQueue.enqueue_user_email(users, msg, subject)

        self.redirect("/requests?user=%s" % self.current_user) class DiscardPushServlet(RequestHandler):

    def _arg(self, key):
        return pushmanager.core.util.get_str_arg(self.request, key, '')

    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'id')
        push_query = db.push_pushes.update().where(db.push_pushes.c.id == self.pushid).values({
            'state': 'discarded',
            'modified': time.time(),
            })
        request_query_pickme = db.push_requests.update().where(
            SA.exists(
                [1],
                SA.and_(
                    db.push_pushcontents.c.push == self.pushid,
                    db.push_pushcontents.c.request == db.push_requests.c.id,
                    db.push_requests.c.state == 'pickme',
                )
            )).values({
                'state': 'requested',
            })
        delete_query = db.push_pushcontents.delete().where(
            SA.exists(
                [1],
                SA.and_(
                    db.push_pushcontents.c.push == self.pushid,
                    db.push_pushcontents.c.request == db.push_requests.c.id,
                    db.push_requests.c.state == 'requested',
                )
            ))
        request_query_all = db.push_requests.update().where(
            SA.exists(
                [1],
                SA.and_(
                    db.push_pushcontents.c.push == self.pushid,
                    db.push_pushcontents.c.request == db.push_requests.c.id,
                )
            )).values({
                'state': 'requested',
            })
        db.execute_transaction_cb(
            [push_query, request_query_pickme, delete_query, request_query_all],
            self.on_db_complete
        )

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results) class EditPushServlet(RequestHandler):

    def _arg(self, key):
        return pushmanager.core.util.get_str_arg(self.request, key, '')

    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'id')
        query = db.push_pushes.update().where(db.push_pushes.c.id == self.pushid).values(**{
            'title': self._arg('push-title'),
            'user': self.current_user,
            'branch': self._arg('push-branch'),
            'stageenv': self._arg('push-stageenv'),
            'revision': "0"*40,
            'modified': time.time(),
            })
        db.execute_cb(
            query,
            lambda _, __: self.redirect("/push?id=%d" % self.pushid)
        ) class LivePushServlet(RequestHandler):

    def _arg(self, key):
        return pushmanager.core.util.get_str_arg(self.request, key, '')

    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'id')
        push_query = db.push_pushes.update().where(db.push_pushes.c.id == self.pushid).values({
            'state': 'live',
            'modified': time.time(),
            })
        request_query = db.push_requests.update().where(SA.and_(
            db.push_requests.c.state == 'blessed',
            SA.exists(
                [1],
                SA.and_(
                    db.push_pushcontents.c.push == self.pushid,
                    db.push_pushcontents.c.request == db.push_requests.c.id,
                )
            ))).values({
                'state': 'live',
                'modified': time.time(),
            })
        reset_query = db.push_requests.update().where(
            SA.exists(
                [1],
                SA.and_(
                    db.push_requests.c.state == 'pickme',
                    db.push_pushcontents.c.push == self.pushid,
                    db.push_pushcontents.c.request == db.push_requests.c.id,
                )
            )).values({
                'state': 'requested',
            })
        delete_query = db.push_pushcontents.delete().where(
            SA.exists([1], SA.and_(
                db.push_pushcontents.c.push == self.pushid,
                db.push_pushcontents.c.request == db.push_requests.c.id,
                db.push_requests.c.state == 'requested',
            )))
        live_query = db.push_requests.select().where(
            SA.and_(db.push_requests.c.state == 'live',
                    db.push_pushcontents.c.push == self.pushid,
                    db.push_pushcontents.c.request == db.push_requests.c.id)
            )
        db.execute_transaction_cb(
            [push_query, request_query, reset_query, delete_query, live_query],
            self.on_db_complete,
        )

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        _, _, _, _, live_requests = db_results
        for req in live_requests:
            if req['reviewid']:
                review_id = int(req['reviewid'])
                RBQueue.enqueue_review(review_id)

            if req['watchers']:
                user_string = '%s (%s)' % (req['user'], req['watchers'])
                users = [req['user']] + req['watchers'].split(',')
            else:
                user_string = req['user']
                users = [req['user']]

            msg = (
                """
                <p>
                    %(pushmaster)s has certified request for %(user)s as stable in production:
                </p>
                <p>
                    <strong>%(user)s - %(title)s</strong><br />
                    <em>%(repo)s/%(branch)s</em>
                </p>
                <p>
                    Regards,<br />
                    PushManager
                </p>"""
                ) % pushmanager.core.util.EscapedDict({
                    'pushmaster': self.current_user,
                    'user': user_string,
                    'title': req['title'],
                    'repo': req['repo'],
                    'branch': req['branch'],
                })
            subject = "[push] %s - %s" % (user_string, req['title'])
            MailQueue.enqueue_user_email(users, msg, subject) class MsgServlet(RequestHandler):
    people = []

    def post(self):
        if not self.current_user:
            return self.send_error(403)

        push_id = self.request.arguments.get('id', [None])[0]
        if not push_id:
            return self.send_error(404)
        contents_query = self.generate_pushcontent_query(push_id)
        db.execute_cb(contents_query, self.get_push_request_users)
        people = self.people
        message = self.request.arguments.get('message', [None])[0]
        if not message:
            return self.send_error(500)

        irc_nick = Settings['irc']['nickname'].format(
            pushmaster=self.current_user
        )

        if not people:
            irc_message = u'[[pushmaster {0}]] {1}'.format(
                self.current_user,
                message,
            )

            subprocess.call([
                '/nail/sys/bin/nodebot',
                '-i',
                irc_nick,
                Settings['irc']['channel'],
                irc_message
            ])
            return

        send_people_msg_in_groups(
            people,
            message,
            irc_nick,
            Settings['irc']['channel'],
            person_per_group=5,
            prefix_msg='[[pushmaster %s]]' % self.current_user
        )

    def generate_pushcontent_query(self, push_id):
        state = self.request.arguments.get('state', [None])[0]
        contents_query = ''
        if state == 'requested':
            contents_query = db.push_requests.select(
                    db.push_pushcontents.c.push == push_id
            )
        else:
            contents_query = db.push_requests.select(
                SA.and_(
                    db.push_requests.c.id == db.push_pushcontents.c.request,
                    db.push_pushcontents.c.push == push_id,
                ),
                order_by=(db.push_requests.c.user, db.push_requests.c.title)
            )
        return contents_query

    def get_push_request_users(self, success, db_results):
        request_list = self.filter_request_by_state(success, db_results)
        people = []
        for request in request_list:
            people.append((request['user']))
            for watcher in request['watchers'].split(','):
                people.append((watcher))
        people = list(set(people))
        people = filter(None, people)
        self.people = people

    def filter_request_by_state(self, success, db_results):
        user_requests = []
        state = self.request.arguments.get('state', [None])[0]
        for request in db_results:
            if (state == 'all' and request['state'] != 'pickme') or (
                    state == request['state']):
                user_requests.append(request)
        return user_requests class NewPushServlet(RequestHandler):

    def _arg(self, key):
        return pushmanager.core.util.get_str_arg(self.request, key, '')

    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.pushtype = self._arg('push-type')
        insert_query = db.push_pushes.insert({
            'title': self._arg('push-title'),
            'user': self.current_user,
            'branch': self._arg('push-branch').strip(),
            'revision': "0"*40,
            'created': time.time(),
            'modified': time.time(),
            'state': 'accepting',
            'pushtype': self.pushtype,
            })
        select_query = db.push_requests.select().where(
            db.push_requests.c.state == 'requested',
        )
        db.execute_transaction_cb([insert_query, select_query], self.on_db_complete)

    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        insert_results, select_results = db_results
        pushurl = '/push?id=%s' % insert_results.lastrowid
        pushmanager_url = self.get_base_url() + pushurl

        def users_involved(request):
            if request['watchers']:
                return [request['user']] + request['watchers'].split(',')
            return [request['user']]

        if self.pushtype in ('private', 'morning'):
            people = None
        elif self.pushtype == 'urgent':
            people = set(user for x in select_results for user in users_involved(x) if 'urgent' in x['tags'].split(','))
        else:
            people = set(user for x in select_results for user in users_involved(x))

        send_notifications(people, self.pushtype, pushmanager_url)

        return self.redirect(pushurl) class NewRequestServlet(RequestHandler):

    def _arg(self, key):
        return pushmanager.core.util.get_str_arg(self.request, key, '')

    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.requestid = self._arg('request-id')
        self.tag_list = [
            x
            for x in TAGS_RE.findall(self._arg('request-tags'))
            if x and x not in CONFLICT_TAGS
        ]

        reviewid = self._arg('request-review')
        if reviewid:
            try:
                reviewid = int(reviewid)
            except (ValueError, TypeError):
                return self.send_error(500)

        watchers = ','.join(map(str.strip, self._arg('request-watchers').split(',')))

        if self.requestid != '':
            self.requestid = int(self.requestid)

            updated_values = {
                    'title': self._arg('request-title'),
                    'tags': ','.join(self.tag_list),
                    'reviewid': reviewid or None,
                    'repo': self._arg('request-repo').strip(),
                    'branch': self._arg('request-branch').strip(),
                    'comments': self._arg('request-comments'),
                    'description': self._arg('request-description'),
                    'watchers': watchers,
                    'modified': time.time(),
                    'revision': '0'*40,
            }

            if len(self._arg('request-takeover')):
                updated_values.update({'user': self.current_user})
                self.request_user = self.current_user
            else:
                self.request_user = self._arg('request-user')

            query = db.push_requests.update().where(
                    db.push_requests.c.id == self.requestid
                ).values(updated_values)
        else:
            query = db.push_requests.insert({
                'title': self._arg('request-title'),
                'user': self.current_user,
                'tags': ','.join(self.tag_list),
                'reviewid': self._arg('request-review') or None,
                'repo': self._arg('request-repo').strip(),
                'branch': self._arg('request-branch').strip(),
                'comments': self._arg('request-comments'),
                'description': self._arg('request-description'),
                'watchers': watchers,
                'created': time.time(),
                'modified': time.time(),
                'state': 'requested',
                'revision': '0'*40,
                })
            self.request_user = self.current_user

        db.execute_cb(query, self.on_request_upsert_complete)

    def on_request_upsert_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        if not self.requestid:
            self.requestid = db_results.lastrowid

        query = db.push_checklist.select().where(db.push_checklist.c.request == self.requestid)
        db.execute_cb(query, self.on_existing_checklist_retrieved)

    def on_existing_checklist_retrieved(self, success, db_results):
        if not success or not db_results:
            # We should have the new request in db by this time.
            return self.send_error(500)

        existing_checklist_types = set(x['type'] for x in db_results.fetchall())
        queries = []

        necessary_checklist_types = set()

        canonical_tag_name = {'search': 'search-backend'}
        for cl_type in ['pushplans', 'search', 'hoods']:
            tag = canonical_tag_name.get(cl_type, cl_type)
            if tag in self.tag_list:
                necessary_checklist_types.add(cl_type)
                necessary_checklist_types.add('{0}-cleanup'.format(cl_type))

        types_to_add = necessary_checklist_types - existing_checklist_types
        types_to_remove = existing_checklist_types - necessary_checklist_types

        for type_ in types_to_add:
            for target in checklist_reminders[type_].keys():
                queries.append(db.push_checklist.insert().values(
                    {'request': self.requestid, 'type': type_, 'target': target}
                ))

        if types_to_remove:
            queries.append(db.push_checklist.delete().where(SA.and_(
                db.push_checklist.c.request == self.requestid,
                db.push_checklist.c.type.in_(types_to_remove),
            )))

        db.execute_transaction_cb(queries, self.on_checklist_upsert_complete)

    def on_checklist_upsert_complete(self, success, db_results):
        if not success:
            return self.send_error(500)

        if self.requestid:
            GitQueue.enqueue_request(
                GitTaskAction.VERIFY_BRANCH,
                self.requestid,
                pushmanager_url=self.get_base_url()
            )

            # Check if the request is already pickme'd for a push, and if
            # so also enqueue it to be checked for conflicts.
            request_push_id = GitQueue._get_push_for_request(self.requestid)
            if request_push_id:
                GitQueue.enqueue_request(
                    GitTaskAction.TEST_PICKME_CONFLICT,
                    self.requestid,
                    pushmanager_url=self.get_base_url()
                )

        return self.redirect("/requests?user=%s" % self.request_user) class PickMeRequestServlet(RequestHandler):

    def post(self):
        if not self.current_user:
            return self.send_error(403)

        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'push')
        self.request_ids = self.request.arguments.get('request', [])

        db.execute_cb(db.push_pushes.select().where(db.push_pushes.c.id == self.pushid), self.on_push_select)

    def on_push_select(self, success, db_results):
        if not success or not db_results:
            return self.send_error(500)

        pushrow = db_results.fetchone()
        if not pushrow:
            return self.send_error(500)

        if pushrow[db.push_pushes.c.state] != 'accepting':
            return self.send_error(403)

        insert_queries = [
            db.push_pushcontents.insert({
                'request': int(i),
                'push': self.pushid
            }) for i in self.request_ids
        ]
        update_query = db.push_requests.update().where(SA.and_(
                db.push_requests.c.id.in_(self.request_ids),
                db.push_requests.c.state == 'requested',
            )).values({'state': 'pickme'})
        request_query = db.push_requests.select().where(
            db.push_requests.c.id.in_(self.request_ids))

        condition_query = SA.select(
            [db.push_pushes, db.push_pushcontents],
            SA.and_(
                db.push_pushcontents.c.request.in_(self.request_ids),
                db.push_pushes.c.id == db.push_pushcontents.c.push,
                db.push_pushes.c.state != 'discarded'
            )
        )

        def condition_fn(db_results):
            return db_results.fetchall() == []

        db.execute_transaction_cb(
            insert_queries + [update_query, request_query],
            self.on_db_complete,
            condition=(condition_query, condition_fn)
        )

    # allow both GET and POST
    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)
        for request_id in self.request_ids:
            GitQueue.enqueue_request(
                GitTaskAction.TEST_PICKME_CONFLICT,
                request_id,
                pushmanager_url=self.get_base_url()
            ) class UnpickMeRequestServlet(RequestHandler):

    def post(self):
        if not self.current_user:
            return self.send_error(403)

        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'push')
        self.request_id = self.request.arguments.get('request', [None])[0]
        delete_query = db.push_pushcontents.delete().where(
            SA.exists([1], SA.and_(
                db.push_pushcontents.c.request == self.request_id,
                db.push_pushcontents.c.push == self.pushid,
                db.push_requests.c.id == db.push_pushcontents.c.request,
                db.push_requests.c.state == 'pickme',
            )))
        update_query = db.push_requests.update().where(SA.and_(
                db.push_requests.c.id == self.request_id,
                db.push_requests.c.state == 'pickme',
            )).values({'state': 'requested'})

        db.execute_transaction_cb([delete_query, update_query], self.on_db_complete)

    # allow both GET and POST
    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)
        # Re-check pickmes that are marked as conflicting, in case this was the pickme
        # that they conflicted against.
        GitQueue.enqueue_request(
            GitTaskAction.TEST_CONFLICTING_PICKMES,
            self.pushid,
            pushmanager_url=self.get_base_url()
        ) class PushServlet(RequestHandler):

    @tornado.web.asynchronous
    @tornado.web.authenticated
    @tornado.gen.engine
    def get(self):
        pushid = pushmanager.core.util.get_int_arg(self.request, 'id')
        override = pushmanager.core.util.get_int_arg(self.request, 'override')
        response = yield tornado.gen.Task(
                        self.async_api_call,
                        "pushdata",
                        {"id": pushid}
                    )

        push_info, push_requests, available_requests = self.get_api_results(response)

        if not push_info['stageenv']:
            push_info['stageenv'] = '(to be determined)'

        push_survey_url = Settings.get('push_survey_url', None)

        self.render(
            "push.html",
            page_title=push_info['title'],
            pushid=pushid,
            push_info=push_info,
            push_contents=push_requests,
            push_survey_url=push_survey_url,
            available_requests=available_requests,
            fullrepo=_repo,
            override=override,
            pickme_orders=['urgent', 'no-verify', Settings['tests_tag']['tag']]
        ) class PushesServlet(RequestHandler):

    @tornado.web.authenticated
    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        pushes_per_page = pushmanager.core.util.get_int_arg(self.request, 'rpp', 50)
        offset = pushmanager.core.util.get_int_arg(self.request, 'offset', 0)
        state = pushmanager.core.util.get_str_arg(self.request, 'state', '')
        push_user = pushmanager.core.util.get_str_arg(self.request, 'user', '')
        response = yield tornado.gen.Task(
            self.async_api_call,
            'pushes',
            {
                'rpp': pushes_per_page,
                'offset': offset,
                'state': state,
                'user': push_user,
            }
        )

        results = self.get_api_results(response)
        if not results:
            self.finish()

        pushes, pushes_count = results
        self.render(
            "pushes.html",
            page_title="Pushes",
            pushes=pushes,
            offset=offset,
            rpp=pushes_per_page,
            state=state,
            push_user=push_user,
            pushes_count=pushes_count,
        ) class PushItemsServlet(RequestHandler):

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        pushid = pushmanager.core.util.get_int_arg(self.request, 'push', None)

        response = yield tornado.gen.Task(
                        self.async_api_call,
                        "pushitems",
                        {"push_id": pushid}
                    )

        results = self.get_api_results(response)
        self.render("pushitems.html", requests=results) class RemoveRequestServlet(RequestHandler):

    @tornado.web.asynchronous
    def post(self):
        if not self.current_user:
            return self.send_error(403)
        self.pushid = pushmanager.core.util.get_int_arg(self.request, 'push')
        self.requestid = self.request.arguments.get('request', [])
        select_query = db.push_requests.select().where(
            db.push_requests.c.id.in_(self.requestid)
        )
        update_query = db.push_requests.update().where(SA.and_(
            db.push_requests.c.id.in_(self.requestid),
            SA.exists([1], SA.and_(
                db.push_pushcontents.c.push == self.pushid,
                db.push_pushcontents.c.request == db.push_requests.c.id,
            )),
        )).values({'state': 'requested'})
        delete_query = db.push_pushcontents.delete(SA.and_(
            db.push_pushcontents.c.push == self.pushid,
            db.push_pushcontents.c.request.in_(self.requestid),
        ))
        db.execute_transaction_cb([select_query, update_query, delete_query], self.on_db_complete)

    # allow both GET and POST
    get = post

    def on_db_complete(self, success, db_results):
        self.check_db_results(success, db_results)

        reqs, _, _ = db_results
        removal_dicts = []
        for req in reqs:
            if req['watchers']:
                user_string = '%s (%s)' % (req['user'], req['watchers'])
                users = [req['user']] + req['watchers'].split(',')
            else:
                user_string = req['user']
                users = [req['user']]
            msg = (
                """
                <p>
                    %(pushmaster)s has removed request for %(user)s from a push:
                </p>
                <p>
                    <strong>%(user)s - %(title)s</strong><br />
                    <em>%(repo)s/%(branch)s</em>
                </p>
                <p>
                    Regards,<br />
                    PushManager
                </p>"""
                ) % pushmanager.core.util.EscapedDict({
                    'pushmaster': self.current_user,
                    'user': user_string,
                    'title': req['title'],
                    'repo': req['repo'],
                    'branch': req['branch'],
                })
            subject = "[push] %s - %s" % (user_string, req['title'])
            MailQueue.enqueue_user_email(users, msg, subject)
            msg = '%(pushmaster)s has removed request "%(title)s" for %(user)s from a push' % {
                    'pushmaster': self.current_user,
                    'title': req['title'],
                    'pushid': self.pushid,
                    'user': user_string,
                }
            XMPPQueue.enqueue_user_xmpp(users, msg)
            removal_dicts.append({
                'request': req['id'],
                'push': self.pushid,
                'reason': 'removal after %s' % req['state'],
                'pushmaster': self._current_user,
                'timestamp': int(time.time()),
            })

        removal_queries = [db.push_removals.insert(removal) for removal in removal_dicts]
        db.execute_transaction_cb(removal_queries, self.on_db_insert_complete)

    def on_db_insert_complete(self, success, db_results):
        if not success:
            self.send_error(500)
        self.finish() class RequestServlet(RequestHandler):

    @tornado.web.asynchronous
    @tornado.web.authenticated
    @tornado.gen.engine
    def get(self):
        request_id = pushmanager.core.util.get_int_arg(self.request, 'id')
        if not request_id:
            self.send_error(404)

        response = yield tornado.gen.Task(
                        self.async_api_call,
                        "request",
                        {'id': request_id}
                    )

        req = self.get_api_results(response)
        if not req:
            self.send_error()

        self.render("request.html", page_title="Request #%d" % request_id, req=req) class RequestsServlet(RequestHandler):

    @tornado.web.asynchronous
    @tornado.web.authenticated
    @tornado.gen.engine
    def get(self):
        username = pushmanager.core.util.get_str_arg(self.request, 'user')
        limit_count = pushmanager.core.util.get_int_arg(self.request, 'max')
        arguments = {'limit': limit_count}

        if username:
            arguments['user'] = username
            page_title = 'Requests from %s' % username
            show_count = False
        else:
            arguments['limit'] = 0
            arguments['state'] = 'requested'
            page_title = 'Open Requests'
            show_count = True

        response = yield tornado.gen.Task(
                        self.async_api_call,
                        "requestsearch",
                        arguments
                    )

        requests = self.get_api_results(response)
        self.render("requests.html", requests=requests, page_title=page_title, show_count=show_count) class TestTagServlet(RequestHandler):

    def _arg(self, key):
        return pushmanager.core.util.get_str_arg(self.request, key, '')

    @tornado.web.asynchronous
    def get(self):
        request_id = pushmanager.core.util.get_int_arg(self.request, 'id')
        resp = {}
        if not request_id:
            self.set_status(404)
        else:
            request = GitQueue._get_request(request_id)
            resp = self._gen_test_tag_resp(request)
        self.finish(resp)

    @classmethod
    def _gen_test_tag_resp(cls, request):
        response = {}

        if 'tests_tag' in Settings and Settings['tests_tag']['tag'] in request['tags']:
            response['tag'] = Settings['tests_tag']['tag']
            try:
                api_url = Settings['tests_tag']['tag_api_endpoint'].replace('%SHA%', request['revision'])
                api_body = Settings['tests_tag']['tag_api_body'].replace('%SHA%', request['revision'])
                api_resp = urllib2.urlopen(api_url, api_body)
                response['tag'] = xhtml_escape(json.loads(api_resp.read())['tag'])
            except Exception as e:
                response['tag'] += ": ERROR connecting to server"
                logging.error(e)

            response['url'] = ''
            if 'url_api_endpoint' in Settings['tests_tag']:
                try:
                    result_api_url = Settings['tests_tag']['url_api_endpoint'].replace('%SHA%', request['revision'])
                    result_api_url = result_api_url.replace('%BRANCH%', request['branch'])
                    result_api_body = Settings['tests_tag']['url_api_body'].replace('%SHA%', request['revision'])
                    result_api_body = result_api_body.replace('%BRANCH%', request['branch'])
                    resp = urllib2.urlopen(result_api_url, result_api_body)
                    result_id = url_escape(json.loads(resp.read())['id'])
                    if result_id != '':
                        response['url'] = Settings['tests_tag']['url_tmpl'].replace(
                            '%ID%',
                            result_id
                        ).replace(
                            '%SHA%',
                            request['revision']
                        )
                        response['url'] = response['url'].replace('%BRANCH%', request['branch'])
                except Exception as e:
                    logging.warning(e)
                    logging.warning(
                        "Couldn't load results for results test URL from %s with body %s" % (
                            Settings['tests_tag']['url_api_endpoint'].replace('%SHA%', request['revision']),
                            Settings['tests_tag']['url_api_body'].replace('%SHA%', request['revision'])
                        )
                    )
        return response class SummaryForBranchServlet(RequestHandler):

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        userbranch = pushmanager.core.util.get_str_arg(self.request, 'userbranch')
        user, branch = userbranch.split('/', 1)
        response = yield tornado.gen.Task(
                        self.async_api_call,
                        "requestsearch",
                        {'repo': user, 'branch': branch}
                    )

        requests = self.get_api_results(response)

        if requests:
            req = sorted(requests, key=operator.itemgetter("id"))[0]
            self.write(req['description'] or req['title'])
            if req['reviewid']:
                self.write("\n\nReview: https://%s/r/%s" % (Settings['reviewboard']['servername'], req['reviewid']))
            self.finish()
        else:
            self.send_error(404)
import os
import pushmanager.core.db as db
import tornado.httpserver
import tornado.web
import pushmanager.ui_methods as ui_methods
import pushmanager.ui_modules as ui_modules
from pushmanager.core import pid
from pushmanager.core.application import Application
from pushmanager.core.git import GitQueue
from pushmanager.core.mail import MailQueue
from pushmanager.core.rb import RBQueue
from pushmanager.core.settings import Settings
from pushmanager.core.util import get_servlet_urlspec
from pushmanager.core.xmppclient import XMPPQueue
from pushmanager.handlers import CheckSitesBookmarkletHandler
from pushmanager.handlers import CreateRequestBookmarkletHandler
from pushmanager.handlers import LoginHandler
from pushmanager.handlers import LogoutHandler
from pushmanager.handlers import NullRequestHandler
from pushmanager.handlers import RedirHandler
from pushmanager.handlers import SamlACSHandler
from pushmanager.servlets.addrequest import AddRequestServlet
from pushmanager.servlets.api import APIServlet
from pushmanager.servlets.blesspush import BlessPushServlet
from pushmanager.servlets.checklist import ChecklistServlet
from pushmanager.servlets.checklist import ChecklistToggleServlet
from pushmanager.servlets.commentrequest import CommentRequestServlet
from pushmanager.servlets.conflictcheck import ConflictCheckServlet
from pushmanager.servlets.delayrequest import DelayRequestServlet
from pushmanager.servlets.deploypush import DeployPushServlet
from pushmanager.servlets.discardpush import DiscardPushServlet
from pushmanager.servlets.discardrequest import DiscardRequestServlet
from pushmanager.servlets.editpush import EditPushServlet
from pushmanager.servlets.livepush import LivePushServlet
from pushmanager.servlets.msg import MsgServlet
from pushmanager.servlets.newpush import NewPushServlet
from pushmanager.servlets.newrequest import NewRequestServlet
from pushmanager.servlets.pickmerequest import PickMeRequestServlet
from pushmanager.servlets.pickmerequest import UnpickMeRequestServlet
from pushmanager.servlets.pingme import PingMeServlet
from pushmanager.servlets.push import PushServlet
from pushmanager.servlets.pushbyrequest import PushByRequestServlet
from pushmanager.servlets.pushes import PushesServlet
from pushmanager.servlets.pushitems import PushItemsServlet
from pushmanager.servlets.removerequest import RemoveRequestServlet
from pushmanager.servlets.request import RequestServlet
from pushmanager.servlets.requests import RequestsServlet
from pushmanager.servlets.smartdest import SmartDestServlet
from pushmanager.servlets.testtag import TestTagServlet
from pushmanager.servlets.summaryforbranch import SummaryForBranchServlet
from pushmanager.servlets.undelayrequest import UndelayRequestServlet
from pushmanager.servlets.userlist import UserListServlet
from pushmanager.servlets.verifyrequest import VerifyRequestServlet
#!/usr/bin/env python



# Servlet dispatch rules
def get_url_specs():
    url_specs = [
        (r'/', SmartDestServlet),
        (r'favicon.*', NullRequestHandler),
        (CreateRequestBookmarkletHandler.url, CreateRequestBookmarkletHandler),
        (CheckSitesBookmarkletHandler.url, CheckSitesBookmarkletHandler),
        (r'/login', LoginHandler),
        (r'/logout', LogoutHandler),
        (r'/acs', SamlACSHandler),
    ]
    for servlet in (APIServlet,
                    ChecklistServlet,
                    ChecklistToggleServlet,
                    ConflictCheckServlet,
                    RequestServlet,
                    RequestsServlet,
                    NewRequestServlet,
                    PickMeRequestServlet,
                    UnpickMeRequestServlet,
                    AddRequestServlet,
                    RemoveRequestServlet,
                    VerifyRequestServlet,
                    DiscardRequestServlet,
                    DelayRequestServlet,
                    UndelayRequestServlet,
                    CommentRequestServlet,
                    PingMeServlet,
                    PushServlet,
                    PushesServlet,
                    EditPushServlet,
                    DiscardPushServlet,
                    DeployPushServlet,
                    BlessPushServlet,
                    LivePushServlet,
                    NewPushServlet,
                    PushItemsServlet,
                    PushByRequestServlet,
                    UserListServlet,
                    SummaryForBranchServlet,
                    MsgServlet,
                    TestTagServlet):
        url_specs.append(get_servlet_urlspec(servlet))
    return url_specs


class PushManagerApp(Application):
    name = "main"

    def __init__(self):
        Application.__init__(self)
        self.redir_port = Settings['main_app']['redir_port']
        self.redir_app = tornado.web.Application(
            [
                (r'/(.*)', RedirHandler),
            ],
        )
        self.main_app = tornado.web.Application(
            get_url_specs(),
            # Server settings
            static_path=os.path.join(os.path.dirname(__file__), "static"),
            template_path=os.path.join(os.path.dirname(__file__), "templates"),
            login_url="/login",
            cookie_secret=Settings['cookie_secret'],
            ui_modules=ui_modules,
            ui_methods=ui_methods,
            autoescape=None,
        )

    def start_services(self):
        # HTTPS server
        sockets = tornado.netutil.bind_sockets(self.port, address=Settings['main_app']['servername'])
        redir_sockets = tornado.netutil.bind_sockets(self.redir_port, address=Settings['main_app']['servername'])

        # Start the mail, git, reviewboard and XMPP queue handlers
        worker_pids = []

        worker_pids.extend(MailQueue.start_worker())