示例#1
0
def send(mailto, subject, content):
    """Send an e-mail.

    `mailto` must be a normalized e-mail address to send this e-mail to. The
    system email will be designated as the sender.
    """
    message = MIMEText(content.strip())
    message["To"] = mailto
    message["From"] = macro.MACRO_EMAIL_ADDRESS
    message["Subject"] = subject

    # smtp.sendmail() only converts CR and LF (produced by MIMEText and our templates) to CRLF in Python 3. In Python 2, we need this:
    msg_crlf = re.sub(r"\r\n|[\r\n]", "\r\n", message.as_string())

    smtp = SMTP(define.config_read_setting('host', "localhost",
                                           section='smtp'))

    try:
        smtp.sendmail(
            from_addr=macro.MACRO_EMAIL_ADDRESS,
            to_addrs=[mailto],
            msg=msg_crlf,
        )
    finally:
        smtp.quit()

    define.metric('increment', 'emails')
示例#2
0
文件: shout.py 项目: amanyashu/weasyl
def insert(userid, target_user, parentid, content, staffnotes):
    # Check invalid content
    if not content:
        raise WeasylError("commentInvalid")
    elif not target_user or not d.is_vouched_for(target_user):
        raise WeasylError("Unexpected")

    # Determine parent userid
    if parentid:
        parentuserid = d.engine.scalar(
            "SELECT userid FROM comments WHERE commentid = %(parent)s",
            parent=parentid,
        )

        if parentuserid is None:
            raise WeasylError("shoutRecordMissing")
    else:
        parentuserid = None

    # Check permissions
    if userid not in staff.MODS:
        if ignoreuser.check(target_user, userid):
            raise WeasylError("pageOwnerIgnoredYou")
        elif ignoreuser.check(userid, target_user):
            raise WeasylError("youIgnoredPageOwner")
        elif ignoreuser.check(parentuserid, userid):
            raise WeasylError("replyRecipientIgnoredYou")
        elif ignoreuser.check(userid, parentuserid):
            raise WeasylError("youIgnoredReplyRecipient")

        _, is_banned, _ = d.get_login_settings(target_user)
        profile_config = d.get_config(target_user)

        if is_banned or "w" in profile_config or "x" in profile_config and not frienduser.check(
                userid, target_user):
            raise WeasylError("insufficientActionPermissions")

    # Create comment
    settings = 's' if staffnotes else ''
    co = d.meta.tables['comments']
    db = d.connect()
    commentid = db.scalar(co.insert().values(userid=userid,
                                             target_user=target_user,
                                             parentid=parentid or None,
                                             content=content,
                                             unixtime=arrow.utcnow(),
                                             settings=settings).returning(
                                                 co.c.commentid))

    # Create notification
    if parentid and userid != parentuserid:
        if not staffnotes or parentuserid in staff.MODS:
            welcome.shoutreply_insert(userid, commentid, parentuserid,
                                      parentid, staffnotes)
    elif not staffnotes and target_user and userid != target_user:
        welcome.shout_insert(userid, commentid, otherid=target_user)

    d.metric('increment', 'shouts')

    return commentid
示例#3
0
def create(form):
    # Normalize form data
    username = d.plaintext(form.username[:_USERNAME])
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck

    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if not sysname or ";" in username:
        raise WeasylError("usernameInvalid")
    if sysname in ["admin", "administrator", "mod", "moderator", "weasyl",
                   "weasyladmin", "weasylmod", "staff", "security"]:
        raise WeasylError("usernameInvalid")
    if email_exists(email):
        raise WeasylError("emailExists")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Create pending account
    token = security.generate_key(40)

    d.engine.execute(d.meta.tables["logincreate"].insert(), {
        "token": token,
        "username": username,
        "login_name": sysname,
        "hashpass": passhash(password),
        "email": email,
        "birthday": birthday,
        "unixtime": arrow.now(),
    })

    # Queue verification email
    emailer.append([email], None, "Weasyl Account Creation", d.render(
        "email/verify_account.html", [token, sysname]))
    d.metric('increment', 'createdusers')
示例#4
0
def insert(userid, shout, staffnotes=False):
    # Check invalid content
    if not shout.content:
        raise WeasylError("commentInvalid")
    elif not shout.userid:
        raise WeasylError("Unexpected")

    # Determine indent and parentuserid
    if shout.parentid:
        query = d.execute("SELECT userid, indent FROM comments WHERE commentid = %i",
                          [shout.parentid], options="single")

        if not query:
            raise WeasylError("shoutRecordMissing")

        indent, parentuserid = query[1] + 1, query[0]
    else:
        indent, parentuserid = 0, None

    # Check permissions
    if userid not in staff.MODS:
        if ignoreuser.check(shout.userid, userid):
            raise WeasylError("pageOwnerIgnoredYou")
        elif ignoreuser.check(userid, shout.userid):
            raise WeasylError("youIgnoredPageOwner")
        elif ignoreuser.check(parentuserid, userid):
            raise WeasylError("replyRecipientIgnoredYou")
        elif ignoreuser.check(userid, parentuserid):
            raise WeasylError("youIgnoredReplyRecipient")

        settings = d.execute("SELECT lo.settings, pr.config FROM login lo"
                             " INNER JOIN profile pr ON lo.userid = pr.userid"
                             " WHERE lo.userid = %i", [shout.userid], options="single")

        if "b" in settings[0] or "w" in settings[1] or "x" in settings[1] and not frienduser.check(userid, shout.userid):
            raise WeasylError("insufficientActionPermissions")

    # Create comment
    settings = 's' if staffnotes else ''
    co = d.meta.tables['comments']
    db = d.connect()
    commentid = db.scalar(
        co.insert()
        .values(userid=userid, target_user=shout.userid, parentid=shout.parentid or None, content=shout.content,
                unixtime=arrow.utcnow(), indent=indent, settings=settings)
        .returning(co.c.commentid))

    # Create notification
    if shout.parentid and userid != parentuserid:
        if not staffnotes or parentuserid in staff.MODS:
            welcome.shoutreply_insert(userid, commentid, parentuserid, shout.parentid, staffnotes)
    elif not staffnotes and shout.userid and userid != shout.userid:
        welcome.shout_insert(userid, commentid, otherid=shout.userid)

    d.metric('increment', 'shouts')

    return commentid
示例#5
0
def insert(userid, shout, staffnotes=False):
    # Check invalid content
    if not shout.content:
        raise WeasylError("commentInvalid")
    elif not shout.userid:
        raise WeasylError("Unexpected")

    # Determine indent and parentuserid
    if shout.parentid:
        query = d.execute("SELECT userid, indent FROM comments WHERE commentid = %i",
                          [shout.parentid], options="single")

        if not query:
            raise WeasylError("shoutRecordMissing")

        indent, parentuserid = query[1] + 1, query[0]
    else:
        indent, parentuserid = 0, None

    # Check permissions
    if userid not in staff.MODS:
        if ignoreuser.check(shout.userid, userid):
            raise WeasylError("pageOwnerIgnoredYou")
        elif ignoreuser.check(userid, shout.userid):
            raise WeasylError("youIgnoredPageOwner")
        elif ignoreuser.check(parentuserid, userid):
            raise WeasylError("replyRecipientIgnoredYou")
        elif ignoreuser.check(userid, parentuserid):
            raise WeasylError("youIgnoredReplyRecipient")

        settings = d.execute("SELECT lo.settings, pr.config FROM login lo"
                             " INNER JOIN profile pr ON lo.userid = pr.userid"
                             " WHERE lo.userid = %i", [shout.userid], options="single")

        if "b" in settings[0] or "w" in settings[1] or "x" in settings[1] and not frienduser.check(userid, shout.userid):
            raise WeasylError("insufficientActionPermissions")

    # Create comment
    settings = 's' if staffnotes else ''
    co = d.meta.tables['comments']
    db = d.connect()
    commentid = db.scalar(
        co.insert()
        .values(userid=userid, target_user=shout.userid, parentid=shout.parentid or None, content=shout.content,
                unixtime=arrow.utcnow(), indent=indent, settings=settings)
        .returning(co.c.commentid))

    # Create notification
    if shout.parentid and userid != parentuserid:
        if not staffnotes or parentuserid in staff.MODS:
            welcome.shoutreply_insert(userid, commentid, parentuserid, shout.parentid, staffnotes)
    elif not staffnotes and shout.userid and userid != shout.userid:
        welcome.shout_insert(userid, commentid, otherid=shout.userid)

    d.metric('increment', 'shouts')

    return commentid
示例#6
0
def create(userid, journal, friends_only=False, tags=None):
    # Check invalid arguments
    if not journal.title:
        raise WeasylError("titleInvalid")
    elif not journal.content:
        raise WeasylError("contentInvalid")
    elif not journal.rating:
        raise WeasylError("ratingInvalid")
    profile.check_user_rating_allowed(userid, journal.rating)

    # Assign settings
    settings = "f" if friends_only else ""

    # Create journal
    jo = d.meta.tables["journal"]

    # Run the journal through Akismet to check for spam
    is_spam = False
    if spam_filtering.FILTERING_ENABLED:
        result = spam_filtering.check(
            user_ip=journal.submitter_ip_address,
            user_agent_id=journal.submitter_user_agent_id,
            user_id=userid,
            comment_type="journal",
            comment_content=journal.content,
        )
        if result == SpamStatus.DefiniteSpam:
            raise WeasylError("SpamFilteringDropped")
        elif result == SpamStatus.ProbableSpam:
            is_spam = True

    journalid = d.engine.scalar(jo.insert().returning(jo.c.journalid), {
        "userid": userid,
        "title": journal.title,
        "content": journal.content,
        "rating": journal.rating.code,
        "unixtime": arrow.now(),
        "settings": settings,
        "is_spam": is_spam,
        "submitter_ip_address": journal.submitter_ip_address,
        "submitter_user_agent_id": journal.submitter_user_agent_id,
    })

    # Assign search tags
    searchtag.associate(userid, tags, journalid=journalid)

    # Create notifications
    if "m" not in settings:
        welcome.journal_insert(userid, journalid, rating=journal.rating.code,
                               settings=settings)

    d.metric('increment', 'journals')

    return journalid
示例#7
0
文件: login.py 项目: Syfaro/weasyl
def signin(userid):
    # Update the last login record for the user
    d.execute("UPDATE login SET last_login = %i WHERE userid = %i", [d.get_time(), userid])

    # Log the successful login and increment the login count
    d.append_to_log('login.success', userid=userid, ip=d.get_address())
    d.metric('increment', 'logins')

    # set the userid on the session
    sess = d.get_weasyl_session()
    sess.userid = userid
    sess.save = True
示例#8
0
def signin(userid):
    # Update the last login record for the user
    d.execute("UPDATE login SET last_login = %i WHERE userid = %i", [d.get_time(), userid])

    # Log the successful login and increment the login count
    d.append_to_log('login.success', userid=userid, ip=d.get_address())
    d.metric('increment', 'logins')

    # set the userid on the session
    sess = d.get_weasyl_session()
    sess.userid = userid
    sess.save = True
示例#9
0
def verify(token, ip_address=None):
    lo = d.meta.tables["login"]
    lc = d.meta.tables["logincreate"]
    query = d.engine.execute(lc.select().where(lc.c.token == token)).first()

    # Did the token match a pending login create record?
    if not query:
        raise WeasylError("logincreateRecordMissing")
    elif query.invalid:
        # If the record is explicitly marked as invalid, treat the record as if it doesn't exist.
        raise WeasylError("logincreateRecordMissing")

    db = d.connect()
    with db.begin():
        # Create login record
        userid = db.scalar(
            lo.insert().returning(lo.c.userid), {
                "login_name": d.get_sysname(query.username),
                "last_login": arrow.now(),
                "email": query.email,
                "ip_address_at_signup": ip_address,
            })

        # Create profile records
        db.execute(d.meta.tables["authbcrypt"].insert(), {
            "userid": userid,
            "hashsum": query.hashpass,
        })
        db.execute(
            d.meta.tables["profile"].insert(), {
                "userid": userid,
                "username": query.username,
                "full_name": query.username,
                "unixtime": arrow.now(),
                "config": "kscftj",
            })
        db.execute(d.meta.tables["userinfo"].insert(), {
            "userid": userid,
            "birthday": query.birthday,
        })
        db.execute(d.meta.tables["userstats"].insert(), {
            "userid": userid,
        })
        db.execute(d.meta.tables["welcomecount"].insert(), {
            "userid": userid,
        })

        # Update logincreate records
        db.execute(lc.delete().where(lc.c.token == token))

    d.metric('increment', 'verifiedusers')
示例#10
0
文件: login.py 项目: Weasyl/weasyl
def verify(token, ip_address=None):
    lo = d.meta.tables["login"]
    lc = d.meta.tables["logincreate"]
    query = d.engine.execute(lc.select().where(lc.c.token == token)).first()

    # Did the token match a pending login create record?
    if not query:
        raise WeasylError("logincreateRecordMissing")
    elif query.invalid:
        # If the record is explicitly marked as invalid, treat the record as if it doesn't exist.
        raise WeasylError("logincreateRecordMissing")

    db = d.connect()
    with db.begin():
        # Create login record
        userid = db.scalar(lo.insert().returning(lo.c.userid), {
            "login_name": d.get_sysname(query.username),
            "last_login": arrow.now(),
            "email": query.email,
            "ip_address_at_signup": ip_address,
        })

        # Create profile records
        db.execute(d.meta.tables["authbcrypt"].insert(), {
            "userid": userid,
            "hashsum": query.hashpass,
        })
        db.execute(d.meta.tables["profile"].insert(), {
            "userid": userid,
            "username": query.username,
            "full_name": query.username,
            "unixtime": arrow.now(),
            "config": "kscftj",
        })
        db.execute(d.meta.tables["userinfo"].insert(), {
            "userid": userid,
            "birthday": query.birthday,
        })
        db.execute(d.meta.tables["userstats"].insert(), {
            "userid": userid,
        })
        db.execute(d.meta.tables["welcomecount"].insert(), {
            "userid": userid,
        })

        # Update logincreate records
        db.execute(lc.delete().where(lc.c.token == token))

    d.metric('increment', 'verifiedusers')
示例#11
0
文件: login.py 项目: weykent/weasyl
def verify(token):
    lo = d.meta.tables["login"]
    lc = d.meta.tables["logincreate"]
    query = d.engine.execute(lc.select().where(lc.c.token == token)).first()

    if not query:
        raise WeasylError("logincreateRecordMissing")

    db = d.connect()
    with db.begin():
        # Create login record
        userid = db.execute(
            lo.insert().returning(lo.c.userid), {
                "login_name": d.get_sysname(query.username),
                "last_login": arrow.now(),
                "email": query.email,
            }).scalar()

        # Create profile records
        db.execute(d.meta.tables["authbcrypt"].insert(), {
            "userid": userid,
            "hashsum": query.hashpass,
        })
        db.execute(
            d.meta.tables["profile"].insert(), {
                "userid": userid,
                "username": query.username,
                "full_name": query.username,
                "unixtime": arrow.now(),
                "config": "kscftj",
            })
        db.execute(d.meta.tables["userinfo"].insert(), {
            "userid": userid,
            "birthday": query.birthday,
        })
        db.execute(d.meta.tables["userstats"].insert(), {
            "userid": userid,
        })
        db.execute(d.meta.tables["welcomecount"].insert(), {
            "userid": userid,
        })

        # Update logincreate records
        db.execute(lc.delete().where(lc.c.token == token))

    d.metric('increment', 'verifiedusers')
示例#12
0
def verify(token):
    lo = d.meta.tables["login"]
    lc = d.meta.tables["logincreate"]
    query = d.engine.execute(lc.select().where(lc.c.token == token)).first()

    if not query:
        raise WeasylError("logincreateRecordMissing")

    db = d.connect()
    with db.begin():
        # Create login record
        userid = db.scalar(lo.insert().returning(lo.c.userid), {
            "login_name": d.get_sysname(query.username),
            "last_login": arrow.now(),
            "email": query.email,
        })

        # Create profile records
        db.execute(d.meta.tables["authbcrypt"].insert(), {
            "userid": userid,
            "hashsum": query.hashpass,
        })
        db.execute(d.meta.tables["profile"].insert(), {
            "userid": userid,
            "username": query.username,
            "full_name": query.username,
            "unixtime": arrow.now(),
            "config": "kscftj",
        })
        db.execute(d.meta.tables["userinfo"].insert(), {
            "userid": userid,
            "birthday": query.birthday,
        })
        db.execute(d.meta.tables["userstats"].insert(), {
            "userid": userid,
        })
        db.execute(d.meta.tables["welcomecount"].insert(), {
            "userid": userid,
        })

        # Update logincreate records
        db.execute(lc.delete().where(lc.c.token == token))

    d.metric('increment', 'verifiedusers')
示例#13
0
def create(userid, journal, friends_only=False, tags=None):
    # Check invalid arguments
    if not journal.title:
        raise WeasylError("titleInvalid")
    elif not journal.content:
        raise WeasylError("contentInvalid")
    elif not journal.rating:
        raise WeasylError("ratingInvalid")
    profile.check_user_rating_allowed(userid, journal.rating)

    # Assign settings
    settings = "f" if friends_only else ""

    # Create journal
    jo = d.meta.tables["journal"]

    journalid = d.engine.scalar(
        jo.insert().returning(jo.c.journalid), {
            "userid": userid,
            "title": journal.title,
            "rating": journal.rating.code,
            "unixtime": arrow.now(),
            "settings": settings,
        })

    # Write journal file
    files.make_path(journalid, "journal")
    files.write(files.make_resource(userid, journalid, "journal/submit"),
                journal.content)

    # Assign search tags
    searchtag.associate(userid, tags, journalid=journalid)

    # Create notifications
    if "m" not in settings:
        welcome.journal_insert(userid,
                               journalid,
                               rating=journal.rating.code,
                               settings=settings)

    d.metric('increment', 'journals')

    return journalid
示例#14
0
文件: login.py 项目: Weasyl/weasyl
def signin(request, userid, ip_address=None, user_agent=None):
    # Update the last login record for the user
    d.execute("UPDATE login SET last_login = %i WHERE userid = %i", [d.get_time(), userid])

    # Log the successful login and increment the login count
    d.append_to_log('login.success', userid=userid, ip=d.get_address())
    d.metric('increment', 'logins')

    # set the userid on the session
    sess = create_session(userid)
    sess.ip_address = ip_address
    sess.user_agent_id = get_user_agent_id(user_agent)
    sess.create = True

    if not isinstance(request.weasyl_session, GuestSession):
        request.pg_connection.delete(request.weasyl_session)
        request.pg_connection.flush()

    request.weasyl_session = sess
示例#15
0
def signin(request, userid, ip_address=None, user_agent=None):
    # Update the last login record for the user
    d.execute("UPDATE login SET last_login = NOW() WHERE userid = %i", [userid])

    # Log the successful login and increment the login count
    d.append_to_log('login.success', userid=userid, ip=d.get_address())
    d.metric('increment', 'logins')

    # set the userid on the session
    sess = create_session(userid)
    sess.ip_address = ip_address
    sess.user_agent_id = get_user_agent_id(user_agent)
    sess.create = True

    if not isinstance(request.weasyl_session, GuestSession):
        request.pg_connection.delete(request.weasyl_session)
        request.pg_connection.flush()

    request.weasyl_session = sess
示例#16
0
文件: emailer.py 项目: weykent/weasyl
def append(mailto, mailfrom, subject, content, displayto=None):
    """Send an e-mail.

    `mailto` must be a list of e-mail addresses to send this e-mail to. If
    `mailfrom` is None, the system email will be designated as the sender.
    Otherwise, `mailfrom` must be a single e-mail address. The 'To' header of the
    e-mail will be a comma-separated list of the `mailto` addresses unless
    `displayto` is not None (in which case it will be set to `displayto`.)
    """

    if not mailfrom:
        mailfrom = macro.MACRO_EMAIL_ADDRESS

    mailfrom = normalize_address(mailfrom)
    subject = subject.strip()
    content = content.strip()

    if not mailto:
        raise error.WeasylError("mailtoInvalid")
    elif not mailfrom:
        raise error.WeasylError("mailfromInvalid")
    elif not content:
        raise error.WeasylError("contentInvalid")

    if not subject:
        subject = "None"

    message = email.mime.text.MIMEText(content.strip())
    if displayto is not None:
        message["To"] = displayto
    else:
        message["To"] = ', '.join(mailto)
    message["From"] = mailfrom
    message["Subject"] = subject

    sendmail_args = ['sendmail'] + list(mailto)
    proc = subprocess.Popen(sendmail_args, stdin=subprocess.PIPE)
    proc.communicate(message.as_string())
    if proc.returncode:
        raise subprocess.CalledProcessError(proc.returncode, sendmail_args)
    define.metric('increment', 'emails')
示例#17
0
文件: journal.py 项目: makyo/weasyl
def create(userid, journal, friends_only=False, tags=None):
    # Check invalid arguments
    if not journal.title:
        raise WeasylError("titleInvalid")
    elif not journal.content:
        raise WeasylError("contentInvalid")
    elif not journal.rating:
        raise WeasylError("ratingInvalid")
    profile.check_user_rating_allowed(userid, journal.rating)

    # Assign settings
    settings = "f" if friends_only else ""

    # Create journal
    jo = d.meta.tables["journal"]

    journalid = d.engine.scalar(jo.insert().returning(jo.c.journalid), {
        "userid": userid,
        "title": journal.title,
        "rating": journal.rating.code,
        "unixtime": arrow.now(),
        "settings": settings,
    })

    # Write journal file
    files.make_path(journalid, "journal")
    files.write(files.make_resource(userid, journalid, "journal/submit"),
                journal.content)

    # Assign search tags
    searchtag.associate(userid, tags, journalid=journalid)

    # Create notifications
    if "m" not in settings:
        welcome.journal_insert(userid, journalid, rating=journal.rating.code,
                               settings=settings)

    d.metric('increment', 'journals')

    return journalid
示例#18
0
def authenticate_bcrypt(username, password, request, ip_address=None, user_agent=None):
    """
    Return a result tuple of the form (userid, error); `error` is None if the
    login was successful. Pass None as the `request` to authenticate a user
    without creating a new session.

    :param username: The username of the user attempting authentication.
    :param password: The user's claimed password to check against the stored hash.
    :param request: The request, or None
    :param ip_address: The address requesting authentication.
    :param user_agent: The user agent string of the submitting client.

    Possible errors are:
    - "invalid"
    - "unexpected"
    - "banned"
    - "suspended"
    - "2fa" - Indicates the user has opted-in to 2FA. Additional authentication required.
    """
    # Check that the user entered potentially valid values for `username` and
    # `password` before attempting to authenticate them
    if not username or not password:
        return 0, "invalid"

    # Select the authentication data necessary to check that the the user-entered
    # credentials are valid
    query = d.engine.execute(
        "SELECT ab.userid, ab.hashsum, lo.twofa_secret FROM authbcrypt ab"
        " RIGHT JOIN login lo USING (userid)"
        " WHERE lo.login_name = %(name)s",
        name=d.get_sysname(username),
    ).first()

    if not query:
        return 0, "invalid"

    USERID, HASHSUM, TWOFA = query
    HASHSUM = HASHSUM.encode('utf-8')
    _, IS_BANNED, IS_SUSPENDED = d.get_login_settings(USERID)

    d.metric('increment', 'attemptedlogins')

    if not bcrypt.checkpw(password.encode('utf-8'), HASHSUM):
        # Log the failed login attempt in a security log if the account the user
        # attempted to log into is a privileged account
        if USERID in staff.MODS:
            d.append_to_log('login.fail', userid=USERID, ip=d.get_address())
            d.metric('increment', 'failedlogins')

        # Return a zero userid and an error code (indicating the entered password
        # was incorrect)
        return 0, "invalid"
    elif IS_BANNED:
        # Return the proper userid and an error code (indicating the user's account
        # has been banned)
        return USERID, "banned"
    elif IS_SUSPENDED:
        from weasyl import moderation
        suspension = moderation.get_suspension(USERID)

        if d.get_time() > suspension.release:
            d.execute("DELETE FROM suspension WHERE userid = %i", [USERID])
            d._get_all_config.invalidate(USERID)
        else:
            # Return the proper userid and an error code (indicating the user's
            # account has been temporarily suspended)
            return USERID, "suspended"

    # Attempt to create a new session if this is a request to log in, then log the signin
    # if it succeeded.
    if request is not None:
        # If the user's record has ``login.twofa_secret`` set (not nulled), return that password authentication succeeded.
        if TWOFA:
            if not isinstance(request.weasyl_session, GuestSession):
                request.pg_connection.delete(request.weasyl_session)
                request.pg_connection.flush()
            request.weasyl_session = create_session(None)
            request.weasyl_session.additional_data = {}
            return USERID, "2fa"
        else:
            signin(request, USERID, ip_address=ip_address, user_agent=user_agent)

    # Either way, authentication succeeded, so return the userid and a status.
    return USERID, None
示例#19
0
def authenticate_bcrypt(username, password, session=True):
    """
    Return a result tuple of the form (userid, error); `error` is None if the
    login was successful. Pass `session` as False to authenticate a user without
    creating a new session.

    Possible errors are:
    - "invalid"
    - "unexpected"
    - "address"
    - "banned"
    - "suspended"
    """
    # Check that the user entered potentially valid values for `username` and
    # `password` before attempting to authenticate them
    if not username or not password:
        return 0, "invalid"

    # Select the authentication data necessary to check that the the user-entered
    # credentials are valid
    query = d.execute("SELECT ab.userid, ab.hashsum, lo.settings FROM authbcrypt ab"
                      " RIGHT JOIN login lo USING (userid)"
                      " WHERE lo.login_name = '%s'", [d.get_sysname(username)], ["single"])

    if not query:
        return 0, "invalid"

    USERID, HASHSUM, SETTINGS = query
    HASHSUM = HASHSUM.encode('utf-8')

    d.metric('increment', 'attemptedlogins')

    unicode_success = bcrypt.checkpw(password.encode('utf-8'), HASHSUM)
    if not unicode_success and not bcrypt.checkpw(d.plaintext(password).encode('utf-8'), HASHSUM):
        # Log the failed login attempt in a security log if the account the user
        # attempted to log into is a privileged account
        if USERID in staff.MODS:
            d.append_to_log('login.fail', userid=USERID, ip=d.get_address())
            d.metric('increment', 'failedlogins')

        # Return a zero userid and an error code (indicating the entered password
        # was incorrect)
        return 0, "invalid"
    elif "b" in SETTINGS:
        # Return the proper userid and an error code (indicating the user's account
        # has been banned)
        return USERID, "banned"
    elif "s" in SETTINGS:
        suspension = moderation.get_suspension(USERID)

        if d.get_time() > suspension.release:
            d.execute("UPDATE login SET settings = REPLACE(settings, 's', '') WHERE userid = %i", [USERID])
            d.execute("DELETE FROM suspension WHERE userid = %i", [USERID])
            d.get_login_settings.invalidate(USERID)
        else:
            # Return the proper userid and an error code (indicating the user's
            # account has been temporarily suspended)
            return USERID, "suspended"

    # Attempt to create a new session if `session` is True, then log the signin
    # if it succeeded.
    if session:
        signin(USERID)
        d.append_to_log('login.success', userid=USERID, ip=d.get_address())
        d.metric('increment', 'logins')

    status = None
    if not unicode_success:
        # Oops; the user's password was stored badly, but they did successfully authenticate.
        status = 'unicode-failure'
    # Either way, authentication succeeded, so return the userid and a status.
    return USERID, status
示例#20
0
def create_literary(userid, submission, embedlink=None, friends_only=False, tags=None,
                    coverfile=None, thumbfile=None, submitfile=None, critique=False,
                    create_notifications=True):
    premium = d.get_premium(userid)

    if embedlink:
        check_google_doc_embed_data(embedlink)

    # Determine filesizes
    coversize = len(coverfile)
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize and not embedlink:
        raise WeasylError("submitSizeZero")
    elif coversize > 10 * _MEGABYTE:
        raise WeasylError("coverSizeExceedsLimit")
    elif thumbsize > 10 * _MEGABYTE:
        raise WeasylError("thumbSizeExceedsLimit")

    if submitsize:
        submitextension = files.get_extension_for_category(submitfile, m.TEXT_SUBMISSION_CATEGORY)
        if submitextension is None:
            raise WeasylError("submitType")
        if _limit(submitsize, submitextension, premium):
            raise WeasylError("submitSizeExceedsLimit")
        submit_media_item = orm.fetch_or_create_media_item(
            submitfile, file_type=submitextension.lstrip('.'))
        check_for_duplicate_media(userid, submit_media_item.mediaid)
    else:
        submit_media_item = None

    thumb_media_item = media.make_cover_media_item(thumbfile)
    cover_media_item = media.make_cover_media_item(coverfile)
    if cover_media_item and not thumb_media_item:
        thumb_media_item = cover_media_item

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    if embedlink:
        settings.append('D')
    settings = "".join(settings)

    # Create submission
    # TODO(kailys): use ORM object
    db = d.connect()
    now = arrow.get()
    try:
        q = (
            d.meta.tables['submission'].insert().values([{
                "folderid": submission.folderid,
                "userid": userid,
                "unixtime": now,
                "title": submission.title,
                "content": submission.content,
                "subtype": submission.subtype,
                "rating": submission.rating.code,
                "settings": settings,
                "sorttime": now,
            }])
            .returning(d.meta.tables['submission'].c.submitid))
        submitid = db.scalar(q)
        if embedlink:
            q = (d.meta.tables['google_doc_embeds'].insert()
                 .values(submitid=submitid, embed_url=embedlink))
            db.execute(q)
    except:
        files.clear_temporary(userid)
        raise

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    if submit_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission', submit_media_item)
    if cover_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover', cover_media_item)
    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings,
                              submission.title, tags)

    # Clear temporary files
    files.clear_temporary(userid)

    d.metric('increment', 'submissions')
    d.metric('increment', 'literarysubmissions')

    return submitid, bool(thumb_media_item)
示例#21
0
文件: comment.py 项目: Syfaro/weasyl
def insert(userid, submitid=None, charid=None, journalid=None, parentid=None, content=None):
    if not submitid and not charid and not journalid:
        raise WeasylError("Unexpected")
    elif not content:
        raise WeasylError("commentInvalid")

    # Determine indent and parentuserid
    if parentid:
        query = d.execute("SELECT userid, indent FROM %s WHERE commentid = %i",
                          ["comments" if submitid else "charcomment" if charid else "journalcomment", parentid],
                          options="single")

        if not query:
            raise WeasylError("Unexpected")

        indent = query[1] + 1
        parentuserid = query[0]
    else:
        indent = 0
        parentuserid = None

    # Determine otherid
    otherid = d.execute("SELECT userid FROM %s WHERE %s = %i AND settings !~ 'h'",
                        ["submission", "submitid", submitid] if submitid else
                        ["character", "charid", charid] if charid else
                        ["journal", "journalid", journalid], options="element")

    # Check permissions
    if not otherid:
        raise WeasylError("submissionRecordMissing")
    elif ignoreuser.check(otherid, userid):
        raise WeasylError("pageOwnerIgnoredYou")
    elif ignoreuser.check(userid, otherid):
        raise WeasylError("youIgnoredPageOwner")
    elif parentuserid and ignoreuser.check(parentuserid, userid):
        raise WeasylError("replyRecipientIgnoredYou")
    elif parentuserid and ignoreuser.check(userid, parentuserid):
        raise WeasylError("youIgnoredReplyRecipient")

    # Create comment
    if submitid:
        co = d.meta.tables['comments']
        db = d.connect()
        commentid = db.scalar(
            co.insert()
            .values(userid=userid, target_sub=submitid, parentid=parentid or None,
                    content=content, unixtime=arrow.utcnow(), indent=indent)
            .returning(co.c.commentid))
    else:
        commentid = d.execute(
            "INSERT INTO %s (userid, targetid, parentid, "
            "content, unixtime, indent) VALUES (%i, %i, %i, '%s', %i, %i) RETURNING "
            "commentid", [
                "charcomment" if charid else "journalcomment", userid,
                d.get_targetid(submitid, charid, journalid),
                parentid, content, d.get_time(), indent
            ], options="element")

    # Create notification
    if parentid and (userid != parentuserid):
        welcome.commentreply_insert(userid, commentid, parentuserid, parentid, submitid, charid, journalid)
    elif not parentid:
        # build a list of people this comment should notify
        # circular imports are cool and fun
        from weasyl.collection import find_owners
        notified = set(find_owners(submitid))

        # check to see who we should deliver comment notifications to
        def can_notify(other):
            other_jsonb = d.get_profile_settings(other)
            allow_notify = other_jsonb.allow_collection_notifs
            ignored = ignoreuser.check(other, userid)
            return allow_notify and not ignored
        notified = set(filter(can_notify, notified))
        # always give notification on own content
        notified.add(otherid)
        # don't give me a notification for my own comment
        notified.discard(userid)

        for other in notified:
            welcome.comment_insert(userid, commentid, other, submitid, charid, journalid)

    d.metric('increment', 'comments')

    return commentid
示例#22
0
def create(userid, character, friends, tags, thumbfile, submitfile):
    # Make temporary filenames
    tempthumb = files.get_temporary(userid, "thumb")
    tempsubmit = files.get_temporary(userid, "char")

    # Determine filesizes
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    # Check invalid title or rating
    if not character.char_name:
        raise WeasylError("characterNameInvalid")
    elif not character.rating:
        raise WeasylError("ratingInvalid")
    profile.check_user_rating_allowed(userid, character.rating)

    # Write temporary thumbnail file
    if thumbsize:
        files.write(tempthumb, thumbfile)
        thumbextension = files.get_extension_for_category(
            thumbfile, macro.ART_SUBMISSION_CATEGORY)
    else:
        thumbextension = None

    # Write temporary submission file
    if submitsize:
        files.write(tempsubmit, submitfile)
        submitextension = files.get_extension_for_category(
            submitfile, macro.ART_SUBMISSION_CATEGORY)
    else:
        submitextension = None

    # Check invalid file data
    if not submitsize:
        files.clear_temporary(userid)
        raise WeasylError("submitSizeZero")
    elif submitsize > _MAIN_IMAGE_SIZE_LIMIT:
        files.clear_temporary(userid)
        raise WeasylError("submitSizeExceedsLimit")
    elif thumbsize > 10 * _MEGABYTE:
        files.clear_temporary(userid)
        raise WeasylError("thumbSizeExceedsLimit")
    elif submitextension not in [".jpg", ".png", ".gif"]:
        files.clear_temporary(userid)
        raise WeasylError("submitType")
    elif thumbsize and thumbextension not in [".jpg", ".png", ".gif"]:
        files.clear_temporary(userid)
        raise WeasylError("thumbType")

    # Assign settings
    settings = []
    settings.append("f" if friends else "")
    settings.append(files.typeflag("submit", submitextension))
    settings.append(files.typeflag("cover", submitextension))
    settings = "".join(settings)

    # Insert submission data
    ch = define.meta.tables["character"]

    try:
        charid = define.engine.scalar(ch.insert().returning(ch.c.charid), {
            "userid": userid,
            "unixtime": arrow.now(),
            "char_name": character.char_name,
            "age": character.age,
            "gender": character.gender,
            "height": character.height,
            "weight": character.weight,
            "species": character.species,
            "content": character.content,
            "rating": character.rating.code,
            "settings": settings,
        })
    except PostgresError:
        files.clear_temporary(userid)
        raise

    # Assign search tags
    searchtag.associate(userid, tags, charid=charid)

    # Make submission file
    files.make_character_directory(charid)
    files.copy(tempsubmit, files.make_resource(userid, charid, "char/submit", submitextension))

    # Make cover file
    image.make_cover(tempsubmit, files.make_resource(userid, charid, "char/cover", submitextension))

    # Make thumbnail selection file
    if thumbsize:
        image.make_cover(
            tempthumb, files.make_resource(userid, charid, "char/.thumb"))

    thumbnail.create(0, 0, 0, 0, charid=charid, remove=False)

    # Create notifications
    welcome.character_insert(userid, charid, rating=character.rating.code,
                             settings=settings)

    # Clear temporary files
    files.clear_temporary(userid)

    define.metric('increment', 'characters')

    return charid
示例#23
0
def insert(userid,
           submitid=None,
           charid=None,
           journalid=None,
           updateid=None,
           parentid=None,
           content=None):
    if submitid:
        table = "comments"
    elif charid:
        table = "charcomment"
    elif journalid:
        table = "journalcomment"
    elif updateid:
        table = "siteupdatecomment"
    else:
        raise WeasylError("Unexpected")

    if not content:
        raise WeasylError("commentInvalid")

    # Determine parent userid
    if parentid:
        parentuserid = d.engine.scalar(
            "SELECT userid FROM {table} WHERE commentid = %(parent)s".format(
                table=table),
            parent=parentid,
        )

        if parentuserid is None:
            raise WeasylError("Unexpected")
    else:
        if updateid:
            parentid = None  # parentid == 0

        parentuserid = None

    if updateid:
        otherid = d.engine.scalar(
            "SELECT userid FROM siteupdate WHERE updateid = %(update)s",
            update=updateid)

        if not otherid:
            raise WeasylError("submissionRecordMissing")
    else:
        # Determine the owner of the target
        otherid = d.engine.scalar(
            "SELECT userid FROM %s WHERE %s = %i AND settings !~ 'h'" %
            (("submission", "submitid", submitid) if submitid else
             ("character", "charid", charid) if charid else
             ("journal", "journalid", journalid)))

        # Check permissions
        if not otherid:
            raise WeasylError("submissionRecordMissing")
        elif ignoreuser.check(otherid, userid):
            raise WeasylError("pageOwnerIgnoredYou")
        elif ignoreuser.check(userid, otherid):
            raise WeasylError("youIgnoredPageOwner")

    if parentuserid and ignoreuser.check(parentuserid, userid):
        raise WeasylError("replyRecipientIgnoredYou")
    elif parentuserid and ignoreuser.check(userid, parentuserid):
        raise WeasylError("youIgnoredReplyRecipient")

    # Create comment
    if submitid:
        co = d.meta.tables['comments']
        db = d.connect()
        commentid = db.scalar(co.insert().values(
            userid=userid,
            target_sub=submitid,
            parentid=parentid or None,
            content=content,
            unixtime=arrow.utcnow()).returning(co.c.commentid))
    elif updateid:
        commentid = d.engine.scalar(
            "INSERT INTO siteupdatecomment (userid, targetid, parentid, content)"
            " VALUES (%(user)s, %(update)s, %(parent)s, %(content)s)"
            " RETURNING commentid",
            user=userid,
            update=updateid,
            parent=parentid,
            content=content,
        )
    else:
        commentid = d.engine.scalar(
            "INSERT INTO {table} (userid, targetid, parentid, content, unixtime)"
            " VALUES (%(user)s, %(target)s, %(parent)s, %(content)s, %(now)s)"
            " RETURNING commentid".format(
                table="charcomment" if charid else "journalcomment"),
            user=userid,
            target=d.get_targetid(charid, journalid),
            parent=parentid or 0,
            content=content,
            now=d.get_time(),
        )

    # Create notification
    if parentid and (userid != parentuserid):
        welcome.commentreply_insert(userid, commentid, parentuserid, parentid,
                                    submitid, charid, journalid, updateid)
    elif not parentid:
        # build a list of people this comment should notify
        # circular imports are cool and fun
        from weasyl.collection import find_owners
        notified = set(find_owners(submitid))

        # check to see who we should deliver comment notifications to
        def can_notify(other):
            other_jsonb = d.get_profile_settings(other)
            allow_notify = other_jsonb.allow_collection_notifs
            ignored = ignoreuser.check(other, userid)
            return allow_notify and not ignored

        notified = set(filter(can_notify, notified))
        # always give notification on own content
        notified.add(otherid)
        # don't give me a notification for my own comment
        notified.discard(userid)

        for other in notified:
            welcome.comment_insert(userid, commentid, other, submitid, charid,
                                   journalid, updateid)

    d.metric('increment', 'comments')

    return commentid
示例#24
0
文件: login.py 项目: weykent/weasyl
def create(form):
    # Normalize form data
    username = d.plaintext(form.username[:_USERNAME])
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck

    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month),
                                   int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if not sysname or ";" in username:
        raise WeasylError("usernameInvalid")
    if sysname in [
            "admin", "administrator", "mod", "moderator", "weasyl",
            "weasyladmin", "weasylmod", "staff", "security"
    ]:
        raise WeasylError("usernameInvalid")
    if email_exists(email):
        raise WeasylError("emailExists")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Create pending account
    token = security.generate_key(40)

    d.engine.execute(
        d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": email,
            "birthday": birthday,
            "unixtime": arrow.now(),
        })

    # Queue verification email
    emailer.append([email], None, "Weasyl Account Creation",
                   d.render("email/verify_account.html", [token, sysname]))
    d.metric('increment', 'createdusers')
示例#25
0
def create_visual(userid, submission,
                  friends_only, tags, imageURL, thumbfile,
                  submitfile, critique, create_notifications):
    premium = d.get_premium(userid)

    if imageURL:
        resp = d.http_get(imageURL, timeout=5)
        submitfile = resp.content

    # Determine filesizes
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize:
        files.clear_temporary(userid)
        raise WeasylError("submitSizeZero")
    elif thumbsize > 10 * _MEGABYTE:
        files.clear_temporary(userid)
        raise WeasylError("thumbSizeExceedsLimit")

    im = image.from_string(submitfile)
    submitextension = image.image_extension(im)
    if _limit(submitsize, submitextension, premium):
        raise WeasylError("submitSizeExceedsLimit")
    elif submitextension not in [".jpg", ".png", ".gif"]:
        raise WeasylError("submitType")
    submit_file_type = submitextension.lstrip('.')
    submit_media_item = orm.fetch_or_create_media_item(
        submitfile, file_type=submit_file_type, im=im)
    check_for_duplicate_media(userid, submit_media_item.mediaid)
    cover_media_item = submit_media_item.ensure_cover_image(im)

    # Thumbnail stuff.
    # Always create a 'generated' thumbnail from the source image.
    thumb_generated = images.make_thumbnail(im)
    if thumb_generated is im:
        thumb_generated_media_item = submit_media_item
    else:
        thumb_generated_media_item = orm.fetch_or_create_media_item(
            thumb_generated.to_buffer(format=submit_file_type), file_type=submit_file_type,
            im=thumb_generated)
    # If requested, also create a 'custom' thumbnail.
    thumb_media_item = media.make_cover_media_item(thumbfile)
    if thumb_media_item:
        thumb_custom = images.make_thumbnail(image.from_string(thumbfile))
        thumb_custom_media_item = orm.fetch_or_create_media_item(
            thumb_custom.to_buffer(format=submit_file_type), file_type=submit_file_type,
            im=thumb_custom)

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    settings = "".join(settings)

    # TODO(kailys): maintain ORM object
    db = d.connect()
    now = arrow.get()
    q = (
        d.meta.tables['submission'].insert().values([{
            "folderid": submission.folderid,
            "userid": userid,
            "unixtime": now,
            "title": submission.title,
            "content": submission.content,
            "subtype": submission.subtype,
            "rating": submission.rating.code,
            "settings": settings,
            "sorttime": now,
        }]).returning(d.meta.tables['submission'].c.submitid))
    submitid = db.scalar(q)

    orm.SubmissionMediaLink.make_or_replace_link(
        submitid, 'submission', submit_media_item)
    orm.SubmissionMediaLink.make_or_replace_link(
        submitid, 'cover', cover_media_item)
    orm.SubmissionMediaLink.make_or_replace_link(
        submitid, 'thumbnail-generated', thumb_generated_media_item)
    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item)
        orm.SubmissionMediaLink.make_or_replace_link(
            submitid, 'thumbnail-custom', thumb_custom_media_item)

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings,
                              submission.title, tags)

    d.metric('increment', 'submissions')
    d.metric('increment', 'visualsubmissions')

    return submitid
示例#26
0
def create_visual(userid, submission,
                  friends_only, tags, imageURL, thumbfile,
                  submitfile, critique, create_notifications):
    premium = d.get_premium(userid)

    if imageURL:
        resp = d.http_get(imageURL, timeout=5)
        submitfile = resp.content

    # Determine filesizes
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize:
        files.clear_temporary(userid)
        raise WeasylError("submitSizeZero")
    elif thumbsize > 10 * _MEGABYTE:
        files.clear_temporary(userid)
        raise WeasylError("thumbSizeExceedsLimit")

    im = image.from_string(submitfile)
    submitextension = image.image_extension(im)
    if _limit(submitsize, submitextension, premium):
        raise WeasylError("submitSizeExceedsLimit")
    elif submitextension not in [".jpg", ".png", ".gif"]:
        raise WeasylError("submitType")
    submit_file_type = submitextension.lstrip('.')
    submit_media_item = orm.fetch_or_create_media_item(
        submitfile, file_type=submit_file_type, im=im)
    check_for_duplicate_media(userid, submit_media_item.mediaid)
    cover_media_item = submit_media_item.ensure_cover_image(im)

    # Thumbnail stuff.
    # Always create a 'generated' thumbnail from the source image.
    thumb_generated = images.make_thumbnail(im)
    if thumb_generated is im:
        thumb_generated_media_item = submit_media_item
    else:
        thumb_generated_media_item = orm.fetch_or_create_media_item(
            thumb_generated.to_buffer(format=submit_file_type), file_type=submit_file_type,
            im=thumb_generated)
    # If requested, also create a 'custom' thumbnail.
    thumb_media_item = media.make_cover_media_item(thumbfile)
    if thumb_media_item:
        thumb_custom = images.make_thumbnail(image.from_string(thumbfile))
        thumb_custom_media_item = orm.fetch_or_create_media_item(
            thumb_custom.to_buffer(format=submit_file_type), file_type=submit_file_type,
            im=thumb_custom)

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    settings = "".join(settings)

    # TODO(kailys): maintain ORM object
    db = d.connect()
    now = arrow.get()
    q = (
        d.meta.tables['submission'].insert().values([{
            "folderid": submission.folderid,
            "userid": userid,
            "unixtime": now,
            "title": submission.title,
            "content": submission.content,
            "subtype": submission.subtype,
            "rating": submission.rating.code,
            "settings": settings,
            "sorttime": now,
        }]).returning(d.meta.tables['submission'].c.submitid))
    submitid = db.scalar(q)

    orm.SubmissionMediaLink.make_or_replace_link(
        submitid, 'submission', submit_media_item)
    orm.SubmissionMediaLink.make_or_replace_link(
        submitid, 'cover', cover_media_item)
    orm.SubmissionMediaLink.make_or_replace_link(
        submitid, 'thumbnail-generated', thumb_generated_media_item)
    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item)
        orm.SubmissionMediaLink.make_or_replace_link(
            submitid, 'thumbnail-custom', thumb_custom_media_item)

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings,
                              submission.title, tags)

    d.metric('increment', 'submissions')
    d.metric('increment', 'visualsubmissions')

    return submitid
示例#27
0
def authenticate_bcrypt(username, password, session=True):
    """
    Return a result tuple of the form (userid, error); `error` is None if the
    login was successful. Pass `session` as False to authenticate a user without
    creating a new session.

    Possible errors are:
    - "invalid"
    - "unexpected"
    - "address"
    - "banned"
    - "suspended"
    - "2fa" - Indicates the user has opted-in to 2FA. Additional authentication required.
    """
    # Check that the user entered potentially valid values for `username` and
    # `password` before attempting to authenticate them
    if not username or not password:
        return 0, "invalid"

    # Select the authentication data necessary to check that the the user-entered
    # credentials are valid
    query = d.execute(
        "SELECT ab.userid, ab.hashsum, lo.settings, lo.twofa_secret FROM authbcrypt ab"
        " RIGHT JOIN login lo USING (userid)"
        " WHERE lo.login_name = '%s'", [d.get_sysname(username)], ["single"])

    if not query:
        return 0, "invalid"

    USERID, HASHSUM, SETTINGS, TWOFA = query
    HASHSUM = HASHSUM.encode('utf-8')

    d.metric('increment', 'attemptedlogins')

    unicode_success = bcrypt.checkpw(password.encode('utf-8'), HASHSUM)
    if not unicode_success and not bcrypt.checkpw(
            d.plaintext(password).encode('utf-8'), HASHSUM):
        # Log the failed login attempt in a security log if the account the user
        # attempted to log into is a privileged account
        if USERID in staff.MODS:
            d.append_to_log('login.fail', userid=USERID, ip=d.get_address())
            d.metric('increment', 'failedlogins')

        # Return a zero userid and an error code (indicating the entered password
        # was incorrect)
        return 0, "invalid"
    elif "b" in SETTINGS:
        # Return the proper userid and an error code (indicating the user's account
        # has been banned)
        return USERID, "banned"
    elif "s" in SETTINGS:
        suspension = moderation.get_suspension(USERID)

        if d.get_time() > suspension.release:
            d.execute(
                "UPDATE login SET settings = REPLACE(settings, 's', '') WHERE userid = %i",
                [USERID])
            d.execute("DELETE FROM suspension WHERE userid = %i", [USERID])
            d.get_login_settings.invalidate(USERID)
        else:
            # Return the proper userid and an error code (indicating the user's
            # account has been temporarily suspended)
            return USERID, "suspended"

    # Attempt to create a new session if `session` is True, then log the signin
    # if it succeeded.
    if session:
        # If the user's record has ``login.twofa_secret`` set (not nulled), return that password authentication succeeded.
        if TWOFA:
            return USERID, "2fa"
        else:
            signin(USERID)

    status = None
    if not unicode_success:
        # Oops; the user's password was stored badly, but they did successfully authenticate.
        status = 'unicode-failure'
    # Either way, authentication succeeded, so return the userid and a status.
    return USERID, status
示例#28
0
文件: login.py 项目: Weasyl/weasyl
def authenticate_bcrypt(username, password, request, ip_address=None, user_agent=None):
    """
    Return a result tuple of the form (userid, error); `error` is None if the
    login was successful. Pass None as the `request` to authenticate a user
    without creating a new session.

    :param username: The username of the user attempting authentication.
    :param password: The user's claimed password to check against the stored hash.
    :param ip_address: The address requesting authentication.
    :param user_agent: The user agent string of the submitting client.

    Possible errors are:
    - "invalid"
    - "unexpected"
    - "address"
    - "banned"
    - "suspended"
    - "2fa" - Indicates the user has opted-in to 2FA. Additional authentication required.
    """
    # Check that the user entered potentially valid values for `username` and
    # `password` before attempting to authenticate them
    if not username or not password:
        return 0, "invalid"

    # Select the authentication data necessary to check that the the user-entered
    # credentials are valid
    query = d.execute("SELECT ab.userid, ab.hashsum, lo.settings, lo.twofa_secret FROM authbcrypt ab"
                      " RIGHT JOIN login lo USING (userid)"
                      " WHERE lo.login_name = '%s'", [d.get_sysname(username)], ["single"])

    if not query:
        return 0, "invalid"

    USERID, HASHSUM, SETTINGS, TWOFA = query
    HASHSUM = HASHSUM.encode('utf-8')

    d.metric('increment', 'attemptedlogins')

    unicode_success = bcrypt.checkpw(password.encode('utf-8'), HASHSUM)
    if not unicode_success and not bcrypt.checkpw(d.plaintext(password).encode('utf-8'), HASHSUM):
        # Log the failed login attempt in a security log if the account the user
        # attempted to log into is a privileged account
        if USERID in staff.MODS:
            d.append_to_log('login.fail', userid=USERID, ip=d.get_address())
            d.metric('increment', 'failedlogins')

        # Return a zero userid and an error code (indicating the entered password
        # was incorrect)
        return 0, "invalid"
    elif "b" in SETTINGS:
        # Return the proper userid and an error code (indicating the user's account
        # has been banned)
        return USERID, "banned"
    elif "s" in SETTINGS:
        suspension = moderation.get_suspension(USERID)

        if d.get_time() > suspension.release:
            d.execute("UPDATE login SET settings = REPLACE(settings, 's', '') WHERE userid = %i", [USERID])
            d.execute("DELETE FROM suspension WHERE userid = %i", [USERID])
            d.get_login_settings.invalidate(USERID)
        else:
            # Return the proper userid and an error code (indicating the user's
            # account has been temporarily suspended)
            return USERID, "suspended"

    # Attempt to create a new session if this is a request to log in, then log the signin
    # if it succeeded.
    if request is not None:
        # If the user's record has ``login.twofa_secret`` set (not nulled), return that password authentication succeeded.
        if TWOFA:
            if not isinstance(request.weasyl_session, GuestSession):
                request.pg_connection.delete(request.weasyl_session)
                request.pg_connection.flush()
            request.weasyl_session = create_session(None)
            request.weasyl_session.additional_data = {}
            return USERID, "2fa"
        else:
            signin(request, USERID, ip_address=ip_address, user_agent=user_agent)

    status = None
    if not unicode_success:
        # Oops; the user's password was stored badly, but they did successfully authenticate.
        status = 'unicode-failure'
    # Either way, authentication succeeded, so return the userid and a status.
    return USERID, status
示例#29
0
文件: login.py 项目: Syfaro/weasyl
def create(form):
    # Normalize form data
    username = d.plaintext(form.username[:_USERNAME])
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck

    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if is_email_blacklisted(email):
        raise WeasylError("emailBlacklisted")
    if not sysname or ";" in username:
        raise WeasylError("usernameInvalid")
    if sysname in ["admin", "administrator", "mod", "moderator", "weasyl",
                   "weasyladmin", "weasylmod", "staff", "security"]:
        raise WeasylError("usernameInvalid")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Account verification token
    token = security.generate_key(40)

    # Only attempt to create the account if the email is unused (as defined by the function)
    if not email_exists(email):
        # Create pending account
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": email,
            "birthday": birthday,
            "unixtime": arrow.now(),
        })

        # Queue verification email
        emailer.append([email], None, "Weasyl Account Creation", d.render(
            "email/verify_account.html", [token, sysname]))
        d.metric('increment', 'createdusers')
    else:
        # Store a dummy record to support plausible deniability of email addresses
        # So "reserve" the username, but mark the record invalid, and use the token to satisfy the uniqueness
        #  constraint for the email field (e.g., if there is already a valid, pending row in the table).
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": token,
            "birthday": arrow.now(),
            "unixtime": arrow.now(),
            "invalid": True,
        })
        # The email address in question is already in use in either `login` or `logincreate`;
        #   let the already registered user know this via email (perhaps they forgot their username/password)
        query_username_login = d.engine.scalar("SELECT login_name FROM login WHERE email = %(email)s", email=email)
        query_username_logincreate = d.engine.scalar("SELECT login_name FROM logincreate WHERE email = %(email)s", email=email)
        emailer.append([email], None, "Weasyl Account Creation - Account Already Exists", d.render(
            "email/email_in_use_account_creation.html", [query_username_login or query_username_logincreate]))
示例#30
0
def create(userid, character, friends, tags, thumbfile, submitfile):
    # Make temporary filenames
    tempthumb = files.get_temporary(userid, "thumb")
    tempsubmit = files.get_temporary(userid, "char")

    # Determine filesizes
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    # Check invalid title or rating
    if not character.char_name:
        raise WeasylError("characterNameInvalid")
    elif not character.rating:
        raise WeasylError("ratingInvalid")
    profile.check_user_rating_allowed(userid, character.rating)

    # Write temporary thumbnail file
    if thumbsize:
        files.easyupload(tempthumb, thumbfile, "image")
        thumbextension = files.get_extension_for_category(
            thumbfile, macro.ART_SUBMISSION_CATEGORY)
    else:
        thumbextension = None

    # Write temporary submission file
    if submitsize:
        files.easyupload(tempsubmit, submitfile, "image")
        submitextension = files.get_extension_for_category(
            submitfile, macro.ART_SUBMISSION_CATEGORY)
    else:
        submitextension = None

    # Check invalid file data
    if not submitsize:
        files.clear_temporary(userid)
        raise WeasylError("submitSizeZero")
    elif submitsize > 10 * _MEGABYTE:
        files.clear_temporary(userid)
        raise WeasylError("submitSizeExceedsLimit")
    elif thumbsize > 10 * _MEGABYTE:
        files.clear_temporary(userid)
        raise WeasylError("thumbSizeExceedsLimit")
    elif submitextension not in [".jpg", ".png", ".gif"]:
        files.clear_temporary(userid)
        raise WeasylError("submitType")
    elif thumbsize and thumbextension not in [".jpg", ".png", ".gif"]:
        files.clear_temporary(userid)
        raise WeasylError("thumbType")

    # Assign settings
    settings = []
    settings.append("f" if friends else "")
    settings.append(files.typeflag("submit", submitextension))
    settings.append(files.typeflag("cover", submitextension))
    settings = "".join(settings)

    # Insert submission data
    ch = define.meta.tables["character"]

    try:
        charid = define.engine.scalar(ch.insert().returning(ch.c.charid), {
            "userid": userid,
            "unixtime": arrow.now(),
            "char_name": character.char_name,
            "age": character.age,
            "gender": character.gender,
            "height": character.height,
            "weight": character.weight,
            "species": character.species,
            "content": character.content,
            "rating": character.rating.code,
            "settings": settings,
        })
    except PostgresError:
        files.clear_temporary(userid)
        raise

    # Assign search tags
    searchtag.associate(userid, tags, charid=charid)

    # Make submission file
    files.make_path(charid, "char")
    files.copy(tempsubmit, files.make_resource(userid, charid, "char/submit", submitextension))

    # Make cover file
    image.make_cover(tempsubmit, files.make_resource(userid, charid, "char/cover", submitextension))

    # Make thumbnail selection file
    if thumbsize:
        image.make_cover(
            tempthumb, files.make_resource(userid, charid, "char/.thumb"))

    thumbnail.create(userid, 0, 0, 0, 0, charid=charid, remove=False)

    # Create notifications
    welcome.character_insert(userid, charid, rating=character.rating.code,
                             settings=settings)

    # Clear temporary files
    files.clear_temporary(userid)

    define.metric('increment', 'characters')

    return charid
示例#31
0
def create_visual(userid, submission, friends_only, tags, imageURL, thumbfile,
                  submitfile, critique, create_notifications):
    if imageURL:
        resp = d.http_get(imageURL, timeout=5)
        submitfile = resp.content

    # Determine filesizes
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize:
        raise WeasylError("submitSizeZero")
    elif thumbsize > 10 * _MEGABYTE:
        raise WeasylError("thumbSizeExceedsLimit")

    im = image.from_string(submitfile)
    submitextension = image.image_extension(im)
    if submitextension not in [".jpg", ".png", ".gif"]:
        raise WeasylError("submitType")
    if _limit(submitsize, submitextension):
        raise WeasylError("submitSizeExceedsLimit")

    # Check if the submission is spam
    is_spam = _check_for_spam(submission=submission, userid=userid)

    submit_file_type = submitextension.lstrip('.')
    submit_media_item = orm.MediaItem.fetch_or_create(
        submitfile, file_type=submit_file_type, im=im)
    check_for_duplicate_media(userid, submit_media_item.mediaid)
    cover_media_item = submit_media_item.ensure_cover_image(im)

    # Thumbnail stuff.
    # Always create a 'generated' thumbnail from the source image.
    with BytesIO(submitfile) as buf:
        thumbnail_formats = images_new.get_thumbnail(buf)

    thumb_generated, thumb_generated_file_type, thumb_generated_attributes = thumbnail_formats.compatible
    thumb_generated_media_item = orm.MediaItem.fetch_or_create(
        thumb_generated,
        file_type=thumb_generated_file_type,
        attributes=thumb_generated_attributes,
    )

    if thumbnail_formats.webp is None:
        thumb_generated_media_item_webp = None
    else:
        thumb_generated, thumb_generated_file_type, thumb_generated_attributes = thumbnail_formats.webp
        thumb_generated_media_item_webp = orm.MediaItem.fetch_or_create(
            thumb_generated,
            file_type=thumb_generated_file_type,
            attributes=thumb_generated_attributes,
        )

    # If requested, also create a 'custom' thumbnail.
    thumb_media_item = media.make_cover_media_item(thumbfile)
    if thumb_media_item:
        thumb_custom = images.make_thumbnail(image.from_string(thumbfile))
        thumb_custom_media_item = orm.MediaItem.fetch_or_create(
            thumb_custom.to_buffer(format=submit_file_type),
            file_type=submit_file_type,
            im=thumb_custom)

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    settings = "".join(settings)

    # TODO(kailys): maintain ORM object
    db = d.connect()
    now = arrow.get()
    q = (d.meta.tables['submission'].insert().values([{
        "folderid":
        submission.folderid,
        "userid":
        userid,
        "unixtime":
        now,
        "title":
        submission.title,
        "content":
        submission.content,
        "subtype":
        submission.subtype,
        "rating":
        submission.rating.code,
        "settings":
        settings,
        "favorites":
        0,
        "is_spam":
        is_spam,
        "submitter_ip_address":
        submission.submitter_ip_address,
        "submitter_user_agent_id":
        submission.submitter_user_agent_id,
    }]).returning(d.meta.tables['submission'].c.submitid))
    submitid = db.scalar(q)

    orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission',
                                                 submit_media_item)
    orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover',
                                                 cover_media_item)

    orm.SubmissionMediaLink.make_or_replace_link(submitid,
                                                 'thumbnail-generated',
                                                 thumb_generated_media_item)

    if thumb_generated_media_item_webp is not None:
        orm.SubmissionMediaLink.make_or_replace_link(
            submitid, 'thumbnail-generated-webp',
            thumb_generated_media_item_webp)

    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid,
                                                     'thumbnail-source',
                                                     thumb_media_item)
        orm.SubmissionMediaLink.make_or_replace_link(submitid,
                                                     'thumbnail-custom',
                                                     thumb_custom_media_item)

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings)

    d.metric('increment', 'submissions')
    d.metric('increment', 'visualsubmissions')

    return submitid
示例#32
0
def create_multimedia(userid, submission, embedlink=None, friends_only=None,
                      tags=None, coverfile=None, thumbfile=None, submitfile=None,
                      critique=False, create_notifications=True, auto_thumb=False):
    premium = d.get_premium(userid)
    embedlink = embedlink.strip()

    # Determine filesizes
    coversize = len(coverfile)
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize and not embedlink:
        raise WeasylError("submitSizeZero")
    elif embedlink and not embed.check_valid(embedlink):
        raise WeasylError("embedlinkInvalid")
    elif coversize > 10 * _MEGABYTE:
        raise WeasylError("coverSizeExceedsLimit")
    elif thumbsize > 10 * _MEGABYTE:
        raise WeasylError("thumbSizeExceedsLimit")

    if submitsize:
        submitextension = files.get_extension_for_category(submitfile, m.MULTIMEDIA_SUBMISSION_CATEGORY)
        if submitextension is None:
            raise WeasylError("submitType")
        elif submitextension not in [".mp3", ".swf"] and not embedlink:
            raise WeasylError("submitType")
        elif _limit(submitsize, submitextension, premium):
            raise WeasylError("submitSizeExceedsLimit")
        submit_media_item = orm.fetch_or_create_media_item(
            submitfile, file_type=submitextension.lstrip('.'))
        check_for_duplicate_media(userid, submit_media_item.mediaid)
    else:
        submit_media_item = None

    thumb_media_item = media.make_cover_media_item(thumbfile)
    cover_media_item = media.make_cover_media_item(coverfile)
    if cover_media_item and not thumb_media_item:
        thumb_media_item = cover_media_item

    tempthumb_media_item = None
    im = None
    if auto_thumb:
        if thumbsize == 0 and coversize == 0:
            # Fetch default thumbnail from source if available
            thumb_url = embed.thumbnail(embedlink)
            if thumb_url:
                resp = d.http_get(thumb_url, timeout=5)
                im = image.from_string(resp.content)
    if not im and (thumbsize or coversize):
        im = image.from_string(thumbfile or coverfile)
    if im:
        tempthumb = images.make_thumbnail(im)
        tempthumb_type = images.image_file_type(tempthumb)
        tempthumb_media_item = orm.fetch_or_create_media_item(
            tempthumb.to_buffer(format=tempthumb_type),
            file_type=tempthumb_type,
            im=tempthumb)

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    settings.append("v" if embedlink else "")
    settings = "".join(settings)

    # Inject embedlink
    if embedlink:
        submission.content = "".join([embedlink, "\n", submission.content])

    # Create submission
    db = d.connect()
    now = arrow.get()
    try:
        q = (
            d.meta.tables['submission'].insert().values([{
                "folderid": submission.folderid,
                "userid": userid,
                "unixtime": now,
                "title": submission.title,
                "content": submission.content,
                "subtype": submission.subtype,
                "rating": submission.rating,
                "settings": settings,
                "sorttime": now,
            }])
            .returning(d.meta.tables['submission'].c.submitid))
        submitid = db.scalar(q)
    except PostgresError:
        files.clear_temporary(userid)
        raise

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    if submit_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission', submit_media_item)
    if cover_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover', cover_media_item)
    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item)
    if tempthumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-custom',
                                                     tempthumb_media_item)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings,
                              submission.title, tags)

    # Clear temporary files
    files.clear_temporary(userid)

    d.metric('increment', 'submissions')
    d.metric('increment', 'multimediasubmissions')

    return submitid, bool(thumb_media_item)
示例#33
0
def create(form):
    # Normalize form data
    username = clean_display_name(form.username)
    sysname = d.get_sysname(username)

    email = emailer.normalize_address(form.email)
    emailcheck = emailer.normalize_address(form.emailcheck)

    password = form.password
    passcheck = form.passcheck
    if form.day and form.month and form.year:
        try:
            birthday = arrow.Arrow(int(form.year), int(form.month), int(form.day))
        except ValueError:
            raise WeasylError("birthdayInvalid")
    else:
        birthday = None

    # Check mismatched form data
    if password != passcheck:
        raise WeasylError("passwordMismatch")
    if email != emailcheck:
        raise WeasylError("emailMismatch")

    # Check invalid form data
    if birthday is None or d.age_in_years(birthday) < 13:
        raise WeasylError("birthdayInvalid")
    if not password_secure(password):
        raise WeasylError("passwordInsecure")
    if not email:
        raise WeasylError("emailInvalid")
    if is_email_blacklisted(email):
        raise WeasylError("emailBlacklisted")
    if username_exists(sysname):
        raise WeasylError("usernameExists")

    # Account verification token
    token = security.generate_key(40)

    # Only attempt to create the account if the email is unused (as defined by the function)
    if not email_exists(email):
        # Create pending account
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": email,
            "birthday": birthday,
        })

        # Send verification email
        emailer.send(email, "Weasyl Account Creation", d.render(
            "email/verify_account.html", [token, sysname]))
        d.metric('increment', 'createdusers')
    else:
        # Store a dummy record to support plausible deniability of email addresses
        # So "reserve" the username, but mark the record invalid, and use the token to satisfy the uniqueness
        #  constraint for the email field (e.g., if there is already a valid, pending row in the table).
        d.engine.execute(d.meta.tables["logincreate"].insert(), {
            "token": token,
            "username": username,
            "login_name": sysname,
            "hashpass": passhash(password),
            "email": token,
            "birthday": arrow.now(),
            "invalid": True,
            # So we have a way for admins to determine which email address collided in the View Pending Accounts Page
            "invalid_email_addr": email,
        })
        # The email address in question is already in use in either `login` or `logincreate`;
        #   let the already registered user know this via email (perhaps they forgot their username/password)
        query_username_login = d.engine.scalar("SELECT login_name FROM login WHERE email = %(email)s", email=email)
        query_username_logincreate = d.engine.scalar("SELECT login_name FROM logincreate WHERE email = %(email)s", email=email)
        emailer.send(email, "Weasyl Account Creation - Account Already Exists", d.render(
            "email/email_in_use_account_creation.html", [query_username_login or query_username_logincreate]))
示例#34
0
def create_literary(userid,
                    submission,
                    embedlink=None,
                    friends_only=False,
                    tags=None,
                    coverfile=None,
                    thumbfile=None,
                    submitfile=None,
                    critique=False,
                    create_notifications=True):
    if embedlink:
        check_google_doc_embed_data(embedlink)

    # Determine filesizes
    coversize = len(coverfile)
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize and not embedlink:
        raise WeasylError("submitSizeZero")
    elif coversize > 10 * _MEGABYTE:
        raise WeasylError("coverSizeExceedsLimit")
    elif thumbsize > 10 * _MEGABYTE:
        raise WeasylError("thumbSizeExceedsLimit")

    if submitsize:
        submitextension = files.get_extension_for_category(
            submitfile, m.TEXT_SUBMISSION_CATEGORY)
        if submitextension is None:
            raise WeasylError("submitType")
        if _limit(submitsize, submitextension):
            raise WeasylError("submitSizeExceedsLimit")
        submit_media_item = orm.MediaItem.fetch_or_create(
            submitfile, file_type=submitextension.lstrip('.'))
        check_for_duplicate_media(userid, submit_media_item.mediaid)
    else:
        submit_media_item = None

    # Check if the submission is spam
    is_spam = _check_for_spam(submission=submission, userid=userid)

    thumb_media_item = media.make_cover_media_item(thumbfile)
    cover_media_item = media.make_cover_media_item(coverfile)
    if cover_media_item and not thumb_media_item:
        thumb_media_item = cover_media_item

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    if embedlink:
        settings.append('D')
    settings = "".join(settings)

    # Create submission
    # TODO(kailys): use ORM object
    db = d.connect()
    now = arrow.get()
    q = (d.meta.tables['submission'].insert().values([{
        "folderid":
        submission.folderid,
        "userid":
        userid,
        "unixtime":
        now,
        "title":
        submission.title,
        "content":
        submission.content,
        "subtype":
        submission.subtype,
        "rating":
        submission.rating.code,
        "settings":
        settings,
        "favorites":
        0,
        "is_spam":
        is_spam,
        "submitter_ip_address":
        submission.submitter_ip_address,
        "submitter_user_agent_id":
        submission.submitter_user_agent_id,
    }]).returning(d.meta.tables['submission'].c.submitid))
    submitid = db.scalar(q)
    if embedlink:
        q = (d.meta.tables['google_doc_embeds'].insert().values(
            submitid=submitid, embed_url=embedlink))
        db.execute(q)

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    if submit_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission',
                                                     submit_media_item)
    if cover_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover',
                                                     cover_media_item)
    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid,
                                                     'thumbnail-source',
                                                     thumb_media_item)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings)

    d.metric('increment', 'submissions')
    d.metric('increment', 'literarysubmissions')

    return submitid, bool(thumb_media_item)
示例#35
0
def insert(userid,
           submitid=None,
           charid=None,
           journalid=None,
           parentid=None,
           content=None):
    if not submitid and not charid and not journalid:
        raise WeasylError("Unexpected")
    elif not content:
        raise WeasylError("commentInvalid")

    # Determine indent and parentuserid
    if parentid:
        query = d.execute(
            "SELECT userid, indent FROM %s WHERE commentid = %i", [
                "comments" if submitid else
                "charcomment" if charid else "journalcomment", parentid
            ],
            options="single")

        if not query:
            raise WeasylError("Unexpected")

        indent = query[1] + 1
        parentuserid = query[0]
    else:
        indent = 0
        parentuserid = None

    # Determine otherid
    otherid = d.execute(
        "SELECT userid FROM %s WHERE %s = %i AND settings !~ 'h'",
        ["submission", "submitid", submitid]
        if submitid else ["character", "charid", charid]
        if charid else ["journal", "journalid", journalid],
        options="element")

    # Check permissions
    if not otherid:
        raise WeasylError("submissionRecordMissing")
    elif ignoreuser.check(otherid, userid):
        raise WeasylError("pageOwnerIgnoredYou")
    elif ignoreuser.check(userid, otherid):
        raise WeasylError("youIgnoredPageOwner")
    elif parentuserid and ignoreuser.check(parentuserid, userid):
        raise WeasylError("replyRecipientIgnoredYou")
    elif parentuserid and ignoreuser.check(userid, parentuserid):
        raise WeasylError("youIgnoredReplyRecipient")

    # Create comment
    if submitid:
        co = d.meta.tables['comments']
        db = d.connect()
        commentid = db.scalar(co.insert().values(userid=userid,
                                                 target_sub=submitid,
                                                 parentid=parentid or None,
                                                 content=content,
                                                 unixtime=arrow.utcnow(),
                                                 indent=indent).returning(
                                                     co.c.commentid))
    else:
        commentid = d.execute(
            "INSERT INTO %s (userid, targetid, parentid, "
            "content, unixtime, indent) VALUES (%i, %i, %i, '%s', %i, %i) RETURNING "
            "commentid", [
                "charcomment" if charid else "journalcomment", userid,
                d.get_targetid(submitid, charid, journalid), parentid, content,
                d.get_time(), indent
            ],
            options="element")

    # Create notification
    if parentid and (userid != parentuserid):
        welcome.commentreply_insert(userid, commentid, parentuserid, parentid,
                                    submitid, charid, journalid)
    elif not parentid:
        # build a list of people this comment should notify
        # circular imports are cool and fun
        from weasyl.collection import find_owners
        notified = set(find_owners(submitid))

        # check to see who we should deliver comment notifications to
        def can_notify(other):
            other_jsonb = d.get_profile_settings(other)
            allow_notify = other_jsonb.allow_collection_notifs
            ignored = ignoreuser.check(other, userid)
            return allow_notify and not ignored

        notified = set(filter(can_notify, notified))
        # always give notification on own content
        notified.add(otherid)
        # don't give me a notification for my own comment
        notified.discard(userid)

        for other in notified:
            welcome.comment_insert(userid, commentid, other, submitid, charid,
                                   journalid)

    d.metric('increment', 'comments')

    return commentid
示例#36
0
def create_multimedia(userid,
                      submission,
                      embedlink=None,
                      friends_only=None,
                      tags=None,
                      coverfile=None,
                      thumbfile=None,
                      submitfile=None,
                      critique=False,
                      create_notifications=True,
                      auto_thumb=False):
    embedlink = embedlink.strip()

    # Determine filesizes
    coversize = len(coverfile)
    thumbsize = len(thumbfile)
    submitsize = len(submitfile)

    if not submitsize and not embedlink:
        raise WeasylError("submitSizeZero")
    elif embedlink and not embed.check_valid(embedlink):
        raise WeasylError("embedlinkInvalid")
    elif coversize > 10 * _MEGABYTE:
        raise WeasylError("coverSizeExceedsLimit")
    elif thumbsize > 10 * _MEGABYTE:
        raise WeasylError("thumbSizeExceedsLimit")

    if submitsize:
        submitextension = files.get_extension_for_category(
            submitfile, m.MULTIMEDIA_SUBMISSION_CATEGORY)
        if submitextension is None:
            raise WeasylError("submitType")
        elif submitextension not in [".mp3", ".swf"] and not embedlink:
            raise WeasylError("submitType")
        elif _limit(submitsize, submitextension):
            raise WeasylError("submitSizeExceedsLimit")
        submit_media_item = orm.MediaItem.fetch_or_create(
            submitfile, file_type=submitextension.lstrip('.'))
        check_for_duplicate_media(userid, submit_media_item.mediaid)
    else:
        submit_media_item = None

    # Check if the submission is spam
    is_spam = _check_for_spam(submission=submission, userid=userid)

    thumb_media_item = media.make_cover_media_item(thumbfile)
    cover_media_item = media.make_cover_media_item(coverfile)
    if cover_media_item and not thumb_media_item:
        thumb_media_item = cover_media_item

    tempthumb_media_item = None
    im = None
    if auto_thumb:
        if thumbsize == 0 and coversize == 0:
            # Fetch default thumbnail from source if available
            thumb_url = embed.thumbnail(embedlink)
            if thumb_url:
                resp = d.http_get(thumb_url, timeout=5)
                im = image.from_string(resp.content)
    if not im and (thumbsize or coversize):
        im = image.from_string(thumbfile or coverfile)
    if im:
        tempthumb = images.make_thumbnail(im)
        tempthumb_type = images.image_file_type(tempthumb)
        tempthumb_media_item = orm.MediaItem.fetch_or_create(
            tempthumb.to_buffer(format=tempthumb_type),
            file_type=tempthumb_type,
            im=tempthumb)

    # Assign settings
    settings = []
    settings.append("f" if friends_only else "")
    settings.append("q" if critique else "")
    settings.append("v" if embedlink else "")
    settings = "".join(settings)

    # Inject embedlink
    if embedlink:
        submission.content = "".join([embedlink, "\n", submission.content])

    # Create submission
    db = d.connect()
    now = arrow.get()
    q = (d.meta.tables['submission'].insert().values([{
        "folderid":
        submission.folderid,
        "userid":
        userid,
        "unixtime":
        now,
        "title":
        submission.title,
        "content":
        submission.content,
        "subtype":
        submission.subtype,
        "rating":
        submission.rating,
        "settings":
        settings,
        "favorites":
        0,
        "is_spam":
        is_spam,
        "submitter_ip_address":
        submission.submitter_ip_address,
        "submitter_user_agent_id":
        submission.submitter_user_agent_id,
    }]).returning(d.meta.tables['submission'].c.submitid))
    submitid = db.scalar(q)

    # Assign search tags
    searchtag.associate(userid, tags, submitid=submitid)

    if submit_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission',
                                                     submit_media_item)
    if cover_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover',
                                                     cover_media_item)
    if thumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid,
                                                     'thumbnail-source',
                                                     thumb_media_item)
    if tempthumb_media_item:
        orm.SubmissionMediaLink.make_or_replace_link(submitid,
                                                     'thumbnail-custom',
                                                     tempthumb_media_item)

    # Create notifications
    if create_notifications:
        _create_notifications(userid, submitid, submission.rating, settings)

    d.metric('increment', 'submissions')
    d.metric('increment', 'multimediasubmissions')

    return submitid, bool(thumb_media_item)