log.Error("Couldn't parse signup form request: %v\n", err)
return nil, ErrBadFormData
}
err = app.formDecoder.Decode(&ur, r.PostForm)
if err != nil {
log.Error("Couldn't decode signup form request: %v\n", err)
return nil, ErrBadFormData
}
}
return signupWithRegistration(app, ur, w, r)
}
func signupWithRegistration(app *App, signup userRegistration, w http.ResponseWriter, r *http.Request) (*AuthUser, error) {
reqJSON := IsJSON(r)
// Validate required params (alias)
if signup.Alias == "" {
return nil, impart.HTTPError{http.StatusBadRequest, "A username is required."}
}
if signup.Pass == "" {
return nil, impart.HTTPError{http.StatusBadRequest, "A password is required."}
}
var desiredUsername string
if signup.Normalize {
// With this option we simply conform the username to what we expect
// without complaining. Since they might've done something funny, like
// enter: write.as/Way Out There, we'll use their raw input for the new
// collection name and sanitize for the slug / username.
desiredUsername = signup.Alias
signup.Alias = getSlug(signup.Alias, "")
}
if !author.IsValidUsername(app.cfg, signup.Alias) {
// Ensure the username is syntactically correct.
return nil, impart.HTTPError{http.StatusPreconditionFailed, "Username is reserved or isn't valid. It must be at least 3 characters long, and can only include letters, numbers, and hyphens."}
log.Error("Couldn't add follower in DB: %v\n", err)
return
}
}
err = t.Commit()
if err != nil {
t.Rollback()
log.Error("Rolling back after Commit(): %v\n", err)
return
}
} else if isUnfollow {
// Remove follower locally
_, err = app.db.Exec("DELETE FROM remotefollows WHERE collection_id = ? AND remote_user_id = (SELECT id FROM remoteusers WHERE actor_id = ?)", c.ID, to.String())
if err != nil {
log.Error("Couldn't remove follower from DB: %v\n", err)
}
}
}()
return nil
}
func makeActivityPost(hostName string, p *activitystreams.Person, url string, m interface{}) error {
// If this is true, the human programmers screwed up. So ask for a bug report and fail, fail, fail
log.Error("[PROGRAMMER ERROR] WARNING: Collection.hostName is empty! Federation and many other things will fail! If you're seeing this in the wild, please report this bug and let us know what you were doing just before this: https://github.com/writeas/writefreely/issues/new?template=bug_report.md")
}
if isSingleUser {
return c.hostName + "/"
}
return fmt.Sprintf("%s/%s/", c.hostName, c.Alias)
}
// PrevPageURL provides a full URL for the previous page of collection posts,
log.Error("Couldn't decode readReq form request: %v\n", err)
return ErrBadFormData
}
}
if readReq.Alias == "" {
return impart.HTTPError{http.StatusBadRequest, "Need a collection `alias` to read."}
}
if readReq.Pass == "" {
return impart.HTTPError{http.StatusBadRequest, "Please supply a password."}
}
var collHashedPass []byte
err := app.db.QueryRow("SELECT password FROM collectionpasswords INNER JOIN collections ON id = collection_id WHERE alias = ?", readReq.Alias).Scan(&collHashedPass)
if err != nil {
if err == sql.ErrNoRows {
log.Error("No collectionpassword found when trying to read collection %s", readReq.Alias)
return impart.HTTPError{http.StatusInternalServerError, "Something went very wrong. The humans have been alerted."}
}
return err
}
if !auth.Authenticated(collHashedPass, []byte(readReq.Pass)) {
err := db.QueryRow("SELECT username, password, email, created, status FROM users WHERE id = ?", id).Scan(&u.Username, &u.HashedPass, &u.Email, &u.Created, &u.Status)
switch {
case err == sql.ErrNoRows:
return nil, ErrUserNotFound
case err != nil:
log.Error("Couldn't SELECT user password: %v", err)
return nil, err
}
return u, nil
}
// IsUserSilenced returns true if the user account associated with id is
err := db.QueryRow("SELECT id, password, email, created, status FROM users WHERE id = ?", u.ID).Scan(&u.ID, &u.HashedPass, &u.Email, &u.Created, &u.Status)
err := db.QueryRow("SELECT username, one_time FROM accesstokens LEFT JOIN users ON user_id = id WHERE token LIKE ? AND (expires IS NULL OR expires > "+db.now()+")", t).Scan(&username, &oneTime)
err := db.QueryRow("SELECT user_id, username, one_time FROM accesstokens LEFT JOIN users ON user_id = id WHERE token LIKE ? AND (expires IS NULL OR expires > "+db.now()+")", t).Scan(&userID, &username, &oneTime)
err := db.QueryRow("SELECT user_id, sudo, one_time FROM accesstokens WHERE token LIKE ? AND (expires IS NULL OR expires > "+db.now()+")", t).Scan(&userID, &sudo, &oneTime)
err := db.QueryRow("SELECT token FROM accesstokens WHERE user_id = ? AND (expires IS NULL OR expires > "+db.now()+") ORDER BY created DESC LIMIT 1", userID).Scan(&t)
switch {
case err == sql.ErrNoRows:
return ""
case err != nil:
log.Error("Failed selecting from accesstoken: %v", err)
return ""
}
u, err := uuid.Parse(t)
if err != nil {
return ""
}
return u.String()
}
// GetAccessToken creates a new non-expiring, valid access token for the given
log.Error("Unable to insert render_mathjax value: %v", err)
return err
}
} else {
_, err = db.Exec("DELETE FROM collectionattributes WHERE collection_id = ? AND attribute = ?", collID, "render_mathjax")
if err != nil {
log.Error("Unable to delete render_mathjax value: %v", err)
return err
}
}
// Update Monetization value
if c.Monetization != nil {
skipUpdate := false
if *c.Monetization != "" {
// Strip away any excess spaces
trimmed := strings.TrimSpace(*c.Monetization)
// Only update value when it starts with "$", per spec: https://paymentpointers.org
if strings.HasPrefix(trimmed, "$") {
c.Monetization = &trimmed
} else {
// Value appears invalid, so don't update
skipUpdate = true
}
}
if !skipUpdate {
_, err = db.Exec("INSERT INTO collectionattributes (collection_id, attribute, value) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE value = ?", collID, "monetization_pointer", *c.Monetization, *c.Monetization)
if err != nil {
log.Error("Unable to insert monetization_pointer value: %v", err)
return err
}
}
}
// Update rest of the collection data
res, err = db.Exec("UPDATE collections SET "+q.Updates+" WHERE "+q.Conditions, q.Params...)
if err != nil {
log.Error("Unable to update collection: %v", err)
return err
}
rowsAffected, _ = res.RowsAffected()
if !changed || rowsAffected == 0 {
// Show the correct error message if nothing was updated
var dummy int
err := db.QueryRow("SELECT 1 FROM collections WHERE alias = ? AND owner_id = ?", alias, c.OwnerID).Scan(&dummy)
switch {
case err == sql.ErrNoRows:
return ErrUnauthorizedEditPost
case err != nil:
log.Error("Failed selecting from collections: %v", err)
}
if !updatePass {
return nil
}
}
if updatePass {
hashedPass, err := auth.HashPass([]byte(c.Pass))
if err != nil {
log.Error("Unable to create hash: %s", err)
return impart.HTTPError{http.StatusInternalServerError, "Could not create password hash."}
}
if db.driverName == driverSQLite {
_, err = db.Exec("INSERT OR REPLACE INTO collectionpasswords (collection_id, password) VALUES ((SELECT id FROM collections WHERE alias = ?), ?)", alias, hashedPass)
} else {
_, err = db.Exec("INSERT INTO collectionpasswords (collection_id, password) VALUES ((SELECT id FROM collections WHERE alias = ?), ?) "+db.upsert("collection_id")+" password = ?", alias, hashedPass, hashedPass)
// TODO: add slight logic difference to getPost / one func
var ownerName sql.NullString
p := &Post{}
row := db.QueryRow("SELECT "+postCols+", (SELECT username FROM users WHERE users.id = posts.owner_id) AS username FROM posts WHERE id = ? LIMIT 1", id)
row = db.QueryRow("SELECT "+postCols+", (SELECT username FROM users WHERE users.id = posts.owner_id) AS username FROM posts WHERE "+where+" LIMIT 1", params...)
rows, err := db.Query("SELECT "+postCols+" FROM posts WHERE collection_id = ? "+pinnedCondition+" "+timeCondition+" ORDER BY created "+order+limitStr, collID)
rows, err = db.Query("SELECT "+postCols+" FROM posts WHERE collection_id = ? AND LOWER(content) regexp ? "+timeCondition+" ORDER BY created "+order+limitStr, collID, `.*#`+strings.ToLower(tag)+`\b.*`)
} else {
rows, err = db.Query("SELECT "+postCols+" FROM posts WHERE collection_id = ? AND LOWER(content) RLIKE ? "+timeCondition+" ORDER BY created "+order+limitStr, collID, "#"+strings.ToLower(tag)+"[[:>:]]")
rows, err := db.Query("SELECT actor_id, inbox, shared_inbox FROM remotefollows f INNER JOIN remoteusers u ON f.remote_user_id = u.id WHERE collection_id = ?", c.ID)
if err != nil {
log.Error("Failed selecting from followers: %v", err)
// FIXME: sqlite-backed instances don't include ellipsis on truncated titles
timeCondition := ""
if !includeFuture {
timeCondition = "AND created <= " + db.now()
}
rows, err := db.Query("SELECT id, slug, title, "+db.clip("content", 80)+", pinned_position FROM posts WHERE collection_id = ? AND pinned_position IS NOT NULL "+timeCondition+" ORDER BY pinned_position ASC", coll.ID)
return nil, impart.HTTPError{http.StatusInternalServerError, "You don't seem to have any blogs; they might've moved to another account. Try logging out and logging into your other account."}
func (db *datastore) GetTopPosts(u *User, alias string) (*[]PublicPost, error) {
params := []interface{}{u.ID}
where := ""
if alias != "" {
where = " AND alias = ?"
params = append(params, alias)
}
rows, err := db.Query("SELECT p.id, p.slug, p.view_count, p.title, c.alias, c.title, c.description, c.view_count FROM posts p LEFT JOIN collections c ON p.collection_id = c.id WHERE p.owner_id = ?"+where+" ORDER BY p.view_count DESC, created DESC LIMIT 25", params...)
if err != nil {
log.Error("Failed selecting from posts: %v", err)
return nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't retrieve user top posts."}
rows, err := db.Query("SELECT id, view_count, title, created, updated, content FROM posts WHERE owner_id = ? AND collection_id IS NULL ORDER BY created DESC", u.ID)
if err != nil {
log.Error("Failed selecting from posts: %v", err)
return nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't retrieve user anonymous posts."}
rows, err := db.Query("SELECT p.id, p.slug, p.view_count, p.title, p.created, p.updated, p.content, p.text_appearance, p.language, p.rtl, c.alias, c.title, c.description, c.view_count FROM posts p LEFT JOIN collections c ON collection_id = c.id WHERE p.owner_id = ? ORDER BY created ASC", u.ID)
if err != nil {
log.Error("Failed selecting from posts: %v", err)
return nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't retrieve user posts."}