<commit_before>package main

import "strconv"

// GetDialogs fetches dialogs from db for user with offset
func (i *Impl) GetDialogs(userID string, offset int) []DialogJSON {
	dialogs := []DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
		ORDER BY c.last_message_id DESC
    LIMIT 10
    OFFSET ?
  `, userID, offset).Find(&dialogs)
	return dialogs
}

// ShowDialog gets one dialog for user
func (i *Impl) ShowDialog(userID string, dialogID int) DialogJSON {
	dialog := DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    WHERE c.id = ?
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
  `, userID, dialogID).Find(&dialog)

	i.UpdateLastMessage(userID, dialogID)

	return dialog
}

// IndexMessages fetches messages for the dialog
func (i *Impl) IndexMessages(userID string, dialogID int, offset int) []MessageJSON {
	messages := []MessageJSON{}
	i.DB.Raw(`
    SELECT * FROM messages
    WHERE messages.dialog_id = ?
    ORDER BY messages.id DESC
    LIMIT 10
    OFFSET ?
  `, dialogID, offset).Find(&messages)

	i.UpdateLastMessage(userID, dialogID)
	return messages
}

// ShowUser fetches user and number of unread dialogs
func (i *Impl) ShowUser(userID string) UserJSON {
	user := UserJSON{}
	user.ID, _ = strconv.Atoi(userID)
	dialogsCount := 0
	i.DB.Raw(`
		SELECT COUNT(dialogs.id)
		FROM dialogs, dialog_users
		WHERE
	  dialogs.last_message_id > dialog_users.last_seen_message_id AND
	  dialog_users.user_id = ? AND
	  dialog_users.dialog_id = dialogs.id
	`, user.ID).Row().Scan(&dialogsCount)
	user.DialogsCount = dialogsCount
	return user
}

// CreateMessage inserts message in db
func (i *Impl) CreateMessage(userID string, dialogID int, message Message) (Message, error) {
	message.DialogID = dialogID
	message.UserID, _ = strconv.Atoi(userID)

	if err := i.DB.Save(&message).Error; err != nil {
		return message, err
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ?, updated_at = now() WHERE dialogs.id = ?", message.ID, message.DialogID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", message.ID, message.DialogID, message.UserID)

	return message, nil
}

// CreateDialog creates dialog. If ony two people are present, it uses existing dialog
func (i *Impl) CreateDialog(userID string, params DialogCreateJSON) (Dialog, error) {
	dialog, err := i.FindDialogByUserIds(params)
	if err != nil {
		dialog := Dialog{}
		dialog.Name = params.Name
		if err := i.DB.Save(&dialog).Error; err != nil {
			return dialog, err
		}
	}

	message := Message{}
	message.DialogID = dialog.ID
	message.Text = params.Message
	message.UserID, _ = strconv.Atoi(userID)

	if err := i.DB.Save(&message).Error; err != nil {
		return dialog, err
	}

	for _, element := range params.UserIds {
		i.DB.Exec("INSERT INTO dialog_users (dialog_id, user_id, last_seen_message_id) VALUES (?, ?, 0)", dialog.ID, element)
	}
	i.DB.Exec("UPDATE dialogs SET last_message_id = ? WHERE id = ?", message.ID, dialog.ID)
	dialog.LastMessageID = message.ID

	return dialog, nil
}

// FindDialogByUserIds return dialog for two users if it exists
func (i *Impl) FindDialogByUserIds(params DialogCreateJSON) (Dialog, error) {
	dialogID := 0
	if len(params.UserIds) == 2 {
		u1 := params.UserIds[0]
		u2 := params.UserIds[1]
		i.DB.Raw(`SELECT dialog_id
			FROM dialog_users
			WHERE user_id = ?::integer OR user_id = ?::integer
			GROUP BY dialog_id
			HAVING COUNT(user_id) = 2 AND array_agg(user_id ORDER BY user_id) @> array[?::integer, ?::integer]
			ORDER BY dialog_id ASC
			LIMIT 1`, u1, u2, u1, u2).Row().Scan(&dialogID)
	}

	dialog := Dialog{}
	if dialogID == 0 {
		dialog.Name = params.Name
		if err := i.DB.Save(&dialog).Error; err != nil {
			return dialog, err
		}
	} else {
		i.DB.Find(&dialog, dialogID)
	}

	return dialog, nil
}

// UpdateLastMessage sets last_message_id for dialog
func (i *Impl) UpdateLastMessage(userID string, dialogID int) {
	lastMessageID := 0
	i.DB.Raw("SELECT last_message_id FROM dialogs WHERE id = ?", dialogID).Row().Scan(&lastMessageID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", lastMessageID, dialogID, userID)
}
<commit_msg>Fixed bug with duplications of dialog_users<commit_after>package main

import "strconv"

// GetDialogs fetches dialogs from db for user with offset
func (i *Impl) GetDialogs(userID string, offset int) []DialogJSON {
	dialogs := []DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
		ORDER BY c.last_message_id DESC
    LIMIT 10
    OFFSET ?
  `, userID, offset).Find(&dialogs)
	return dialogs
}

// ShowDialog gets one dialog for user
func (i *Impl) ShowDialog(userID string, dialogID int) DialogJSON {
	dialog := DialogJSON{}
	i.DB.Raw(`
    SELECT c.*, array_agg(du.user_id) AS user_ids
    FROM
      (SELECT
        dialogs.id AS id,
        dialogs.name AS name,
        dialogs.created_at AS created_at,
        dialogs.updated_at AS updated_at,
        dialogs.last_message_id AS last_message_id,
        messages.text AS last_message,
        messages.user_id AS last_message_user_id,
    	  dialog_users.last_seen_message_id AS last_seen_message_id
      FROM dialogs
      JOIN messages ON messages.id = dialogs.last_message_id
      JOIN dialog_users ON dialog_users.dialog_id = dialogs.id
      WHERE dialog_users.user_id = ?
      ORDER BY dialogs.last_message_id DESC
      ) c
    JOIN dialog_users du ON c.id = du.dialog_id
    WHERE c.id = ?
    GROUP BY
      c.id,
      c.name,
      c.created_at,
      c.updated_at,
      c.last_message_id,
      c.last_message,
      c.last_message_user_id,
      c.last_seen_message_id
  `, userID, dialogID).Find(&dialog)

	i.UpdateLastMessage(userID, dialogID)

	return dialog
}

// IndexMessages fetches messages for the dialog
func (i *Impl) IndexMessages(userID string, dialogID int, offset int) []MessageJSON {
	messages := []MessageJSON{}
	i.DB.Raw(`
    SELECT * FROM messages
    WHERE messages.dialog_id = ?
    ORDER BY messages.id DESC
    LIMIT 10
    OFFSET ?
  `, dialogID, offset).Find(&messages)

	i.UpdateLastMessage(userID, dialogID)
	return messages
}

// ShowUser fetches user and number of unread dialogs
func (i *Impl) ShowUser(userID string) UserJSON {
	user := UserJSON{}
	user.ID, _ = strconv.Atoi(userID)
	dialogsCount := 0
	i.DB.Raw(`
		SELECT COUNT(dialogs.id)
		FROM dialogs, dialog_users
		WHERE
	  dialogs.last_message_id > dialog_users.last_seen_message_id AND
	  dialog_users.user_id = ? AND
	  dialog_users.dialog_id = dialogs.id
	`, user.ID).Row().Scan(&dialogsCount)
	user.DialogsCount = dialogsCount
	return user
}

// CreateMessage inserts message in db
func (i *Impl) CreateMessage(userID string, dialogID int, message Message) (Message, error) {
	message.DialogID = dialogID
	message.UserID, _ = strconv.Atoi(userID)

	if err := i.DB.Save(&message).Error; err != nil {
		return message, err
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ?, updated_at = now() WHERE dialogs.id = ?", message.ID, message.DialogID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", message.ID, message.DialogID, message.UserID)

	return message, nil
}

// CreateDialog creates dialog. If ony two people are present, it uses existing dialog
func (i *Impl) CreateDialog(userID string, params DialogCreateJSON) (Dialog, error) {
	dialog, created, err := i.FindDialogByUserIds(params)
	if err != nil {
		dialog := Dialog{}
		dialog.Name = params.Name
		if err := i.DB.Save(&dialog).Error; err != nil {
			return dialog, err
		}
	}

	message := Message{}
	message.DialogID = dialog.ID
	message.Text = params.Message
	message.UserID, _ = strconv.Atoi(userID)

	if err := i.DB.Save(&message).Error; err != nil {
		return dialog, err
	}

	if created == 1 {
		for _, element := range params.UserIds {
			i.DB.Exec("INSERT INTO dialog_users (dialog_id, user_id, last_seen_message_id) VALUES (?, ?, 0)", dialog.ID, element)
		}
	}

	i.DB.Exec("UPDATE dialogs SET last_message_id = ? WHERE id = ?", message.ID, dialog.ID)
	dialog.LastMessageID = message.ID

	return dialog, nil
}

// FindDialogByUserIds return dialog for two users if it exists
func (i *Impl) FindDialogByUserIds(params DialogCreateJSON) (Dialog, int, error) {
	dialogID := 0
	created := 0
	if len(params.UserIds) == 2 {
		u1 := params.UserIds[0]
		u2 := params.UserIds[1]
		i.DB.Raw(`SELECT dialog_id
			FROM dialog_users
			WHERE user_id = ?::integer OR user_id = ?::integer
			GROUP BY dialog_id
			HAVING COUNT(user_id) = 2 AND array_agg(user_id ORDER BY user_id) @> array[?::integer, ?::integer]
			ORDER BY dialog_id ASC
			LIMIT 1`, u1, u2, u1, u2).Row().Scan(&dialogID)
	}

	dialog := Dialog{}
	if dialogID == 0 {
		dialog.Name = params.Name
		created = 1
		if err := i.DB.Save(&dialog).Error; err != nil {
			return dialog, created, err
		}
	} else {
		i.DB.Find(&dialog, dialogID)
	}

	return dialog, created, nil
}

// UpdateLastMessage sets last_message_id for dialog
func (i *Impl) UpdateLastMessage(userID string, dialogID int) {
	lastMessageID := 0
	i.DB.Raw("SELECT last_message_id FROM dialogs WHERE id = ?", dialogID).Row().Scan(&lastMessageID)
	i.DB.Exec("UPDATE dialog_users SET last_seen_message_id = ? WHERE dialog_id = ? AND user_id = ?", lastMessageID, dialogID, userID)
}
<|endoftext|>


<commit_before>package main

import (
	"container/list"
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
	"sync"
	"time"
)

const (
	defaultHistSize = 60
	minFmt          = `010220061504`
	hourFmt         = `0102200615`
	dayFmt          = `01022006`
	monFmt          = `012006`

	dataSize = 8 * 3
)

var (
	errNotOpen          = errors.New("Database not open")
	errInvalidTimestamp = errors.New("Invalid timestamp")
	errCorruptValue     = errors.New("Corrupt value in DB")

	errNoBucket = errors.New("Bucket does not exist")

	bktMin  = []byte(`min`)
	bktHour = []byte(`hour`)
	bktDay  = []byte(`day`)
	bktMon  = []byte(`mon`)

	zeroTime time.Time
)

type newVarInit func() Sample

type bwdb struct {
	open     bool
	mtx      *sync.Mutex
	db       *bolt.DB
	hist     *list.List
	histSize int
	last     time.Time
	newVar   newVarInit
}

type Sample interface {
	After(time.Time) bool
	Add(Sample) error
	Decode([]byte) error
	Encode() []byte
	TimeLabel(string) []byte
	TS() time.Time
	SetTS(time.Time)
}

//we hand in a temporary variable that represents the type
//used in storing to the DB, this is so we can use an interface here
func NewBwDb(path string, liveSize int, nv newVarInit) (*bwdb, error) {
	db, err := bolt.Open(path, 0600, nil)
	if err != nil {
		return nil, err
	}
	if liveSize <= 0 {
		liveSize = defaultHistSize
	}
	r := &bwdb{
		mtx:      &sync.Mutex{},
		db:       db,
		open:     true,
		hist:     list.New(),
		histSize: liveSize,
		newVar:   nv,
	}
	return r, nil
}

func (db *bwdb) Close() error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}
	if err := db.db.Close(); err != nil {
		return err
	}
	db.hist.Init()
	db.open = false
	return nil
}

//Add adds a timestamp to the DB with the number of bytes it represents
func (db *bwdb) Add(s Sample) error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}
	//check if this isn't a regular sequential update
	if !s.After(db.last) {
		return db.addOutOfOrder(s)
	}
	//add to our live list
	db.hist.PushFront(s)

	//trim
	for db.hist.Len() > db.histSize {
		db.hist.Remove(db.hist.Back())
	}
	if db.last == zeroTime {
		db.last = s.TS()
	}

	//add value to each bucket and shift if needed
	err := db.db.Batch(func(tx *bolt.Tx) error {
		//get all our lables
		minLbl := s.TimeLabel(minFmt)
		hourLbl := s.TimeLabel(hourFmt)
		dayLbl := s.TimeLabel(dayFmt)
		monLbl := s.TimeLabel(monFmt)

		//get all our buckets
		minBkt, err := tx.CreateBucketIfNotExists(bktMin)
		if err != nil {
			return err
		}
		hourBkt, err := tx.CreateBucketIfNotExists(bktHour)
		if err != nil {
			return err
		}
		dayBkt, err := tx.CreateBucketIfNotExists(bktDay)
		if err != nil {
			return err
		}
		monBkt, err := tx.CreateBucketIfNotExists(bktMon)
		if err != nil {
			return err
		}
		//perform any required shifts
		//new hour
		if string(hourLbl) != db.last.Format(hourFmt) {
			lastHourLbl := []byte(db.last.Format(hourFmt))
			if err := db.sumAndShift(minBkt, hourBkt, lastHourLbl); err != nil {
				return err
			}
		}
		//new day
		if string(dayLbl) != db.last.Format(dayFmt) {
			lastDayLbl := []byte(db.last.Format(dayFmt))
			if err := db.sumAndShift(hourBkt, dayBkt, lastDayLbl); err != nil {
				return err
			}
		}
		//new month
		if string(monLbl) != db.last.Format(monFmt) {
			lastMonLabel := []byte(db.last.Format(monFmt))
			if err := db.sumAndShift(dayBkt, monBkt, lastMonLabel); err != nil {
				return err
			}
		}

		//minutes
		if err := db.updateVal(minBkt, minLbl, s); err != nil {
			return err
		}

		//hour
		if err := db.updateVal(hourBkt, hourLbl, s); err != nil {
			return err
		}

		//days
		if err := db.updateVal(dayBkt, dayLbl, s); err != nil {
			return err
		}

		//months
		if err := db.updateVal(monBkt, monLbl, s); err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}
	db.last = s.TS()
	return nil
}

func (db *bwdb) AddRand(s Sample) error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	return db.addOutOfOrder(s)
}

//addOutOfOrder does not go into the live set, as its assumed to come out of order and does not update the last variable
func (db *bwdb) addOutOfOrder(s Sample) error {
	if !db.open {
		return errNotOpen
	}
	return db.db.Batch(func(tx *bolt.Tx) error {
		//figure out where it should be
		if s.TS().Minute() == db.last.Minute() {
			minBkt, err := tx.CreateBucketIfNotExists(bktMin)
			if err != nil {
				return err
			}
			minLbl := s.TimeLabel(minFmt)
			if err := db.updateVal(minBkt, minLbl, s); err != nil {
				return err
			}
		} else if s.TS().Hour() == db.last.Hour() {
			hourBkt, err := tx.CreateBucketIfNotExists(bktHour)
			if err != nil {
				return err
			}
			hourLbl := s.TimeLabel(hourFmt)
			if err := db.updateVal(hourBkt, hourLbl, s); err != nil {
				return err
			}

		} else if s.TS().Day() == db.last.Day() {
			dayBkt, err := tx.CreateBucketIfNotExists(bktDay)
			if err != nil {
				return err
			}
			dayLbl := s.TimeLabel(dayFmt)
			if err := db.updateVal(dayBkt, dayLbl, s); err != nil {
				return err
			}
		} else {
			monBkt, err := tx.CreateBucketIfNotExists(bktMon)
			if err != nil {
				return err
			}
			monLbl := s.TimeLabel(monFmt)
			if err := db.updateVal(monBkt, monLbl, s); err != nil {
				return err
			}
		}
		return nil
	})
}

//Rebase will swep through our time buckets and ensure that they only contain
//the appropriate entires.  Should be called each time the DB is opened
func (db *bwdb) Rebase(ts time.Time) error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}

	if err := db.shiftIfOlder(bktMin, bktHour, prevHour(ts), hourFmt); err != nil {
		return err
	}
	if err := db.shiftIfOlder(bktHour, bktDay, prevDay(ts), dayFmt); err != nil {
		return err
	}
	if err := db.shiftIfOlder(bktDay, bktMon, prevMon(ts), monFmt); err != nil {
		return err
	}
	return nil
}

func (db *bwdb) shiftIfOlder(srcBktKey, dstBktKey []byte, tcheck time.Time, keyFmt string) error {
	s := db.newVar()
	return db.db.Batch(func(tx *bolt.Tx) error {
		srcBkt, err := tx.CreateBucketIfNotExists(srcBktKey)
		if err != nil {
			return err
		}
		dstBkt, err := tx.CreateBucketIfNotExists(dstBktKey)
		if err != nil {
			return err
		}
		return srcBkt.ForEach(func(k, v []byte) error {
			if err := s.Decode(v); err != nil {
				return err
			}
			//if its outside our window shift up
			if s.TS().Before(tcheck) {
				//delete from the old one
				if err := srcBkt.Delete(k); err != nil {
					return err
				}
				//push into the new one
				if err := db.updateVal(dstBkt, []byte(s.TS().Format(keyFmt)), s); err != nil {
					return err
				}
			}
			return nil
		})
	})
}

func (db *bwdb) sumAndShift(pullBkt, putBkt *bolt.Bucket, putKey []byte) error {
	//pull all the values out of the pullBkt and sum them
	s := db.newVar()
	err := pullBkt.ForEach(func(k, v []byte) error {
		sx := db.newVar()
		if err := sx.Decode(v); err != nil {
			return err
		}
		if err := s.Add(sx); err != nil {
			return err
		}
		s.SetTS(sx.TS())
		return pullBkt.Delete(k)
	})
	if err != nil {
		return err
	}
	return db.updateVal(putBkt, putKey, s) //if for some reason the key already exists, add it
}

func (db *bwdb) pullSet(bktName []byte) ([]Sample, error) {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return nil, errNotOpen
	}
	var ss []Sample
	err := db.db.View(func(tx *bolt.Tx) error {
		bkt := tx.Bucket(bktName)
		if bkt == nil {
			return errNoBucket
		}
		return bkt.ForEach(func(k, v []byte) error {
			s := db.newVar()
			if err := s.Decode(v); err != nil {
				return err
			}
			ss = append(ss, s)
			return nil
		})
	})
	if err != nil {
		return nil, err
	}
	return ss, nil
}

func (db *bwdb) LiveSet() ([]Sample, error) {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return nil, errNotOpen
	}
	var set []Sample
	for e := db.hist.Front(); e != nil; e = e.Next() {
		set = append(set, e.Value.(Sample))
	}
	return set, nil
}

//purge removes all entries from the bolt DB and live set
func (db *bwdb) purge() error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}
	//purge last update
	db.last = zeroTime

	//purge live
	db.hist = db.hist.Init()
	if db.hist.Len() != 0 {
		return errors.New("Failed to clear live set")
	}

	//roll through each bucket and delete its contents
	return db.db.Batch(func(tx *bolt.Tx) error {
		return tx.ForEach(func(name []byte, b *bolt.Bucket) error {
			return b.ForEach(func(k, _ []byte) error {
				return b.Delete(k)
			})
		})
	})
}

func (db *bwdb) Minutes() ([]Sample, error) {
	return db.pullSet(bktMin)
}

func (db *bwdb) Hours() ([]Sample, error) {
	return db.pullSet(bktHour)
}

func (db *bwdb) Days() ([]Sample, error) {
	return db.pullSet(bktDay)
}

func (db *bwdb) Months() ([]Sample, error) {
	return db.pullSet(bktMon)
}

func (db *bwdb) updateVal(bkt *bolt.Bucket, key []byte, s Sample) error {
	//attempt to get what is there
	v := bkt.Get(key)
	if v == nil {
		e := s.Encode()
		return bkt.Put(key, e)
	}
	sold := db.newVar()
	if err := sold.Decode(v); err != nil {
		return err
	}
	//WARNING: because s is an interface it is naturally a pointer, you MUST add to sold and NOT s
	sold.Add(s)
	e := sold.Encode()
	return bkt.Put(key, e)
}

/*
func printBucket(bkt *bolt.Bucket) {
	bkt.ForEach(func(k, v []byte) error {
		sx := &BWSample{}
		if err := sx.Decode(v); err != nil {
			return err
		}
		fmt.Printf("%s %d %d\n", sx.ts, sx.BytesUp, sx.BytesDown)
		return nil
	})
}
*/

//these functions are ghetto as shit, but I don't want to do the math...
func prevHour(ts time.Time) time.Time {
	tn, _ := time.Parse(hourFmt, ts.UTC().Format(hourFmt))
	return tn.Add(time.Hour)
}

func prevDay(ts time.Time) time.Time {
	tn, _ := time.Parse(dayFmt, ts.UTC().Format(dayFmt))
	return tn.Add(time.Hour)
}

func prevMon(ts time.Time) time.Time {
	year := ts.UTC().Year()
	month := ts.UTC().Month()
	if month == time.January {
		year -= 1
		month = time.December
	} else {
		month -= 1
	}
	tn, _ := time.Parse(`2006-01`, fmt.Sprintf("%04d-%02d", year, month))
	return tn
}
<commit_msg>error in adding up days<commit_after>package main

import (
	"container/list"
	"errors"
	"fmt"
	"github.com/boltdb/bolt"
	"sync"
	"time"
)

const (
	defaultHistSize = 60
	minFmt          = `010220061504`
	hourFmt         = `0102200615`
	dayFmt          = `01022006`
	monFmt          = `012006`

	dataSize = 8 * 3
)

var (
	errNotOpen          = errors.New("Database not open")
	errInvalidTimestamp = errors.New("Invalid timestamp")
	errCorruptValue     = errors.New("Corrupt value in DB")

	errNoBucket = errors.New("Bucket does not exist")

	bktMin  = []byte(`min`)
	bktHour = []byte(`hour`)
	bktDay  = []byte(`day`)
	bktMon  = []byte(`mon`)

	zeroTime time.Time
)

type newVarInit func() Sample

type bwdb struct {
	open     bool
	mtx      *sync.Mutex
	db       *bolt.DB
	hist     *list.List
	histSize int
	last     time.Time
	newVar   newVarInit
}

type Sample interface {
	After(time.Time) bool
	Add(Sample) error
	Decode([]byte) error
	Encode() []byte
	TimeLabel(string) []byte
	TS() time.Time
	SetTS(time.Time)
}

//we hand in a temporary variable that represents the type
//used in storing to the DB, this is so we can use an interface here
func NewBwDb(path string, liveSize int, nv newVarInit) (*bwdb, error) {
	db, err := bolt.Open(path, 0600, nil)
	if err != nil {
		return nil, err
	}
	if liveSize <= 0 {
		liveSize = defaultHistSize
	}
	r := &bwdb{
		mtx:      &sync.Mutex{},
		db:       db,
		open:     true,
		hist:     list.New(),
		histSize: liveSize,
		newVar:   nv,
	}
	return r, nil
}

func (db *bwdb) Close() error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}
	if err := db.db.Close(); err != nil {
		return err
	}
	db.hist.Init()
	db.open = false
	return nil
}

//Add adds a timestamp to the DB with the number of bytes it represents
func (db *bwdb) Add(s Sample) error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}
	//check if this isn't a regular sequential update
	if !s.After(db.last) {
		return db.addOutOfOrder(s)
	}
	//add to our live list
	db.hist.PushFront(s)

	//trim
	for db.hist.Len() > db.histSize {
		db.hist.Remove(db.hist.Back())
	}
	if db.last == zeroTime {
		db.last = s.TS()
	}

	//add value to each bucket and shift if needed
	err := db.db.Batch(func(tx *bolt.Tx) error {
		//get all our lables
		minLbl := s.TimeLabel(minFmt)
		hourLbl := s.TimeLabel(hourFmt)
		dayLbl := s.TimeLabel(dayFmt)
		monLbl := s.TimeLabel(monFmt)

		//get all our buckets
		minBkt, err := tx.CreateBucketIfNotExists(bktMin)
		if err != nil {
			return err
		}
		hourBkt, err := tx.CreateBucketIfNotExists(bktHour)
		if err != nil {
			return err
		}
		dayBkt, err := tx.CreateBucketIfNotExists(bktDay)
		if err != nil {
			return err
		}
		monBkt, err := tx.CreateBucketIfNotExists(bktMon)
		if err != nil {
			return err
		}
		//perform any required shifts
		//new hour
		if string(hourLbl) != db.last.Format(hourFmt) {
			lastHourLbl := []byte(db.last.Format(hourFmt))
			if err := db.sumAndShift(minBkt, hourBkt, lastHourLbl); err != nil {
				return err
			}
		}
		//new day
		if string(dayLbl) != db.last.Format(dayFmt) {
			lastDayLbl := []byte(db.last.Format(dayFmt))
			if err := db.sumAndShift(hourBkt, dayBkt, lastDayLbl); err != nil {
				return err
			}
		}
		//new month
		if string(monLbl) != db.last.Format(monFmt) {
			lastMonLabel := []byte(db.last.Format(monFmt))
			if err := db.sumAndShift(dayBkt, monBkt, lastMonLabel); err != nil {
				return err
			}
		}

		//minutes
		if err := db.updateVal(minBkt, minLbl, s); err != nil {
			return err
		}

		//hour
		if err := db.updateVal(hourBkt, hourLbl, s); err != nil {
			return err
		}

		//days
		if err := db.updateVal(dayBkt, dayLbl, s); err != nil {
			return err
		}

		//months
		if err := db.updateVal(monBkt, monLbl, s); err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return err
	}
	db.last = s.TS()
	return nil
}

func (db *bwdb) AddRand(s Sample) error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	return db.addOutOfOrder(s)
}

//addOutOfOrder does not go into the live set, as its assumed to come out of order and does not update the last variable
func (db *bwdb) addOutOfOrder(s Sample) error {
	if !db.open {
		return errNotOpen
	}
	return db.db.Batch(func(tx *bolt.Tx) error {
		//figure out where it should be
		if s.TS().Minute() == db.last.Minute() {
			minBkt, err := tx.CreateBucketIfNotExists(bktMin)
			if err != nil {
				return err
			}
			minLbl := s.TimeLabel(minFmt)
			if err := db.updateVal(minBkt, minLbl, s); err != nil {
				return err
			}
		} else if s.TS().Hour() == db.last.Hour() {
			hourBkt, err := tx.CreateBucketIfNotExists(bktHour)
			if err != nil {
				return err
			}
			hourLbl := s.TimeLabel(hourFmt)
			if err := db.updateVal(hourBkt, hourLbl, s); err != nil {
				return err
			}

		} else if s.TS().Day() == db.last.Day() {
			dayBkt, err := tx.CreateBucketIfNotExists(bktDay)
			if err != nil {
				return err
			}
			dayLbl := s.TimeLabel(dayFmt)
			if err := db.updateVal(dayBkt, dayLbl, s); err != nil {
				return err
			}
		} else {
			monBkt, err := tx.CreateBucketIfNotExists(bktMon)
			if err != nil {
				return err
			}
			monLbl := s.TimeLabel(monFmt)
			if err := db.updateVal(monBkt, monLbl, s); err != nil {
				return err
			}
		}
		return nil
	})
}

//Rebase will swep through our time buckets and ensure that they only contain
//the appropriate entires.  Should be called each time the DB is opened
func (db *bwdb) Rebase(ts time.Time) error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}

	if err := db.shiftIfOlder(bktMin, bktHour, prevHour(ts), hourFmt); err != nil {
		return err
	}
	if err := db.shiftIfOlder(bktHour, bktDay, prevDay(ts), dayFmt); err != nil {
		return err
	}
	if err := db.shiftIfOlder(bktDay, bktMon, prevMon(ts), monFmt); err != nil {
		return err
	}
	return nil
}

func (db *bwdb) shiftIfOlder(srcBktKey, dstBktKey []byte, tcheck time.Time, keyFmt string) error {
	s := db.newVar()
	return db.db.Batch(func(tx *bolt.Tx) error {
		srcBkt, err := tx.CreateBucketIfNotExists(srcBktKey)
		if err != nil {
			return err
		}
		dstBkt, err := tx.CreateBucketIfNotExists(dstBktKey)
		if err != nil {
			return err
		}
		return srcBkt.ForEach(func(k, v []byte) error {
			if err := s.Decode(v); err != nil {
				return err
			}
			//if its outside our window shift up
			if s.TS().Before(tcheck) {
				//delete from the old one
				if err := srcBkt.Delete(k); err != nil {
					return err
				}
				//push into the new one
				if err := db.updateVal(dstBkt, []byte(s.TS().Format(keyFmt)), s); err != nil {
					return err
				}
			}
			return nil
		})
	})
}

func (db *bwdb) sumAndShift(pullBkt, putBkt *bolt.Bucket, putKey []byte) error {
	//pull all the values out of the pullBkt and sum them
	s := db.newVar()
	err := pullBkt.ForEach(func(k, v []byte) error {
		sx := db.newVar()
		if err := sx.Decode(v); err != nil {
			return err
		}
		if err := s.Add(sx); err != nil {
			return err
		}
		s.SetTS(sx.TS())
		return pullBkt.Delete(k)
	})
	if err != nil {
		return err
	}
	return db.updateVal(putBkt, putKey, s) //if for some reason the key already exists, add it
}

func (db *bwdb) pullSet(bktName []byte) ([]Sample, error) {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return nil, errNotOpen
	}
	var ss []Sample
	err := db.db.View(func(tx *bolt.Tx) error {
		bkt := tx.Bucket(bktName)
		if bkt == nil {
			return errNoBucket
		}
		return bkt.ForEach(func(k, v []byte) error {
			s := db.newVar()
			if err := s.Decode(v); err != nil {
				return err
			}
			ss = append(ss, s)
			return nil
		})
	})
	if err != nil {
		return nil, err
	}
	return ss, nil
}

func (db *bwdb) LiveSet() ([]Sample, error) {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return nil, errNotOpen
	}
	var set []Sample
	for e := db.hist.Front(); e != nil; e = e.Next() {
		set = append(set, e.Value.(Sample))
	}
	return set, nil
}

//purge removes all entries from the bolt DB and live set
func (db *bwdb) purge() error {
	db.mtx.Lock()
	defer db.mtx.Unlock()
	if !db.open {
		return errNotOpen
	}
	//purge last update
	db.last = zeroTime

	//purge live
	db.hist = db.hist.Init()
	if db.hist.Len() != 0 {
		return errors.New("Failed to clear live set")
	}

	//roll through each bucket and delete its contents
	return db.db.Batch(func(tx *bolt.Tx) error {
		return tx.ForEach(func(name []byte, b *bolt.Bucket) error {
			return b.ForEach(func(k, _ []byte) error {
				return b.Delete(k)
			})
		})
	})
}

func (db *bwdb) Minutes() ([]Sample, error) {
	return db.pullSet(bktMin)
}

func (db *bwdb) Hours() ([]Sample, error) {
	return db.pullSet(bktHour)
}

func (db *bwdb) Days() ([]Sample, error) {
	return db.pullSet(bktDay)
}

func (db *bwdb) Months() ([]Sample, error) {
	return db.pullSet(bktMon)
}

func (db *bwdb) updateVal(bkt *bolt.Bucket, key []byte, s Sample) error {
	//attempt to get what is there
	v := bkt.Get(key)
	if v == nil {
		e := s.Encode()
		return bkt.Put(key, e)
	}
	sold := db.newVar()
	if err := sold.Decode(v); err != nil {
		return err
	}
	//WARNING: because s is an interface it is naturally a pointer, you MUST add to sold and NOT s
	sold.Add(s)
	e := sold.Encode()
	return bkt.Put(key, e)
}

/*
func printBucket(bkt *bolt.Bucket) {
	bkt.ForEach(func(k, v []byte) error {
		sx := &BWSample{}
		if err := sx.Decode(v); err != nil {
			return err
		}
		fmt.Printf("%s %d %d\n", sx.ts, sx.BytesUp, sx.BytesDown)
		return nil
	})
}
*/

//these functions are ghetto as shit, but I don't want to do the math...
func prevHour(ts time.Time) time.Time {
	tn, _ := time.Parse(hourFmt, ts.UTC().Format(hourFmt))
	return tn.Add(time.Hour)
}

func prevDay(ts time.Time) time.Time {
	tn, _ := time.Parse(dayFmt, ts.UTC().Format(dayFmt))
	return tn.Add(24*time.Hour)
}

func prevMon(ts time.Time) time.Time {
	year := ts.UTC().Year()
	month := ts.UTC().Month()
	if month == time.January {
		year -= 1
		month = time.December
	} else {
		month -= 1
	}
	tn, _ := time.Parse(`2006-01`, fmt.Sprintf("%04d-%02d", year, month))
	return tn
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "container/list"
    "fmt"
    "log"
    "os"
    "strings"
    "strconv"
    "unicode"
)

type Buffer struct {
    lines *list.List
    filename string
    currentLine int
    modified bool
    err string
}

func NewBuffer() *Buffer {
    b := new(Buffer)
    b.lines = list.New()

    return b
}

func (b *Buffer) Index(idx int) *list.Element {
    i := 0
    for e := b.lines.Front(); e != nil; e = e.Next() {
        if i == idx {
            return e
        }

        i++
    }

    return nil
}

func (b *Buffer) Open(filename string) {
    file, err := os.Open(filename)

    if err != nil {
        log.Fatal(err)
    }

    b.filename = filename
    size := 0

    scanner := bufio.NewScanner(file)

    i := 1
    for scanner.Scan() {
        text := scanner.Text()
        size += len(text)
        b.lines.PushBack(text)
        i++
    }

    b.currentLine = i - 1

    file.Close()

    fmt.Println(size)
}

func (b *Buffer) Print(start, end int, numbers bool) {
    i := 1

    for e := b.lines.Front(); e != nil; e = e.Next() {
        if i >= start && i <= end {
            if numbers {
                fmt.Printf("%d\t%s\n", i, e.Value)
            } else {
                fmt.Println(e.Value)
            }

            b.currentLine = i
        }

        i++
    }
}

func readLine() string {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return scanner.Text()
}

func readLines() *list.List {
    input := list.New()

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()

        if text == "." {
            break
        }

        input.PushBack(text)
    }

    return input
}

func (b *Buffer) InsertBefore(other *list.List, line int) {
    node := b.Index(line-1)

    for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
        b.lines.InsertBefore(e.Value, node)
        node = node.Prev()
    }
}

func (b *Buffer) Insert(line int) {
    input := readLines()
    b.InsertBefore(input, line)

    b.modified = true
}

func (b *Buffer) Delete(start, end int) {
    curr := b.Index(start-1)

    for i := start; i <= end; i++ {
        next := curr.Next()
        b.lines.Remove(curr)
        curr = next
    }

    b.modified = true
}

func (b *Buffer) Error(msg string) {
    b.err = msg
    fmt.Println("?")
}

func (b *Buffer) Prompt() {
    text := readLine()

    if len(text) == 0 {
        b.Error("invalid address")
        return
    }

    command := rune(text[len(text)-1])
    nrange := text

    if unicode.IsLetter(command) {
        nrange = text[:len(text)-1]
    } else {
        command = 'p'
    }

    nums := strings.Split(nrange, ",")
    start := 0
    end := 0

    if len(nums) == 2 {
        start, _ = strconv.Atoi(nums[0])
        end, _ = strconv.Atoi(nums[1])
    } else if len(nums) == 1 {
        start, _ = strconv.Atoi(nums[0])
        end = start
    }

    if start == 0 || end == 0 {
        start = b.currentLine
        end = b.currentLine
    }

    if start > end || start < 0 || end > b.lines.Len() {
        b.Error("invalid address")
        return
    }

    switch command {
    case 'p':
        b.Print(start, end, false)
    case 'n':
        b.Print(start, end, true)
    case 'i':
        b.Insert(end)
    case 'd':
        b.Delete(start, end)
    case 'c':
        b.Delete(start, end)
        b.Insert(start)
    case 'h':
        if len(b.err) > 0 {
            fmt.Println(b.err)
        }
    case 'q':
        if b.modified {
            b.Error("warning: file modified")
            b.modified = false
        } else {
            os.Exit(0)
        }
    case 'Q':
        os.Exit(0)
    default:
        b.Error("unknown command")
    }
}

func main() {
    buffer := NewBuffer()

    filename := "README.md"
    buffer.Open(filename)

    for {
        buffer.Prompt()
    }
}
<commit_msg>renamed some variables<commit_after>package main

import (
    "bufio"
    "container/list"
    "fmt"
    "log"
    "os"
    "strings"
    "strconv"
    "unicode"
)

type Editor struct {
    buffer *list.List
    filename string
    currentLine int
    modified bool
    err string
}

func NewEditor() *Editor {
    e := new(Editor)
    e.buffer = list.New()

    return e
}

func (e *Editor) Index(idx int) *list.Element {
    i := 0
    for l := e.buffer.Front(); l != nil; l = l.Next() {
        if i == idx {
            return l
        }

        i++
    }

    return nil
}

func (e *Editor) Open(filename string) {
    file, err := os.Open(filename)

    if err != nil {
        log.Fatal(err)
    }

    e.filename = filename
    size := 0

    scanner := bufio.NewScanner(file)

    i := 1
    for scanner.Scan() {
        text := scanner.Text()
        size += len(text) + 1
        e.buffer.PushBack(text)
        i++
    }

    e.currentLine = i - 1

    file.Close()

    fmt.Println(size)
}

func (e *Editor) Print(start, end int, numbers bool) {
    i := 1

    for l := e.buffer.Front(); l != nil; l = l.Next() {
        if i >= start && i <= end {
            if numbers {
                fmt.Printf("%d\t%s\n", i, l.Value)
            } else {
                fmt.Println(l.Value)
            }

            e.currentLine = i
        }

        i++
    }
}

func readLine() string {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return scanner.Text()
}

func readLines() *list.List {
    input := list.New()

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()

        if text == "." {
            break
        }

        input.PushBack(text)
    }

    return input
}

func (e *Editor) InsertBefore(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := other.Len(), other.Back(); i > 0; i, l = i-1, l.Prev() {
        e.buffer.InsertBefore(l.Value, node)
        node = node.Prev()
    }
}

func (e *Editor) Insert(line int) {
    input := readLines()
    e.InsertBefore(input, line)

    e.modified = true
}

func (e *Editor) Delete(start, end int) {
    curr := e.Index(start-1)

    for i := start; i <= end; i++ {
        next := curr.Next()
        e.buffer.Remove(curr)
        curr = next
    }

    e.modified = true
}

func (e *Editor) Error(msg string) {
    e.err = msg
    fmt.Println("?")
}

func (e *Editor) Prompt() {
    text := readLine()

    if len(text) == 0 {
        e.Error("invalid address")
        return
    }

    command := rune(text[len(text)-1])
    nrange := text

    if unicode.IsLetter(command) {
        nrange = text[:len(text)-1]
    } else {
        command = 'p'
    }

    nums := strings.Split(nrange, ",")
    start := 0
    end := 0

    if len(nums) == 2 {
        start, _ = strconv.Atoi(nums[0])
        end, _ = strconv.Atoi(nums[1])
    } else if len(nums) == 1 {
        start, _ = strconv.Atoi(nums[0])
        end = start
    }

    if start == 0 || end == 0 {
        start = e.currentLine
        end = e.currentLine
    }

    if start > end || start < 0 || end > e.buffer.Len() {
        e.Error("invalid address")
        return
    }

    switch command {
    case 'p':
        e.Print(start, end, false)
    case 'n':
        e.Print(start, end, true)
    case 'i':
        e.Insert(end)
    case 'd':
        e.Delete(start, end)
    case 'c':
        e.Delete(start, end)
        e.Insert(start)
    case 'h':
        if len(e.err) > 0 {
            fmt.Println(e.err)
        }
    case 'q':
        if e.modified {
            e.Error("warning: file modified")
            e.modified = false
        } else {
            os.Exit(0)
        }
    case 'Q':
        os.Exit(0)
    default:
        e.Error("unknown command")
    }
}

func main() {
    editor := NewEditor()

    if len(os.Args) > 1 {
        editor.Open(os.Args[1])
    }

    for {
        editor.Prompt()
    }
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/igungor/go-putio/putio"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const DefaultUserAgent = "putiofs - FUSE bridge to Put.io"
const AttrValidityDuration = time.Hour

type FileSystem struct {
	putio  *putio.Client
	logger *Logger
}

var (
	_ fs.FS         = (*FileSystem)(nil)
	_ fs.FSStatfser = (*FileSystem)(nil)
)

func NewFileSystem(token string, debug bool) *FileSystem {
	oauthClient := oauth2.NewClient(
		oauth2.NoContext,
		oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
	)
	client := putio.NewClient(oauthClient)
	client.UserAgent = DefaultUserAgent

	return &FileSystem{
		putio:  client,
		logger: NewLogger("putiofs: ", debug),
	}
}

func (f *FileSystem) List(ctx context.Context, id int64) ([]putio.File, error) {
	files, _, err := f.putio.Files.List(ctx, id)
	return files, err
}

func (f *FileSystem) Get(ctx context.Context, id int64) (putio.File, error) {
	return f.putio.Files.Get(ctx, id)
}

func (f *FileSystem) Delete(ctx context.Context, id int64) error {
	return f.putio.Files.Delete(ctx, id)
}

func (f *FileSystem) Download(ctx context.Context, id int64) (io.ReadCloser, error) {
	return f.putio.Files.Download(ctx, id, true, nil)
}

func (f *FileSystem) Root() (fs.Node, error) {
	f.logger.Debugln("Filesystem Root request")

	root, err := f.Get(nil, 0)
	if err != nil {
		f.logger.Printf("Root failed: %v\n", err)
		return nil, fuse.ENOENT
	}

	return &Dir{
		fs:   f,
		ID:   root.ID,
		Name: root.Filename,
		Size: root.Filesize,
	}, nil
}

func (f *FileSystem) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	f.logger.Debugln("Filesystem Stat request")

	info, err := f.putio.Account.Info(ctx)
	if err != nil {
		return err
	}

	resp.Blocks = uint64(info.Disk.Used)
	return nil
}

type Dir struct {
	fs *FileSystem

	ID       int64
	Name     string
	Size     int64
	children fs.Node
}

var (
	_ fs.Node                = (*Dir)(nil)
	_ fs.NodeRequestLookuper = (*Dir)(nil)
	_ fs.NodeRemover         = (*Dir)(nil)
	_ fs.HandleReadDirAller  = (*Dir)(nil)
)

func (d *Dir) String() string {
	return fmt.Sprintf("<%v - %q>", d.ID, d.Name)
}

func (d *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	d.fs.logger.Debugf("Directory stat for %v\n", d)

	attr.Inode = uint64(d.ID)
	attr.Mode = os.ModeDir | 0755
	attr.Size = uint64(d.Size)
	return nil
}

// Lookup looks up a specific entry in the current directory.
func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	d.fs.logger.Debugf("Directory lookup for %v in %v\n", req.Name, d)

	filename := req.Name
	if isJunkFile(filename) {
		return nil, fuse.ENOENT
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Lookup failed for %v: %v\n", d, err)
		return nil, fuse.ENOENT
	}

	for _, file := range files {
		if file.Filename == filename {
			if file.IsDir() {
				return &Dir{
					fs:   d.fs,
					ID:   file.ID,
					Name: file.Filename,
					Size: file.Filesize,
				}, nil
			}
			return &File{
				fs:   d.fs,
				File: &file,
			}, nil
		}
	}

	return nil, fuse.ENOENT
}

func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	d.fs.logger.Debugf("Directory listing for %v\n", d)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return nil, fuse.ENOENT
	}

	var entries []fuse.Dirent
	for _, file := range files {
		var entry fuse.Dirent

		var dt fuse.DirentType
		if file.IsDir() {
			dt = fuse.DT_Dir
		} else {
			dt = fuse.DT_File
		}
		entry = fuse.Dirent{
			Inode: uint64(file.ID),
			Name:  file.Filename,
			Type:  dt,
		}
		entries = append(entries, entry)
	}
	return entries, nil
}

// Remove removes the entry with the given name from the current directory. The
// entry to be removed may correspond to a file or to a directory.
func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	d.fs.logger.Debugf("Remove request for %v in %v\n", req.Name, d)

	filename := req.Name
	if filename == "/" || filename == "Your Files" {
		return fuse.ENOENT
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.ENOENT
	}

	for _, file := range files {
		if file.Filename == filename {
			return d.fs.Delete(ctx, file.ID)
		}
	}

	return fuse.ENOENT
}

type File struct {
	fs *FileSystem

	*putio.File
}

var (
	_ fs.Node       = (*File)(nil)
	_ fs.NodeOpener = (*File)(nil)
)

func (f *File) Attr(ctx context.Context, attr *fuse.Attr) error {
	f.fs.logger.Debugf("File stat for %v\n", f)

	attr.Inode = uint64(f.ID)
	attr.Mode = os.ModePerm | 0644
	attr.Size = uint64(f.Filesize)
	attr.Ctime = f.CreatedAt.Time
	return nil
}

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	f.fs.logger.Debugf("File open request for %v\n", f)

	body, err := f.fs.Download(ctx, f.ID)
	if err != nil {
		f.fs.logger.Printf("Error opening file %v: %v\n", f, err)
		return nil, fuse.ENOENT
	}

	return &FileHandle{
		fs:   f.fs,
		ID:   f.ID,
		Name: f.Filename,
		body: body,
	}, nil
}

type FileHandle struct {
	fs   *FileSystem
	ID   int64
	Name string
	body io.ReadCloser
}

var (
	_ fs.HandleReader   = (*FileHandle)(nil)
	_ fs.HandleReleaser = (*FileHandle)(nil)
)

func (fh *FileHandle) String() string {
	return fmt.Sprintf("<%v - %q>", fh.ID, fh.Name)
}

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	fh.fs.logger.Debugln("FileHandler Read request")

	buf := make([]byte, req.Size)
	n, err := io.ReadFull(fh.body, buf)
	if err == io.ErrUnexpectedEOF || err == io.EOF {
		err = nil
	}
	if err != nil {
		fh.fs.logger.Printf("Error reading file %v: %v\n", fh, err)
		return err
	}
	resp.Data = buf[:n]
	return err
}

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	fh.fs.logger.Debugln("FileHandler Release request")

	return fh.body.Close()
}

var junkFilePrefixes = []string{
	"._",
	".DS_Store",
	".Spotlight-",
	".git",
	".hidden",
	".metadata_never_index",
	".nomedia",
}

// isJunkFile reports whether the given file path is considered useless. MacOSX
// Finder is looking for a few hidden files per a file stat request. So this is
// used to speed things a bit.
func isJunkFile(abspath string) bool {
	_, filename := filepath.Split(abspath)
	for _, v := range junkFilePrefixes {
		if strings.HasPrefix(filename, v) {
			return true
		}
	}
	return false
}
<commit_msg>Add Rename/Move support<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/igungor/go-putio/putio"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
)

const DefaultUserAgent = "putiofs - FUSE bridge to Put.io"
const AttrValidityDuration = time.Hour

type FileSystem struct {
	putio  *putio.Client
	logger *Logger
}

var (
	_ fs.FS = (*FileSystem)(nil)
)

func NewFileSystem(token string, debug bool) *FileSystem {
	oauthClient := oauth2.NewClient(
		oauth2.NoContext,
		oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}),
	)
	client := putio.NewClient(oauthClient)
	client.UserAgent = DefaultUserAgent

	return &FileSystem{
		putio:  client,
		logger: NewLogger("putiofs: ", debug),
	}
}

func (f *FileSystem) List(ctx context.Context, id int64) ([]putio.File, error) {
	files, _, err := f.putio.Files.List(ctx, id)
	return files, err
}

func (f *FileSystem) Get(ctx context.Context, id int64) (putio.File, error) {
	return f.putio.Files.Get(ctx, id)
}

func (f *FileSystem) Delete(ctx context.Context, id int64) error {
	return f.putio.Files.Delete(ctx, id)
}

func (f *FileSystem) Download(ctx context.Context, id int64) (io.ReadCloser, error) {
	return f.putio.Files.Download(ctx, id, true, nil)
}

func (f *FileSystem) Rename(ctx context.Context, id int64, newname string) error {
	return f.putio.Files.Rename(ctx, id, newname)
}

func (f *FileSystem) Move(ctx context.Context, parent int64, fileid int64) error {
	return f.putio.Files.Move(ctx, parent, fileid)
}

func (f *FileSystem) Root() (fs.Node, error) {
	root, err := f.Get(nil, 0)
	if err != nil {
		f.logger.Printf("Root failed: %v\n", err)
		return nil, fuse.ENOENT
	}

	return &Dir{
		fs:   f,
		ID:   root.ID,
		Name: root.Filename,
		Size: root.Filesize,
	}, nil
}

type Dir struct {
	fs *FileSystem

	ID   int64
	Name string
	Size int64
}

var (
	_ fs.Node                = (*Dir)(nil)
	_ fs.NodeRequestLookuper = (*Dir)(nil)
	_ fs.NodeRemover         = (*Dir)(nil)
	_ fs.HandleReadDirAller  = (*Dir)(nil)
)

func (d *Dir) String() string {
	return fmt.Sprintf("<%v - %q>", d.ID, d.Name)
}

func (d *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
	d.fs.logger.Debugf("Directory stat for %v\n", d)

	attr.Inode = uint64(d.ID)
	attr.Mode = os.ModeDir | 0755
	attr.Size = uint64(d.Size)
	return nil
}

// Lookup looks up a specific entry in the current directory.
func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
	// skip junk files to quiet log noise
	filename := req.Name
	if isJunkFile(filename) {
		d.fs.logger.Debugf("Skipped, %q seems like a junk file\n", filename)
		return nil, fuse.ENOENT
	}

	d.fs.logger.Debugf("Directory lookup for %v in %v\n", req.Name, d)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Lookup failed for %v: %v\n", d, err)
		return nil, fuse.ENOENT
	}

	for _, file := range files {
		if file.Filename == filename {
			if file.IsDir() {
				return &Dir{
					fs:   d.fs,
					ID:   file.ID,
					Name: file.Filename,
					Size: file.Filesize,
				}, nil
			}
			return &File{
				fs:   d.fs,
				File: &file,
			}, nil
		}
	}

	return nil, fuse.ENOENT
}

func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	d.fs.logger.Debugf("Directory listing for %v\n", d)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return nil, fuse.ENOENT
	}

	var entries []fuse.Dirent
	for _, file := range files {
		var entry fuse.Dirent

		var dt fuse.DirentType
		if file.IsDir() {
			dt = fuse.DT_Dir
		} else {
			dt = fuse.DT_File
		}
		entry = fuse.Dirent{
			Inode: uint64(file.ID),
			Name:  file.Filename,
			Type:  dt,
		}
		entries = append(entries, entry)
	}
	return entries, nil
}

// Remove removes the entry with the given name from the current directory. The
// entry to be removed may correspond to a file or to a directory.
func (d *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	d.fs.logger.Debugf("Remove request for %v in %v\n", req.Name, d)

	filename := req.Name
	if filename == "/" || filename == "Your Files" {
		return fuse.ENOENT
	}

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.ENOENT
	}

	for _, file := range files {
		if file.Filename == filename {
			return d.fs.Delete(ctx, file.ID)
		}
	}

	return fuse.ENOENT
}

func (d *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
	newdir, ok := newDir.(*Dir)
	if !ok {
		d.fs.logger.Debugln("Error converting Node to Dir")
		return fuse.ENOENT
	}

	oldname := req.OldName
	newname := req.NewName

	d.fs.logger.Printf("origdirid: %v, newDirid: %v, old: %v, newname: %v\n", d, newdir, req.OldName, req.NewName)

	files, err := d.fs.List(ctx, d.ID)
	if err != nil {
		d.fs.logger.Printf("Listing directory failed for %v: %v\n", d, err)
		return fuse.ENOENT
	}

	fileid := int64(-1)
	for _, file := range files {
		if file.Filename == oldname {
			fileid = file.ID
		}
	}

	if fileid < 0 {
		d.fs.logger.Printf("File not found %v: %v\n", oldname, err)
		return fuse.ENOENT
	}

	// request is to ust change the name
	if newdir.ID == d.ID {
		return d.rename(ctx, fileid, oldname, newname)
	}

	// file/directory moved into another directory
	return d.move(ctx, fileid, newdir.ID, oldname, newname)
}

func (d *Dir) rename(ctx context.Context, fileid int64, oldname, newname string) error {
	d.fs.logger.Debugf("Rename request for %v:%v -> %v\n", fileid, oldname, newname)

	if oldname == newname {
		return nil
	}

	return d.fs.Rename(ctx, fileid, newname)
}

func (d *Dir) move(ctx context.Context, fileid int64, parent int64, oldname string, newname string) error {
	d.fs.logger.Debugf("Move request for %v:%v -> %v:%v\n", fileid, oldname, parent, newname)

	err := d.fs.Move(ctx, parent, fileid)
	if err != nil {
		d.fs.logger.Printf("Error moving file: %v\n", err)
		return fuse.ENOENT
	}

	if oldname != newname {
		return d.fs.Rename(ctx, fileid, newname)
	}

	return nil
}

type File struct {
	fs *FileSystem

	*putio.File
}

var (
	_ fs.Node       = (*File)(nil)
	_ fs.NodeOpener = (*File)(nil)
)

func (f *File) Attr(ctx context.Context, attr *fuse.Attr) error {
	f.fs.logger.Debugf("File stat for %v\n", f)

	attr.Inode = uint64(f.ID)
	attr.Mode = os.ModePerm | 0644
	attr.Size = uint64(f.Filesize)
	attr.Ctime = f.CreatedAt.Time
	return nil
}

func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
	f.fs.logger.Debugf("File open request for %v\n", f)

	body, err := f.fs.Download(ctx, f.ID)
	if err != nil {
		f.fs.logger.Printf("Error opening file %v: %v\n", f, err)
		return nil, fuse.ENOENT
	}

	return &FileHandle{
		fs:   f.fs,
		ID:   f.ID,
		Name: f.Filename,
		body: body,
	}, nil
}

type FileHandle struct {
	fs   *FileSystem
	ID   int64
	Name string
	body io.ReadCloser
}

var (
	_ fs.HandleReader   = (*FileHandle)(nil)
	_ fs.HandleReleaser = (*FileHandle)(nil)
)

func (fh *FileHandle) String() string {
	return fmt.Sprintf("<%v - %q>", fh.ID, fh.Name)
}

func (fh *FileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
	fh.fs.logger.Debugln("FileHandler Read request")

	buf := make([]byte, req.Size)
	n, err := io.ReadFull(fh.body, buf)
	if err == io.ErrUnexpectedEOF || err == io.EOF {
		err = nil
	}
	if err != nil {
		fh.fs.logger.Printf("Error reading file %v: %v\n", fh, err)
		return err
	}
	resp.Data = buf[:n]
	return err
}

func (fh *FileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
	fh.fs.logger.Debugln("FileHandler Release request")

	return fh.body.Close()
}

var junkFilePrefixes = []string{
	"._",
	".DS_Store",
	".Spotlight-",
	".git",
	".hidden",
	".metadata_never_index",
	".nomedia",
	".envrc",
}

// isJunkFile reports whether the given file path is considered useless. MacOSX
// Finder is looking for a few hidden files per a file stat request. So this is
// used to speed things a bit.
func isJunkFile(abspath string) bool {
	_, filename := filepath.Split(abspath)
	for _, v := range junkFilePrefixes {
		if strings.HasPrefix(filename, v) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package coreutils

import (
	"errors"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"
)

// AbsPath get the absolute directory path, cleaning out any file names, home directory references, etc.
func AbsPath(path string) string {
	user, userGetErr := user.Current()

	if userGetErr == nil { // If we didn't fail getting the current user
		path = strings.Replace(path, "~", user.HomeDir+Separator, -1) // Replace any home directory reference
	}

	path, _ = filepath.Abs(path) // Get the absolute path of path

	var stripLastElement bool

	if file, openErr := os.Open(path); openErr == nil { // Attempt to open the path, to validate if it is a file or directory
		stat, statErr := file.Stat()
		stripLastElement = (statErr == nil) && !stat.IsDir() // Sets stripLastElement to true if stat.IsDir is not true
	} else { // If we failed to open the directory or file
		lastElement := filepath.Base(path)
		stripLastElement = filepath.Ext(lastElement) != "" // If lastElement is either a dotfile or has an extension, assume it is a file
	}

	if stripLastElement {
		path = filepath.Dir(path) + Separator // Strip out the last element and add the separator
	}

	return path
}

// CopyDirectory will copy a directory, sub-directories, and files
func CopyDirectory(sourceDirectory, destinationDirectory string) error {
	var copyError error

	if IsDir(sourceDirectory) { // If sourceDirectory is a valid directory
		os.MkdirAll(destinationDirectory, NonGlobalFileMode)                     // Make all the needed directories to destinationDirectory
		sourceDirectoryFile, _ := os.Open(sourceDirectory)                       // Get the source directory "file" struct
		directoryContents, directoryReadError := sourceDirectoryFile.Readdir(-1) // Read the directory contents

		if directoryReadError == nil { /// If there was no read error on the directory
			if len(directoryContents) != 0 { // If there is content
				for _, contentItemFileInfo := range directoryContents { // For each FileInfo struct in directoryContents
					contentItemName := contentItemFileInfo.Name() // Get the name of the item
					sourceItemPath := sourceDirectory + "/" + contentItemName
					destinationItemPath := destinationDirectory + "/" + contentItemName

					if contentItemFileInfo.IsDir() { // If this is a directory
						copyError = CopyDirectory(sourceItemPath, destinationItemPath) // Copy this sub-directory and its contents
					} else { // If this is a file
						copyError = CopyFile(sourceItemPath, destinationItemPath) // Copy the directory
					}
				}
			}
		} else { // If there was a read error on the directory
			copyError = errors.New("Unable to read: " + sourceDirectory)
		}
	} else { // If sourceDirectory is not a valid directory
		copyError = errors.New(sourceDirectory + " is not a valid directory.")
	}

	return copyError
}

// CopyFile will copy a file and its relevant permissions
func CopyFile(sourceFile, destinationFile string) error {
	var copyError error

	sourceFileStruct, sourceFileError := os.Open(sourceFile) // Attempt to open the sourceFile

	if sourceFileError == nil { // If there was not an error opening the source file
		sourceFileStats, _ := sourceFileStruct.Stat() // Get the stats of the file

		if sourceFileStats.IsDir() { // If this is actually a directory
			copyError = errors.New(sourceFile + " is a directory. Please use CopyDirectory instead.")
		} else { // If it is indeed a file
			var fileContent []byte
			sourceFileMode := sourceFileStats.Mode() // Get the FileMode of this file
			sourceFileStruct.Close()                 // Close the file

			fileContent, copyError = ioutil.ReadFile(sourceFile) // Read the source file
			copyError = WriteOrUpdateFile(destinationFile, fileContent, sourceFileMode)
		}
	} else { // If the file does not exist
		copyError = errors.New(sourceFile + " does not exist.")
	}

	return copyError
}

// GetFiles will get all the files from a directory.
func GetFiles(path string, recursive bool) ([]string, error) {
	var files []string      // Define files as a []string
	var getFilesError error // Define getFilesError as an error

	if directory, openErr := os.Open(path); openErr == nil {
		directoryContents, directoryReadError := directory.Readdir(-1)

		if directoryReadError == nil { // If there was no issue reading the directory contents
			for _, fileInfoStruct := range directoryContents { // For each FileInfo struct in directoryContents
				name := fileInfoStruct.Name()

				if recursive && fileInfoStruct.IsDir() { // If the FileInfo indicates the object is a directory and we're doing recursive file fetching
					additionalFiles, _ := GetFiles(path + Separator + name, true)
					files = append(files, additionalFiles...)
				} else if !fileInfoStruct.IsDir() { // FileInfo is not a directory
					files = append(files, path+ Separator + name) // Add to files the file's name
				}
			}
		} else { // If there was ano issue reading the directory content
			getFilesError = errors.New("Cannot read the contents of " + path)
		}
	} else { // If path is not a directory
		getFilesError = errors.New(path + " is not a directory.")
	}

	return files, getFilesError
}

// GetFilesContains will return any files from a directory containing a particular string
func GetFilesContains(path, substring string) ([]string, error) {
	var files []string                // Define files as the parsed files
	var getFilesError error           // Define getFilesError as an error
	var allDirectoryContents []string // Define allDirectoryContents as the contents returned (if any) from GetFiles

	allDirectoryContents, getFilesError = GetFiles(path, false) // Get all the files from the path

	if getFilesError == nil { // If there was no issue getting the directory contents
		for _, fileName := range allDirectoryContents { // For each file name in directory contents
			if strings.Contains(filepath.Base(fileName), substring) { // If the file name contains our substring
				files = append(files, fileName) // Append to files
			}
		}
	}

	return files, getFilesError
}

// IsDir checks if the path provided is a directory or not
func IsDir(path string) bool {
	var isDir bool
	fileObject, fileOpenError := os.Open(path) // Open currentDirectory + path

	if fileOpenError == nil { // If there was no error opening the file object
		stat, filePathError := fileObject.Stat() // Get any stats

		if filePathError == nil { // If we got the statistics properly
			isDir = stat.IsDir() // Set isDir to result from stat
		}
	}

	return isDir
}

// WriteOrUpdateFile writes or updates the file contents of the passed file under the leading filepath with the specified sourceFileMode
func WriteOrUpdateFile(file string, fileContent []byte, sourceFileMode os.FileMode) error {
	currentDirectory, _ := os.Getwd()            // Get the working directory
	currentDirectory = AbsPath(currentDirectory) // Get the absolute path of the current working directory

	writeDirectory := AbsPath(file)
	fileName := filepath.Base(file)

	if currentDirectory != writeDirectory { // If the currentDirectory is not the same directory as the writeDirectory
		if createDirsErr := os.MkdirAll(writeDirectory, sourceFileMode); createDirsErr != nil { // If we failed to make all the directories needed
			return errors.New("Failed to create the path leading up to " + fileName + ": " + writeDirectory)
		}
	}

	writeErr := ioutil.WriteFile(writeDirectory+fileName, fileContent, sourceFileMode)

	if writeErr != nil {
		writeErr = errors.New("Failed to write " + fileName + " in directory " + writeDirectory)
	}

	return writeErr
}
<commit_msg>Write to current directory if no directory is provided during WriteOrUpdateFile.<commit_after>package coreutils

import (
	"errors"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"
)

// AbsPath get the absolute directory path, cleaning out any file names, home directory references, etc.
func AbsPath(path string) string {
	user, userGetErr := user.Current()

	if userGetErr == nil { // If we didn't fail getting the current user
		path = strings.Replace(path, "~", user.HomeDir+Separator, -1) // Replace any home directory reference
	}

	path, _ = filepath.Abs(path) // Get the absolute path of path

	var stripLastElement bool

	if file, openErr := os.Open(path); openErr == nil { // Attempt to open the path, to validate if it is a file or directory
		stat, statErr := file.Stat()
		stripLastElement = (statErr == nil) && !stat.IsDir() // Sets stripLastElement to true if stat.IsDir is not true
	} else { // If we failed to open the directory or file
		lastElement := filepath.Base(path)
		stripLastElement = filepath.Ext(lastElement) != "" // If lastElement is either a dotfile or has an extension, assume it is a file
	}

	if stripLastElement {
		path = filepath.Dir(path) + Separator // Strip out the last element and add the separator
	}

	return path
}

// CopyDirectory will copy a directory, sub-directories, and files
func CopyDirectory(sourceDirectory, destinationDirectory string) error {
	var copyError error

	if IsDir(sourceDirectory) { // If sourceDirectory is a valid directory
		os.MkdirAll(destinationDirectory, NonGlobalFileMode)                     // Make all the needed directories to destinationDirectory
		sourceDirectoryFile, _ := os.Open(sourceDirectory)                       // Get the source directory "file" struct
		directoryContents, directoryReadError := sourceDirectoryFile.Readdir(-1) // Read the directory contents

		if directoryReadError == nil { /// If there was no read error on the directory
			if len(directoryContents) != 0 { // If there is content
				for _, contentItemFileInfo := range directoryContents { // For each FileInfo struct in directoryContents
					contentItemName := contentItemFileInfo.Name() // Get the name of the item
					sourceItemPath := sourceDirectory + "/" + contentItemName
					destinationItemPath := destinationDirectory + "/" + contentItemName

					if contentItemFileInfo.IsDir() { // If this is a directory
						copyError = CopyDirectory(sourceItemPath, destinationItemPath) // Copy this sub-directory and its contents
					} else { // If this is a file
						copyError = CopyFile(sourceItemPath, destinationItemPath) // Copy the directory
					}
				}
			}
		} else { // If there was a read error on the directory
			copyError = errors.New("Unable to read: " + sourceDirectory)
		}
	} else { // If sourceDirectory is not a valid directory
		copyError = errors.New(sourceDirectory + " is not a valid directory.")
	}

	return copyError
}

// CopyFile will copy a file and its relevant permissions
func CopyFile(sourceFile, destinationFile string) error {
	var copyError error

	sourceFileStruct, sourceFileError := os.Open(sourceFile) // Attempt to open the sourceFile

	if sourceFileError == nil { // If there was not an error opening the source file
		sourceFileStats, _ := sourceFileStruct.Stat() // Get the stats of the file

		if sourceFileStats.IsDir() { // If this is actually a directory
			copyError = errors.New(sourceFile + " is a directory. Please use CopyDirectory instead.")
		} else { // If it is indeed a file
			var fileContent []byte
			sourceFileMode := sourceFileStats.Mode() // Get the FileMode of this file
			sourceFileStruct.Close()                 // Close the file

			fileContent, copyError = ioutil.ReadFile(sourceFile) // Read the source file
			copyError = WriteOrUpdateFile(destinationFile, fileContent, sourceFileMode)
		}
	} else { // If the file does not exist
		copyError = errors.New(sourceFile + " does not exist.")
	}

	return copyError
}

// GetFiles will get all the files from a directory.
func GetFiles(path string, recursive bool) ([]string, error) {
	var files []string      // Define files as a []string
	var getFilesError error // Define getFilesError as an error

	if directory, openErr := os.Open(path); openErr == nil {
		directoryContents, directoryReadError := directory.Readdir(-1)

		if directoryReadError == nil { // If there was no issue reading the directory contents
			for _, fileInfoStruct := range directoryContents { // For each FileInfo struct in directoryContents
				name := fileInfoStruct.Name()

				if recursive && fileInfoStruct.IsDir() { // If the FileInfo indicates the object is a directory and we're doing recursive file fetching
					additionalFiles, _ := GetFiles(path + Separator + name, true)
					files = append(files, additionalFiles...)
				} else if !fileInfoStruct.IsDir() { // FileInfo is not a directory
					files = append(files, path+ Separator + name) // Add to files the file's name
				}
			}
		} else { // If there was ano issue reading the directory content
			getFilesError = errors.New("Cannot read the contents of " + path)
		}
	} else { // If path is not a directory
		getFilesError = errors.New(path + " is not a directory.")
	}

	return files, getFilesError
}

// GetFilesContains will return any files from a directory containing a particular string
func GetFilesContains(path, substring string) ([]string, error) {
	var files []string                // Define files as the parsed files
	var getFilesError error           // Define getFilesError as an error
	var allDirectoryContents []string // Define allDirectoryContents as the contents returned (if any) from GetFiles

	allDirectoryContents, getFilesError = GetFiles(path, false) // Get all the files from the path

	if getFilesError == nil { // If there was no issue getting the directory contents
		for _, fileName := range allDirectoryContents { // For each file name in directory contents
			if strings.Contains(filepath.Base(fileName), substring) { // If the file name contains our substring
				files = append(files, fileName) // Append to files
			}
		}
	}

	return files, getFilesError
}

// IsDir checks if the path provided is a directory or not
func IsDir(path string) bool {
	var isDir bool
	fileObject, fileOpenError := os.Open(path) // Open currentDirectory + path

	if fileOpenError == nil { // If there was no error opening the file object
		stat, filePathError := fileObject.Stat() // Get any stats

		if filePathError == nil { // If we got the statistics properly
			isDir = stat.IsDir() // Set isDir to result from stat
		}
	}

	return isDir
}

// WriteOrUpdateFile writes or updates the file contents of the passed file under the leading filepath with the specified sourceFileMode
func WriteOrUpdateFile(file string, fileContent []byte, sourceFileMode os.FileMode) error {
	var writeDirectory string // Directory to write file

	currentDirectory, _ := os.Getwd()            // Get the working directory
	currentDirectory = AbsPath(currentDirectory) // Get the absolute path of the current working directory
	fileName := filepath.Base(file)

	if file == fileName { // If we did not specify a directory to write to
		writeDirectory = currentDirectory // Set to the current directory
	} else {
		writeDirectory = AbsPath(file)
	}

	if currentDirectory != writeDirectory { // If the currentDirectory is not the same directory as the writeDirectory
		if createDirsErr := os.MkdirAll(writeDirectory, sourceFileMode); createDirsErr != nil { // If we failed to make all the directories needed
			return errors.New("Failed to create the path leading up to " + fileName + ": " + writeDirectory)
		}
	}

	writeErr := ioutil.WriteFile(writeDirectory+fileName, fileContent, sourceFileMode)

	if writeErr != nil {
		writeErr = errors.New("Failed to write " + fileName + " in directory " + writeDirectory)
	}

	return writeErr
}
<|endoftext|>


<commit_before>/**
 *
 * @author  chosen0ne(louzhenlin86@126.com)
 * @date    2017-10-30 15:56:36
 */

package goutils

import (
	"io"
)

func Write(l Logger, w io.Writer, b []byte) error {
	if n, err := w.Write(b); err != nil {
		l.Exception(err, "failed to write")
		return err
	} else if n != len(b) {
		l.Error("failed to write a whole buffer, buf: %d, write: %d", len(b), n)
		return NewErr("failed to write a whole buffer, buf: %d, write: %d", len(b), n)
	}

	return nil
}
<commit_msg>feat: support nil logger for Write<commit_after>/**
 *
 * @author  chosen0ne(louzhenlin86@126.com)
 * @date    2017-10-30 15:56:36
 */

package goutils

import (
	"io"
)

func Write(l Logger, w io.Writer, b []byte) error {
	if n, err := w.Write(b); err != nil {
		if l != nil {
			l.Exception(err, "failed to write")
		}
		return err
	} else if n != len(b) {
		if l != nil {
			l.Error("failed to write a whole buffer, buf: %d, write: %d", len(b), n)
		}
		return NewErr("failed to write a whole buffer, buf: %d, write: %d", len(b), n)
	}

	return nil
}
<|endoftext|>


<commit_before>package jo

// Parsing events.
type Event int

const (
	Continue = iota

	StringStart
	StringEnd
	BoolStart
	BoolEnd
	NullStart
	NullEnd

	SyntaxError
)

// Parser states.
const (
	_StateValue = iota

	_StateStringUnicode  // "\u
	_StateStringUnicode2 // "\u1
	_StateStringUnicode3 // "\u12
	_StateStringUnicode4 // "\u123
	_StateString         // "
	_StateStringEscaped  // "\

	_StateTrue  // t
	_StateTrue2 // tr
	_StateTrue3 // tru

	_StateFalse  // f
	_StateFalse2 // fa
	_StateFalse3 // fal
	_StateFalse4 // fals

	_StateNull  // n
	_StateNull2 // nu
	_StateNull3 // nul

	_StateDone
	_StateSyntaxError
)

// Our own little implementation of the `error` interface.
type syntaxError string

func (e syntaxError) Error() string {
	return string(e)
}

// Parser state machine.
type Parser struct {
	state int
	queue []int
	err   error
}

// Parses a byte slice containing JSON data. Returns the number of bytes
// read and an appropriate Event.
func (p *Parser) Parse(input []byte) (int, Event) {
	for i, b := range input {
		switch p.state {
		case _StateValue:
			switch b {
			case '"':
				p.state = _StateString
				return i + 1, StringStart
			case 't':
				p.state = _StateTrue
				return i + 1, BoolStart
			case 'f':
				p.state = _StateFalse
				return i + 1, BoolStart
			case 'n':
				p.state = _StateNull
				return i + 1, NullStart
			default:
				return i, p.error(`_StateValue: @todo`)
			}

		case _StateStringUnicode, _StateStringUnicode2,
			_StateStringUnicode3, _StateStringUnicode4:
			switch {
			case '0' <= b && b <= '9':
			case 'a' <= b && b <= 'f':
			case 'A' <= b && b <= 'F':
			default:
				return i, p.error(`_StateStringUnicodeX: @todo`)
			}

			p.state++ // note that `_StateString == (_StateStringUnicode4 + 1)`

		case _StateString:
			switch {
			case b == '"':
				p.state = p.next()
				return i + 1, StringEnd
			case b == '\\':
				p.state = _StateStringEscaped
			case b < 0x20:
				return i, p.error(`_StateString: @todo`)
			}

		case _StateStringEscaped:
			switch b {
			case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
				p.state = _StateString
			case 'u':
				p.state = _StateStringUnicode
			default:
				return i, p.error(`_StateStringEscaped: @todo`)
			}

		case _StateTrue:
			if b != 'r' {
				return i, p.error(`_StateTrue: @todo`)
			}
			p.state++

		case _StateTrue2:
			if b != 'u' {
				return i, p.error(`_StateTrue2: @todo`)
			}
			p.state++

		case _StateTrue3:
			if b != 'e' {
				return i, p.error(`_StateTrue3: @todo`)
			}
			p.state = p.next()

			return i + 1, BoolEnd

		case _StateFalse:
			if b != 'a' {
				return i, p.error(`_StateFalse: @todo`)
			}
			p.state++

		case _StateFalse2:
			if b != 'l' {
				return i, p.error(`_StateFalse2: @todo`)
			}
			p.state++

		case _StateFalse3:
			if b != 's' {
				return i, p.error(`_StateFalse3: @todo`)
			}
			p.state++

		case _StateFalse4:
			if b != 'e' {
				return i, p.error(`_StateFalse4: @todo`)
			}
			p.state = p.next()

			return i + 1, BoolEnd

		case _StateNull:
			if b != 'u' {
				return i, p.error(`_StateNull: @todo`)
			}
			p.state++

		case _StateNull2:
			if b != 'l' {
				return i, p.error(`_StateNull2: @todo`)
			}
			p.state++

		case _StateNull3:
			if b != 'l' {
				return i, p.error(`_StateNull3: @todo`)
			}
			p.state = p.next()

			return i + 1, NullEnd

		case _StateDone:
			return i, p.error(`_StateDone: @todo`)

		default:
			panic(`invalid state`)
		}
	}

	return len(input) - 1, Continue
}

// Pops the next state off the parser struct's queue.
func (p *Parser) next() int {
	length := len(p.queue)

	// with the "state queue" empty, we can only wait for EOF
	if length == 0 {
		return _StateDone
	}

	state := p.queue[length]
	p.queue = p.queue[:length-1]

	return state
}

// Insert a new state at the top of the queue.
func (p *Parser) push(state int) {
	p.queue = append(p.queue, state)
}

// Registers a syntax error. Always returns a SyntaxError event.
func (p *Parser) error(message string) Event {
	p.err = syntaxError(message)
	return SyntaxError
}
<commit_msg>Add support for number literals<commit_after>package jo

// Parsing events.
type Event int

const (
	None = iota
	SyntaxError

	StringStart
	StringEnd
	NumberStart
	NumberEnd
	BoolStart
	BoolEnd
	NullStart
	NullEnd
)

// Parser states.
const (
	_StateValue = iota

	_StateStringUnicode  // "\u
	_StateStringUnicode2 // "\u1
	_StateStringUnicode3 // "\u12
	_StateStringUnicode4 // "\u123
	_StateString         // "
	_StateStringEscaped  // "\

	_StateNumberNegative           // -
	_StateNumberZero               // 0
	_StateNumber                   // 123
	_StateNumberDotFirstDigit      // 123.
	_StateNumberDotDigit           // 123.4
	_StateNumberExponentSign       // 123e
	_StateNumberExponentFirstDigit // 123e+
	_StateNumberExponentDigit      // 123e+1

	_StateTrue  // t
	_StateTrue2 // tr
	_StateTrue3 // tru

	_StateFalse  // f
	_StateFalse2 // fa
	_StateFalse3 // fal
	_StateFalse4 // fals

	_StateNull  // n
	_StateNull2 // nu
	_StateNull3 // nul

	_StateDone
	_StateSyntaxError
)

// Our own little implementation of the `error` interface.
type syntaxError string

func (e syntaxError) Error() string {
	return string(e)
}

// Parser state machine.
type Parser struct {
	state int
	queue []int
	err   error
}

// Parses a byte slice containing JSON data. Returns the number of bytes
// read and an appropriate Event.
func (p *Parser) Parse(input []byte) (int, Event) {
	for i, b := range input {
		switch p.state {
		case _StateValue:
			switch {
			case b == '"':
				p.state = _StateString
				return i + 1, StringStart
			case b == '-':
				p.state = _StateNumberNegative
				return i + 1, NumberStart
			case b == '0':
				p.state = _StateNumberZero
				return i + 1, NumberStart
			case '1' <= b && b <= '9':
				p.state = _StateNumber
				return i + 1, NumberStart
			case b == 't':
				p.state = _StateTrue
				return i + 1, BoolStart
			case b == 'f':
				p.state = _StateFalse
				return i + 1, BoolStart
			case b == 'n':
				p.state = _StateNull
				return i + 1, NullStart
			default:
				return i, p.error(`_StateValue: @todo`)
			}

		case _StateStringUnicode, _StateStringUnicode2,
			_StateStringUnicode3, _StateStringUnicode4:
			switch {
			case '0' <= b && b <= '9':
			case 'a' <= b && b <= 'f':
			case 'A' <= b && b <= 'F':
			default:
				return i, p.error(`_StateStringUnicodeX: @todo`)
			}

			p.state++ // note that `_StateString == (_StateStringUnicode4 + 1)`

		case _StateString:
			switch {
			case b == '"':
				p.state = p.next()
				return i + 1, StringEnd
			case b == '\\':
				p.state = _StateStringEscaped
			case b < 0x20:
				return i, p.error(`_StateString: @todo`)
			}

		case _StateStringEscaped:
			switch b {
			case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
				p.state = _StateString
			case 'u':
				p.state = _StateStringUnicode
			default:
				return i, p.error(`_StateStringEscaped: @todo`)
			}

		case _StateNumberNegative:
			switch {
			case b == '0':
				p.state = _StateNumberZero
			case '1' <= b && b <= '9':
				p.state = _StateNumber
			default:
				return i, p.error(`_StateNumberNegative: @todo`)
			}

		case _StateNumber:
			if '0' <= b && b <= '9' {
				break
			}
			fallthrough

		case _StateNumberZero:
			switch b {
			case '.':
				p.state = _StateNumberDotFirstDigit
			case 'e', 'E':
				p.state = _StateNumberExponentSign
			default:
				return i, p.error(`_StateNumberZero: @todo`)
			}

		case _StateNumberDotFirstDigit:
			if b < '0' || b > '9' {
				return i, p.error(`_StateNumberDot: @todo`)
			}
			p.state++

		case _StateNumberDotDigit:
			switch {
			case b == 'e', b == 'E':
				p.state = _StateNumberExponentSign
			case b < '0' || b > '9':
				return i, p.error(`_StateNumberDotDigit: @todo`)
			}

		case _StateNumberExponentSign:
			p.state++
			if b == '+' || b == '-' {
				break
			}
			fallthrough

		case _StateNumberExponentFirstDigit:
			if b < '0' || b > '9' {
				return i, p.error(`_StateNumberAfterExponent: @todo`)
			}
			p.state++

		case _StateNumberExponentDigit:
			if b < '0' || b > '9' {
				p.state = p.next()
				return i + 1, NumberEnd
			}

		case _StateTrue:
			if b != 'r' {
				return i, p.error(`_StateTrue: @todo`)
			}
			p.state++

		case _StateTrue2:
			if b != 'u' {
				return i, p.error(`_StateTrue2: @todo`)
			}
			p.state++

		case _StateTrue3:
			if b != 'e' {
				return i, p.error(`_StateTrue3: @todo`)
			}
			p.state = p.next()

			return i + 1, BoolEnd

		case _StateFalse:
			if b != 'a' {
				return i, p.error(`_StateFalse: @todo`)
			}
			p.state++

		case _StateFalse2:
			if b != 'l' {
				return i, p.error(`_StateFalse2: @todo`)
			}
			p.state++

		case _StateFalse3:
			if b != 's' {
				return i, p.error(`_StateFalse3: @todo`)
			}
			p.state++

		case _StateFalse4:
			if b != 'e' {
				return i, p.error(`_StateFalse4: @todo`)
			}
			p.state = p.next()

			return i + 1, BoolEnd

		case _StateNull:
			if b != 'u' {
				return i, p.error(`_StateNull: @todo`)
			}
			p.state++

		case _StateNull2:
			if b != 'l' {
				return i, p.error(`_StateNull2: @todo`)
			}
			p.state++

		case _StateNull3:
			if b != 'l' {
				return i, p.error(`_StateNull3: @todo`)
			}
			p.state = p.next()

			return i + 1, NullEnd

		case _StateDone:
			return i, p.error(`_StateDone: @todo`)

		default:
			panic(`invalid state`)
		}
	}

	return len(input), None
}

// Informs the parser not to expect any further input. Returns
// pending NumberEnd events if there are any, or a SyntaxError
// if EOF was not expected -- otherwise None.
func (p *Parser) Eof() Event {
	switch p.state {
	case _StateNumberZero,
		_StateNumber,
		_StateNumberDotDigit,
		_StateNumberExponentDigit:
		p.state = _StateDone
		return NumberEnd
	case _StateDone:
		return None
	}
	return p.error(`.Eof(): @todo`)
}

// Pops the next state off the parser struct's queue.
func (p *Parser) next() int {
	length := len(p.queue)

	// with the "state queue" empty, we can only wait for EOF
	if length == 0 {
		return _StateDone
	}

	state := p.queue[length]
	p.queue = p.queue[:length-1]

	return state
}

// Insert a new state at the top of the queue.
func (p *Parser) push(state int) {
	p.queue = append(p.queue, state)
}

// Registers a syntax error. Always returns a SyntaxError event.
func (p *Parser) error(message string) Event {
	p.err = syntaxError(message)
	return SyntaxError
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"errors"
	"fmt"
	"regexp"

	"github.com/FactomProject/btcutil/base58"
	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/goleveldb/leveldb"
)

var (
	ErrFeeTooLow     = errors.New("wallet: Insufficient Fee")
	ErrNoSuchAddress = errors.New("wallet: No such address")
	ErrTXExists      = errors.New("wallet: Transaction name already exists")
	ErrTXNotExists   = errors.New("wallet: Transaction name was not found")
	ErrTXNoInputs    = errors.New("wallet: Transaction has no inputs")
	ErrTXInvalidName = errors.New("wallet: Transaction name is not valid")
)

func (w *Wallet) NewTransaction(name string) error {
	if w.TransactionExists(name) {
		return ErrTXExists
	}

	// check that the transaction name is valid
	if name == "" {
		return ErrTXInvalidName
	}
	if len(name) > 32 {
		return ErrTXInvalidName
	}
	if match, err := regexp.MatchString("[^a-zA-Z0-9_-]", name); err != nil {
		return err
	} else if match {
		return ErrTXInvalidName
	}

	tx := new(factoid.Transaction)
	tx.SetTimestamp(primitives.NewTimestampNow())

	w.txlock.Lock()
	defer w.txlock.Unlock()

	w.transactions[name] = tx
	return nil
}

func (w *Wallet) DeleteTransaction(name string) error {
	if !w.TransactionExists(name) {
		return ErrTXNotExists
	}

	w.txlock.Lock()
	defer w.txlock.Unlock()
	delete(w.transactions, name)
	return nil
}

func (w *Wallet) AddInput(name, address string, amount uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	a, err := w.GetFCTAddress(address)
	if err == leveldb.ErrNotFound {
		return ErrNoSuchAddress
	} else if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	// First look if this is really an update
	for _, input := range tx.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			input.SetAmount(amount)
			return nil
		}
	}

	// Add our new input
	tx.AddInput(adr, amount)
	tx.AddRCD(factoid.NewRCD_1(a.PubBytes()))

	return nil
}

func (w *Wallet) AddOutput(name, address string, amount uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	// Make sure that this is a valid Factoid output
	if factom.AddressStringType(address) != factom.FactoidPub {
		return errors.New("Invalid Factoid Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	// First look if this is really an update
	for _, output := range tx.GetOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(amount)
			return nil
		}
	}

	tx.AddOutput(adr, amount)

	return nil
}

func (w *Wallet) AddECOutput(name, address string, amount uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	// Make sure that this is a valid Entry Credit output
	if factom.AddressStringType(address) != factom.ECPub {
		return errors.New("Invalid Entry Credit Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	// First look if this is really an update
	for _, output := range tx.GetECOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(amount)
			return nil
		}
	}

	tx.AddECOutput(adr, amount)

	return nil
}

func (w *Wallet) AddFee(name, address string, rate uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	{
		ins, err := tx.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := tx.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := tx.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	txfee, err := tx.CalculateFee(rate)
	if err != nil {
		return err
	}

	a, err := w.GetFCTAddress(address)
	if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	for _, input := range tx.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			amt, err := factoid.ValidateAmounts(input.GetAmount(), txfee)
			if err != nil {
				return err
			}
			input.SetAmount(amt)
			return nil
		}
	}
	return fmt.Errorf("%s is not an input to the transaction.", address)
}

func (w *Wallet) SubFee(name, address string, rate uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	{
		ins, err := tx.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := tx.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := tx.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	txfee, err := tx.CalculateFee(rate)
	if err != nil {
		return err
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	for _, output := range tx.GetOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(output.GetAmount() - txfee)
			return nil
		}
	}
	return fmt.Errorf("%s is not an output to the transaction.", address)
}

// SignTransaction signs a tmp transaction in the wallet with the appropriate
// keys from the wallet db
// force=true ignores the existing balance and fee overpayment checks.
func (w *Wallet) SignTransaction(name string, force bool) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	if force == false {
		// check that the address balances are sufficient for the transaction
		if err := checkCovered(tx); err != nil {
			return err
		}

		// check that the fee is being paid (and not overpaid)
		if err := checkFee(tx); err != nil {
			return err
		}
	}

	data, err := tx.MarshalBinarySig()
	if err != nil {
		return err
	}

	rcds := tx.GetRCDs()
	if len(rcds) == 0 {
		return ErrTXNoInputs
	}
	for i, rcd := range rcds {
		a, err := rcd.GetAddress()
		if err != nil {
			return err
		}

		f, err := w.GetFCTAddress(primitives.ConvertFctAddressToUserStr(a))
		if err != nil {
			return err
		}
		sig := factoid.NewSingleSignatureBlock(f.SecBytes(), data)
		tx.SetSignatureBlock(i, sig)
	}

	return nil
}

func (w *Wallet) GetTransaction(name string) (*factoid.Transaction, error) {
	if !w.TransactionExists(name) {
		return nil, ErrTXNotExists
	}

	w.txlock.Lock()
	defer w.txlock.Unlock()

	return w.transactions[name], nil
}

func (w *Wallet) GetTransactions() map[string]*factoid.Transaction {
	return w.transactions
}

func (w *Wallet) TransactionExists(name string) bool {
	w.txlock.Lock()
	defer w.txlock.Unlock()

	if _, exists := w.transactions[name]; exists {
		return true
	}
	return false
}

func (w *Wallet) ComposeTransaction(name string) (*factom.JSON2Request, error) {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return nil, err
	}

	type txreq struct {
		Transaction string `json:"transaction"`
	}

	param := new(txreq)
	if p, err := tx.MarshalBinary(); err != nil {
		return nil, err
	} else {
		param.Transaction = hex.EncodeToString(p)
	}

	req := factom.NewJSON2Request("factoid-submit", APICounter(), param)

	return req, nil
}

// Hexencoded transaction
func (w *Wallet) ImportComposedTransaction(name string, hexEncoded string) error {
	tx := new(factoid.Transaction)
	data, err := hex.DecodeString(hexEncoded)
	if err != nil {
		return err
	}

	err = tx.UnmarshalBinary(data)
	if err != nil {
		return err
	}

	w.txlock.Lock()
	w.transactions[name] = tx
	w.txlock.Unlock()

	return nil
}

func checkCovered(tx *factoid.Transaction) error {
	for _, in := range tx.GetInputs() {
		balance, err := factom.GetFactoidBalance(in.GetUserAddress())
		if err != nil {
			return err
		}
		if uint64(balance) < in.GetAmount() {
			return fmt.Errorf(
				"Address %s balance is too low. Available: %s Needed: %s",
				in.GetUserAddress(),
				factom.FactoshiToFactoid(uint64(balance)),
				factom.FactoshiToFactoid(in.GetAmount()),
			)
		}
	}
	return nil
}

func checkFee(tx *factoid.Transaction) error {
	ins, err := tx.TotalInputs()
	if err != nil {
		return err
	}
	outs, err := tx.TotalOutputs()
	if err != nil {
		return err
	}
	ecs, err := tx.TotalECs()
	if err != nil {
		return err
	}

	// fee is the fee that will be paid
	fee := int64(ins) - int64(outs) - int64(ecs)

	if fee <= 0 {
		return ErrFeeTooLow
	}

	rate, err := factom.GetRate()
	if err != nil {
		return err
	}

	// cfee is the fee calculated for the transaction
	var cfee int64
	if c, err := tx.CalculateFee(rate); err != nil {
		return err
	} else if c == 0 {
		return errors.New("wallet: Could not calculate fee")
	} else {
		cfee = int64(c)
	}

	// fee is too low
	if fee < cfee {
		return ErrFeeTooLow
	}

	// fee is too high (over 10x cfee)
	if fee >= cfee*10 {
		return fmt.Errorf(
			"wallet: Overpaying fee by >10x. Paying: %v Requires: %v",
			factom.FactoshiToFactoid(uint64(fee)),
			factom.FactoshiToFactoid(uint64(cfee)),
		)
	}

	return nil
}
<commit_msg>go fmt<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"errors"
	"fmt"
	"regexp"

	"github.com/FactomProject/btcutil/base58"
	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/goleveldb/leveldb"
)

var (
	ErrFeeTooLow     = errors.New("wallet: Insufficient Fee")
	ErrNoSuchAddress = errors.New("wallet: No such address")
	ErrTXExists      = errors.New("wallet: Transaction name already exists")
	ErrTXNotExists   = errors.New("wallet: Transaction name was not found")
	ErrTXNoInputs    = errors.New("wallet: Transaction has no inputs")
	ErrTXInvalidName = errors.New("wallet: Transaction name is not valid")
)

func (w *Wallet) NewTransaction(name string) error {
	if w.TransactionExists(name) {
		return ErrTXExists
	}

	// check that the transaction name is valid
	if name == "" {
		return ErrTXInvalidName
	}
	if len(name) > 32 {
		return ErrTXInvalidName
	}
	if match, err := regexp.MatchString("[^a-zA-Z0-9_-]", name); err != nil {
		return err
	} else if match {
		return ErrTXInvalidName
	}

	tx := new(factoid.Transaction)
	tx.SetTimestamp(primitives.NewTimestampNow())

	w.txlock.Lock()
	defer w.txlock.Unlock()

	w.transactions[name] = tx
	return nil
}

func (w *Wallet) DeleteTransaction(name string) error {
	if !w.TransactionExists(name) {
		return ErrTXNotExists
	}

	w.txlock.Lock()
	defer w.txlock.Unlock()
	delete(w.transactions, name)
	return nil
}

func (w *Wallet) AddInput(name, address string, amount uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	a, err := w.GetFCTAddress(address)
	if err == leveldb.ErrNotFound {
		return ErrNoSuchAddress
	} else if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	// First look if this is really an update
	for _, input := range tx.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			input.SetAmount(amount)
			return nil
		}
	}

	// Add our new input
	tx.AddInput(adr, amount)
	tx.AddRCD(factoid.NewRCD_1(a.PubBytes()))

	return nil
}

func (w *Wallet) AddOutput(name, address string, amount uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	// Make sure that this is a valid Factoid output
	if factom.AddressStringType(address) != factom.FactoidPub {
		return errors.New("Invalid Factoid Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	// First look if this is really an update
	for _, output := range tx.GetOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(amount)
			return nil
		}
	}

	tx.AddOutput(adr, amount)

	return nil
}

func (w *Wallet) AddECOutput(name, address string, amount uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	// Make sure that this is a valid Entry Credit output
	if factom.AddressStringType(address) != factom.ECPub {
		return errors.New("Invalid Entry Credit Address")
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	// First look if this is really an update
	for _, output := range tx.GetECOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(amount)
			return nil
		}
	}

	tx.AddECOutput(adr, amount)

	return nil
}

func (w *Wallet) AddFee(name, address string, rate uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	{
		ins, err := tx.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := tx.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := tx.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	txfee, err := tx.CalculateFee(rate)
	if err != nil {
		return err
	}

	a, err := w.GetFCTAddress(address)
	if err != nil {
		return err
	}
	adr := factoid.NewAddress(a.RCDHash())

	for _, input := range tx.GetInputs() {
		if input.GetAddress().IsSameAs(adr) {
			amt, err := factoid.ValidateAmounts(input.GetAmount(), txfee)
			if err != nil {
				return err
			}
			input.SetAmount(amt)
			return nil
		}
	}
	return fmt.Errorf("%s is not an input to the transaction.", address)
}

func (w *Wallet) SubFee(name, address string, rate uint64) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	if !factom.IsValidAddress(address) {
		return errors.New("Invalid Address")
	}

	{
		ins, err := tx.TotalInputs()
		if err != nil {
			return err
		}
		outs, err := tx.TotalOutputs()
		if err != nil {
			return err
		}
		ecs, err := tx.TotalECs()
		if err != nil {
			return err
		}

		if ins != outs+ecs {
			return fmt.Errorf("Inputs and outputs don't add up")
		}
	}

	txfee, err := tx.CalculateFee(rate)
	if err != nil {
		return err
	}

	adr := factoid.NewAddress(base58.Decode(address)[2:34])

	for _, output := range tx.GetOutputs() {
		if output.GetAddress().IsSameAs(adr) {
			output.SetAmount(output.GetAmount() - txfee)
			return nil
		}
	}
	return fmt.Errorf("%s is not an output to the transaction.", address)
}

// SignTransaction signs a tmp transaction in the wallet with the appropriate
// keys from the wallet db
// force=true ignores the existing balance and fee overpayment checks.
func (w *Wallet) SignTransaction(name string, force bool) error {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return err
	}

	if force == false {
		// check that the address balances are sufficient for the transaction
		if err := checkCovered(tx); err != nil {
			return err
		}

		// check that the fee is being paid (and not overpaid)
		if err := checkFee(tx); err != nil {
			return err
		}
	}

	data, err := tx.MarshalBinarySig()
	if err != nil {
		return err
	}

	rcds := tx.GetRCDs()
	if len(rcds) == 0 {
		return ErrTXNoInputs
	}
	for i, rcd := range rcds {
		a, err := rcd.GetAddress()
		if err != nil {
			return err
		}

		f, err := w.GetFCTAddress(primitives.ConvertFctAddressToUserStr(a))
		if err != nil {
			return err
		}
		sig := factoid.NewSingleSignatureBlock(f.SecBytes(), data)
		tx.SetSignatureBlock(i, sig)
	}

	return nil
}

func (w *Wallet) GetTransaction(name string) (*factoid.Transaction, error) {
	if !w.TransactionExists(name) {
		return nil, ErrTXNotExists
	}

	w.txlock.Lock()
	defer w.txlock.Unlock()

	return w.transactions[name], nil
}

func (w *Wallet) GetTransactions() map[string]*factoid.Transaction {
	return w.transactions
}

func (w *Wallet) TransactionExists(name string) bool {
	w.txlock.Lock()
	defer w.txlock.Unlock()

	if _, exists := w.transactions[name]; exists {
		return true
	}
	return false
}

func (w *Wallet) ComposeTransaction(name string) (*factom.JSON2Request, error) {
	tx, err := w.GetTransaction(name)
	if err != nil {
		return nil, err
	}

	type txreq struct {
		Transaction string `json:"transaction"`
	}

	param := new(txreq)
	if p, err := tx.MarshalBinary(); err != nil {
		return nil, err
	} else {
		param.Transaction = hex.EncodeToString(p)
	}

	req := factom.NewJSON2Request("factoid-submit", APICounter(), param)

	return req, nil
}

// Hexencoded transaction
func (w *Wallet) ImportComposedTransaction(name string, hexEncoded string) error {
	tx := new(factoid.Transaction)
	data, err := hex.DecodeString(hexEncoded)
	if err != nil {
		return err
	}

	err = tx.UnmarshalBinary(data)
	if err != nil {
		return err
	}

	w.txlock.Lock()
	w.transactions[name] = tx
	w.txlock.Unlock()

	return nil
}

func checkCovered(tx *factoid.Transaction) error {
	for _, in := range tx.GetInputs() {
		balance, err := factom.GetFactoidBalance(in.GetUserAddress())
		if err != nil {
			return err
		}
		if uint64(balance) < in.GetAmount() {
			return fmt.Errorf(
				"Address %s balance is too low. Available: %s Needed: %s",
				in.GetUserAddress(),
				factom.FactoshiToFactoid(uint64(balance)),
				factom.FactoshiToFactoid(in.GetAmount()),
			)
		}
	}
	return nil
}

func checkFee(tx *factoid.Transaction) error {
	ins, err := tx.TotalInputs()
	if err != nil {
		return err
	}
	outs, err := tx.TotalOutputs()
	if err != nil {
		return err
	}
	ecs, err := tx.TotalECs()
	if err != nil {
		return err
	}

	// fee is the fee that will be paid
	fee := int64(ins) - int64(outs) - int64(ecs)

	if fee <= 0 {
		return ErrFeeTooLow
	}

	rate, err := factom.GetRate()
	if err != nil {
		return err
	}

	// cfee is the fee calculated for the transaction
	var cfee int64
	if c, err := tx.CalculateFee(rate); err != nil {
		return err
	} else if c == 0 {
		return errors.New("wallet: Could not calculate fee")
	} else {
		cfee = int64(c)
	}

	// fee is too low
	if fee < cfee {
		return ErrFeeTooLow
	}

	// fee is too high (over 10x cfee)
	if fee >= cfee*10 {
		return fmt.Errorf(
			"wallet: Overpaying fee by >10x. Paying: %v Requires: %v",
			factom.FactoshiToFactoid(uint64(fee)),
			factom.FactoshiToFactoid(uint64(cfee)),
		)
	}

	return nil
}
<|endoftext|>