예제 #1
0
    def password(self, new):
        table = self._table
        if len(new) < 8:
            raise WakaError('Passwords should be at least eight characters!')

        new = misc.hide_critical_data(new, config.SECRET)

        self._update_db(password=new)
        self._password = new
예제 #2
0
    def make_admin_report_panel(self, sortby='date', order='desc'):
        sortby_type = sortby
        sortby_dir = order

        session = model.Session()
        table = model.report
        sql = table.select()

        # Enforce limited moderation reign.
        if self.user.account == staff.MODERATOR:
            sql = sql.where(table.c.board.in_(self.user.reign))

        # Determine order.
        if sortby_type in ('board', 'postnum', 'date'):
            try:
                column = getattr(table.c, sortby_type)
            except AttributeError:
                raise WakaError('Sort-by column is absent from table.')
            sort = column.desc
            if sortby_dir == 'asc':
                sort = column.asc
            sql = sql.order_by(sort(), table.c.date.desc())
        else:
            sql = sql.order_by(table.c.date.desc())

        # Paginate.
        res = model.Page(sql, self.page, self.perpage)

        # Hidden input fields.
        inputs = [{
            'name': 'task',
            'value': 'reports'
        }, {
            'name': 'order',
            'value': sortby_dir
        }, {
            'name': 'sortby',
            'value': sortby_type
        }]

        rooturl = misc.make_script_url(task='reports',
                                       sortby=sortby_type,
                                       order=sortby_dir,
                                       _amp=True)

        Template.__init__(self,
                          'report_panel_template',
                          reports=res.rows,
                          sortby=sortby_type,
                          order=sortby_dir,
                          number_of_pages=res.total_pages,
                          rowcount=res.total_entries,
                          inputs=inputs,
                          rooturl=rooturl)
예제 #3
0
def global_cache_rebuild_proxy(task_data):
    if task_data.user.account != staff.ADMIN:
        raise WakaError(strings.INUSUFFICENTPRIVLEDGES)
    Popen([
        sys.executable, sys.argv[0], 'rebuild_global_cache',
        local.environ['DOCUMENT_ROOT'], local.environ['SCRIPT_NAME'],
        local.environ['SERVER_NAME']
    ])
    referer = local.environ['HTTP_REFERER']
    task_data.contents.append(referer)
    return util.make_http_forward(referer, config.ALTERNATE_REDIRECT)
예제 #4
0
def update_spam_file(task_data, spam):
    if task_data.user.account == staff.MODERATOR:
        raise WakaError(strings.INUSUFFICENTPRIVLEDGES)

    # Dump all contents to first spam file.
    with open(config.SPAM_FILES[0], 'w') as f:
        f.write(spam)

    board = local.environ['waka.board']
    forward_url = misc.make_script_url(task='spam', board=board.name)

    return util.make_http_forward(forward_url, config.ALTERNATE_REDIRECT)
예제 #5
0
def get_action_name(action_to_view, debug=0):
    try:
        name = ACTION_TRANSLATION[action_to_view]['name']
        content = ACTION_TRANSLATION[action_to_view]['content']
    except KeyError:
        raise WakaError('Missing action key or unknown action key.')

    if not debug:
        return name
    elif debug == 1:
        return (name, content)
    else:
        return content
예제 #6
0
def edit_staff_proxy(admin,
                     mpass,
                     username,
                     newpassword=None,
                     newclass=None,
                     originalpassword='',
                     reign=None,
                     disable=None):

    user = staff.check_password(admin)

    if user.username == username:
        if misc.hide_critical_data(originalpassword, config.SECRET) \
           != user.password:
            raise WakaError(strings.WRONGPASS)
        newclass = None
        reign = None
    elif user.account == staff.ADMIN:
        edited_user = staff.StaffMember.get(username)
        if edited_user.account == staff.ADMIN and mpass != config.ADMIN_PASS:
            raise WakaError('Incorrect management password.')
    else:
        raise WakaError(strings.INSUFFICIENTPRIVILEGES)

    staff.edit_staff(username,
                     clear_pass=newpassword,
                     new_class=newclass,
                     reign=reign,
                     disable=disable)

    board = local.environ['waka.board']

    forward_task = 'admin' if user.username == username else 'staff'

    return make_http_forward(
        misc.make_script_url(task=forward_task, board=board.name),
        config.ALTERNATE_REDIRECT)
예제 #7
0
def task_editpost(environ, start_response):
    request = environ['werkzeug.request']
    board = environ['waka.board']

    if request.method != 'POST':
        raise WakaError("POST only accepted")

    admin = get_cookie_from_request(request, 'wakaadmin')

    wakapost = WakaPost.from_request(request)

    if wakapost.admin_post:
        return StaffAction(admin, 'admin_edit', wakapost=wakapost,
                           board=board).execute()
    else:
        return board.edit_stuff(wakapost)
예제 #8
0
def add_proxy_entry(task_data, type, ip, timestamp):
    session = model.Session()
    table = model.proxy

    if not misc.validate_ip(ip):
        raise WakaError(strings.BADIP)

    age = config.PROXY_WHITE_AGE if type == 'white' else config.PROXY_BLACK_AGE
    timestamp = int(timestamp or '0') - age + time.time()
    date = misc.make_date(time.time(), style=config.DATE_STYLE)

    query = table.delete().where(table.c.ip == ip)
    session.execute(query)

    query = table.insert().values(type=type,
                                  ip=ip,
                                  timestamp=timestamp,
                                  date=date)
    session.execute(query)

    board = local.environ['waka.board']
    forward_url = misc.make_script_url(task='proxy', board=board.name)

    return util.make_http_forward(forward_url, config.ALTERNATE_REDIRECT)
예제 #9
0
    def make_admin_activity_panel(self,
                                  view='',
                                  user_to_view=None,
                                  action_to_view=None,
                                  ip_to_view=None,
                                  post_to_view=None,
                                  sortby_name='date',
                                  sortby_dir='desc'):

        board = self.board

        template_view = 'staff_activity_unfiltered'
        action_name = action_content = ''

        table = model.activity
        account_table = model.account

        dual_table_select = [
            account_table.c.username, account_table.c.account,
            account_table.c.disabled, table.c.action, table.c.info,
            table.c.date, table.c.ip
        ]
        sql = select(dual_table_select,
                     from_obj=[
                         table.join(
                             account_table,
                             table.c.username == model.account.c.username)
                     ])

        rooturl_args = dict(task='stafflog',
                            board=board.name,
                            view=view,
                            sortby=sortby_name,
                            order=sortby_dir,
                            _amp=True)

        if view == 'user':
            if not user_to_view:
                raise WakaError('Please select a user to view.')
            template_view = 'staff_activity_by_user'
            sql = sql.where(table.c.username == user_to_view)
            rooturl_args['usertoview'] = user_to_view

        elif view == 'action':
            if not action_to_view:
                raise WakaError('Please select an action to view.')
            template_view = 'staff_activity_by_actions'
            (action_name, action_content) \
                = staff_tasks.get_action_name(action_to_view, 1)
            sql = sql.where(table.c.action == action_to_view)
            rooturl_args['actiontoview'] = action_to_view

        elif view == 'ip':
            if not ip_to_view:
                raise WakaError('Please specify an IP address to view.')
            template_view = 'staff_activity_by_ip_address'
            sql = sql.where(table.c.info.like('%' + ip_to_view + '%'))
            rooturl_args['iptoview'] = ip_to_view

        elif view == 'post':
            if not post_to_view:
                raise WakaError('Post key missing.')
            template_view = 'staff_activity_by_post'
            sql = sql.where(table.c.info.like('%' + post_to_view + '%'))
            rooturl_args['posttoview'] = post_to_view

        rooturl = misc.make_script_url(**rooturl_args)

        # Acquire staff info.
        session = model.Session()
        staff_get = model.account.select()
        staff = session.execute(staff_get).fetchall()

        # Establish list of hidden inputs.
        inputs = [{
            'name': 'actiontoview',
            'value': action_to_view
        }, {
            'name': 'task',
            'value': 'stafflog'
        }, {
            'name': 'posttoview',
            'value': post_to_view
        }, {
            'name': 'usertoview',
            'value': user_to_view
        }, {
            'name': 'iptoview',
            'value': ip_to_view
        }, {
            'name': 'order',
            'value': sortby_dir
        }, {
            'name': 'sortby',
            'value': sortby_name
        }, {
            'name': 'view',
            'value': view
        }]

        if self.board:
            inputs.append({'name': 'board', 'value': self.board.name})

        # Apply sorting.
        if sortby_name and hasattr(table.c, sortby_name):
            order_col = getattr(table.c, sortby_name)
            if sortby_dir.lower() == 'asc':
                sort_spec = order_col.asc()
            else:
                sort_spec = order_col.desc()
            sql = sql.order_by(sort_spec)

        res = model.Page(sql, self.page, self.perpage)

        Template.__init__(self,
                          template_view,
                          user_to_view=user_to_view,
                          entries=res.rows,
                          staff=staff,
                          rowcount=res.total_entries,
                          numberofpages=res.total_pages,
                          view=view,
                          order=sortby_dir,
                          action_name=action_name,
                          content_name=action_content,
                          sortby=sortby_name,
                          number_of_pages=res.total_pages,
                          rooturl=rooturl,
                          inputs=inputs)
예제 #10
0
def move_thread(task_data, parent, src_brd_obj, dest_brd_obj):
    if not parent:
        raise WakaError('No thread specified.')
    if src_brd_obj.name == dest_brd_obj.name:
        raise WakaError('Source and destination boards match.')

    # Check administrative access rights to both boards.
    user = task_data.user
    src_brd_obj.check_access(user)
    dest_brd_obj.check_access(user)

    session = model.Session()
    src_table = src_brd_obj.table
    dest_table = dest_brd_obj.table

    sql = select([src_table.c.parent], src_table.c.num == parent)
    row = session.execute(sql).fetchone()

    if not row:
        raise WakaError('Thread not found.')
    elif row[0]:
        # Automatically correct if reply instead of thread was given.
        parent = row[0]

    sql = src_table.select().where(or_(src_table.c.num == parent,
                                       src_table.c.parent == parent))\
                            .order_by(src_table.c.num.asc())
    thread = [dict(x.items()) for x in session.execute(sql).fetchall()]

    # Indicate OP post number after insertion.
    new_parent = 0

    # List of images/thumbs to move around.
    image_move = []
    thumb_move = []

    lasthit = time.time()

    # DB operations
    for post in thread:
        # Grab post contents as dictionary of updates. Remove primary key.
        del post['num']

        post['lasthit'] = lasthit
        image = post['image']
        thumbnail = post['thumbnail']

        if image:
            image_move.append(image)
        if re.match(src_brd_obj.options['THUMB_DIR'], thumbnail):
            thumb_move.append(thumbnail)

        # Update post reference links.
        if new_parent:
            post['parent'] = new_parent

            new_comment = re.sub(r'a href="(.*?)'
                + os.path.join(src_brd_obj.path,
                               src_brd_obj.options['RES_DIR'],
                               '%d%s' % (int(parent)), config.PAGE_EXT),
                r'a href="\1' + os.path.join(\
                               dest_brd_obj.path,
                               dest_brd_obj.options['RES_DIR'],
                               '%d%s' % (int((new_parent), config.PAGE_EXT))),
                post['comment'])

            post['comment'] = new_comment

        sql = dest_table.insert().values(**post)
        result = session.execute(sql)

        if not new_parent:
            new_parent = result.inserted_primary_key[0]

    # Nested associate for moving files in bulk.
    def rename_files(filename_list, dir_type):
        for filename in filename_list:
            src_filename = os.path.join(src_brd_obj.path, filename)
            dest_filename = re.sub('^/?' + src_brd_obj.options[dir_type],
                                   dest_brd_obj.options[dir_type], filename)
            dest_filename = os.path.join(dest_brd_obj.path, dest_filename)
            os.rename(src_filename, dest_filename)

    # File transfer operations.
    rename_files(image_move, 'IMG_DIR')
    rename_files(thumb_move, 'THUMB_DIR')

    dest_brd_obj.build_cache()
    dest_brd_obj.build_thread_cache(new_parent)

    src_brd_obj.delete_stuff([parent], '', False, False, caller='internal')

    forward_url = misc.make_script_url(task='mpanel',
                                       board=dest_brd_obj.name,
                                       page=('t%s' % new_parent))

    # Log.
    task_data.contents.append('/%s/%d to /%s/%d' \
                              % (src_brd_obj.name, int(parent),
                                 dest_brd_obj.name, int(new_parent)))

    return util.make_http_forward(forward_url)
예제 #11
0
def add_admin_entry(task_data,
                    option,
                    comment,
                    ip='',
                    mask='255.255.255.255',
                    sval1='',
                    total='',
                    expiration=0,
                    caller=''):
    session = model.Session()
    table = model.admin

    ival1 = ival2 = 0

    if not comment:
        raise WakaError(strings.COMMENT_A_MUST)
    if option in ('ipban', 'whitelist'):
        if not ip:
            raise WakaError('IP address required.')
        if not mask:
            mask = '255.255.255.255'
        # Convert to decimal.
        (ival1, ival2) = (misc.dot_to_dec(ip), misc.dot_to_dec(mask))
        sql = table.select().where(table.c.type == option)
        query = session.execute(sql)

        for row in query:
            try:
                if int(row.ival1) & int(row.ival2) == ival1 & ival2:
                    raise WakaError('IP address and mask match ban #%d.' % \
                                    (row.num))
            except ValueError:
                raise WakaError("Entry #%s on ban table is inconsistent. "
                                "This shouldn't happen." % row.num)
        # Add info to task data.
        content = ip + (' (' + mask + ')' if mask else '')

        if total == 'yes':
            add_htaccess_entry(ip)
            content += ' (no browse)'

        content += ' "' + comment + '"'
        task_data.contents.append(content)
    else:
        if not sval1:
            raise WakaError(strings.STRINGFIELDMISSING)
        sql = table.select().where(
            and_(table.c.sval1 == sval1, table.c.type == option))
        row = session.execute(sql).fetchone()

        if row:
            raise WakaError('Duplicate String in ban #%d.' % (row.num))
        # Add ifno to task data.
        task_data.contents.append(sval1)

    comment = str_format.clean_string(\
        str_format.decode_string(comment, config.CHARSET))
    expiration = int(expiration) if expiration else 0
    if expiration:
        expiration = expiration + time.time()

    sql = table.insert().values(type=option,
                                comment=comment,
                                ival1=int(ival1),
                                ival2=int(ival2),
                                sval1=sval1,
                                total=total,
                                expiration=expiration)
    result = session.execute(sql)

    task_data.admin_id = result.inserted_primary_key[0]

    # Add specific action name to task data.
    task_data.action = option

    board = local.environ['waka.board']
    forward_url = misc.make_script_url(task='bans', board=board.name)

    if caller == 'window':
        return Template('edit_successful')
    return util.make_http_forward(forward_url, config.ALTERNATE_REDIRECT)
예제 #12
0
    def make_admin_post_search_panel(self, search, text, caller='internal'):
        board = self.board
        session = model.Session()
        table = board.table

        board.check_access(self.user)

        popup = caller != 'board'

        if search.find('IP Address') != -1:
            try:
                sql = table.select()\
                           .where(table.c.ip == misc.dot_to_dec(text))
            except ValueError:
                raise WakaError('Please enter a valid IP.')
            search_type = 'IP'
        elif search.find('Text String') != -1:
            sql = table.select().where(table.c.comment.like('%' + text + '%'))
            search_type = 'text string'
        elif search.find('Author') != -1:
            sql = table.select().where(
                or_(table.c.name.like('%' + text + '%'),
                    table.c.trip.like('%' + text + '%')))
            search_type = 'author'
        else:
            sql = table.select().where(table.c.num == text)
            search_type = 'ID'

        if search_type != 'ID':
            page = model.Page(sql, self.page, self.perpage)
            rowcount = page.total_entries
            total_pages = page.total_pages
            posts = page.rows
            if not posts:
                raise WakaError("No posts found for %s %s" %
                                (search_type, text))
        else:
            rowcount = total_pages = 1
            row = session.execute(sql).fetchone()
            if not row:
                raise WakaError("Post not found. (It may have just been"
                                " deleted.)")
            posts = [row]

        inputs = [{
            'name': 'board',
            'value': board.name
        }, {
            'name': 'task',
            'value': 'searchposts'
        }, {
            'name': 'text',
            'value': text
        }, {
            'name': 'caller',
            'value': caller
        }, {
            'name': 'search',
            'value': search
        }]

        rooturl = misc.make_script_url(task='searchposts',
                                       board=board.name,
                                       caller=caller,
                                       search=search,
                                       text=text,
                                       _amp=True)

        Template.__init__(self,
                          'post_search',
                          num=id,
                          posts=posts,
                          search=search,
                          text=text,
                          inputs=inputs,
                          number_of_pages=total_pages,
                          rooturl=rooturl,
                          rowcount=rowcount,
                          popup=popup)
예제 #13
0
 def ret_func(*args, **kwargs):
     self = args[0]
     if self.user.account == staff.MODERATOR:
         raise WakaError(strings.INSUFFICIENTPRIVILEGES)
     f(*args, **kwargs)
예제 #14
0
    def make_admin_trash_panel(self):
        board = self.board
        table = model.backup
        session = model.Session()
        template_kwargs = {}

        # List of current threads *and* orphaned posts.
        threads = []

        if str(self.page).startswith('t'):
            self.page = self.page[1:]
            sql = table.select().where(and_(or_(table.c.postnum == self.page,
                                                table.c.parent == self.page),
                                            table.c.board_name == board.name))\
                                .order_by(table.c.timestampofarchival.desc(),
                                          table.c.postnum.asc())
            thread = [dict(x.items()) for x in session.execute(sql).fetchall()]

            if not thread:
                raise WakaError('Thread not found.')

            threads = [{'posts': thread}]

            template_kwargs = {
                'postform':
                board.options['ALLOW_TEXTONLY']
                or board.options['ALLOW_IMAGES'],
                'image_inp':
                board.options['ALLOW_IMAGES'],
                'textonly_inp':
                0,
                'threads':
                threads,
                'thread':
                self.page,
                'parent':
                self.page
            }

        elif config.POST_BACKUP:
            max_res = board.options['IMAGES_PER_PAGE']

            sqlcond = and_(
                or_(
                    table.c.parent == 0,
                    and_(
                        table.c.parent > 0,
                        not_(
                            exists([table.c.num],
                                   table.c.parent == table.c.postnum)))),
                table.c.board_name == board.name)

            # Acquire the number of full threads *and* orphaned posts.
            sql = select([func.count()], sqlcond, table)\
                  .order_by(table.c.timestampofarchival.desc(),
                              table.c.postnum.asc())

            thread_ct = session.execute(sql).fetchone()[0]

            total = int(thread_ct + max_res - 1) / max_res
            offset = self.page * max_res

            (pages, prevpage, nextpage) \
                = board.get_board_page_data(self.page, total,
                                            admin_page='postbackups')

            last_page = len(pages) - 1
            if self.page > last_page and last_page > 0:
                self.page = last_page

            sql = table.select().where(sqlcond)\
                  .order_by(table.c.timestampofarchival.desc(),
                              table.c.num.asc())\
                  .limit(board.options['IMAGES_PER_PAGE'])\
                  .offset(offset)
            threads = [{'posts' : [dict(x.items())]} \
                for x in session.execute(sql)]

            # Loop through 'posts' key in each dictionary in the threads
            # list.
            for item in threads:
                thread = item['posts']
                threadnum = thread[0]['postnum']
                postcount = imgcount = shownimages = 0

                # Orphaned?
                item['standalone'] = 0

                if not thread[0]['parent']:
                    sql = select(
                        [func.count(), func.count(table.c.image)],
                        table.c.parent == threadnum, table)

                    (postcount, imgcount) = session.execute(sql).fetchone()

                    max_res = board.options['REPLIES_PER_THREAD']
                    offset = postcount - imgcount if postcount > max_res \
                                                  else 0

                    sql = table.select().where(table.c.parent == threadnum)\
                            .order_by(table.c.timestampofarchival.desc(),
                                      table.c.postnum.asc())\
                            .limit(max_res)\
                            .offset(offset)
                    thread.extend([dict(x.items()) \
                                       for x in session.execute(sql)])

                else:
                    item['standalone'] = 1

                for post in thread:
                    image_dir \
                        = os.path.join(board.path, board.options['IMG_DIR'])

                    thumb_dir \
                        = os.path.join(board.path, board.options['THUMB_DIR'])

                    base_thumb = os.path.basename(post['thumbnail'] or '')
                    base_image = os.path.basename(post['image'] or '')

                    base_filename = (post['image'] or '')\
                        .replace(image_dir, '').lstrip('/')

                    backup_dir = os.path.join(board.url,
                                              board.options['ARCHIVE_DIR'],
                                              board.options['BACKUP_DIR'])

                    if post['image']:
                        post['image'] = os.path.join(backup_dir, base_image)
                        shownimages += 1

                    if re.match(board.options['THUMB_DIR'], post['thumbnail']
                                or ''):
                        post['thumbnail'] \
                            = os.path.join(backup_dir, base_thumb)

                item['omit'] = postcount - max_res if postcount > max_res\
                                                   else 0

                item['omitimages'] = imgcount - shownimages \
                                     if imgcount > shownimages else 0

                template_kwargs = {'postform' \
                                      : board.options['ALLOW_TEXTONLY'] or
                                        board.options['ALLOW_IMAGES'],
                                  'image_inp' : board.options['ALLOW_IMAGES'],
                                   'textonly_inp' \
                                      : board.options['ALLOW_IMAGES'] and
                                        board.options['ALLOW_TEXTONLY'],
                                   'nextpage' : nextpage,
                                   'prevpage' : prevpage,
                                   'threads' : threads,
                                   'pages' : pages}

        Template.__init__(self, 'backup_panel_template', **template_kwargs)
예제 #15
0
 def check_access(self, board_name):
     if self.account == MODERATOR and board_name not in self.reign:
         raise WakaError('Access to this board (%s) denied.' % board_name)
예제 #16
0
 def account(self, new):
     if new in CLASSES:
         self._update_db(account=new)
         self._class = new
     else:
         raise WakaError('Invalid class name %s' % new)
예제 #17
0
 def ret_func(*args, **kwargs):
     self = args[0]
     if self.user.account != staff.ADMIN:
         raise WakaError(strings.INUSUFFICENTPRIVLEDGES)
     f(*args, **kwargs)