Ejemplo n.º 1
0
func (s *Server) upload(from *[32]byte, conn *transport.Conn, upload *pond.Upload) *pond.Reply {
	account, ok := s.getAccount(from)
	if !ok {
		return &pond.Reply{Status: pond.Reply_NO_ACCOUNT.Enum()}
	}

	if *upload.Size < 1 {
		return &pond.Reply{Status: pond.Reply_PARSE_ERROR.Enum()}
	}

	path := filepath.Join(account.FilePath(), strconv.FormatUint(*upload.Id, 16))

	if !account.LoadFileInfo() {
		return &pond.Reply{Status: pond.Reply_INTERNAL_ERROR.Enum()}
	}

	file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Printf("Failed to create file %s: %s", path, err)
		return &pond.Reply{Status: pond.Reply_INTERNAL_ERROR.Enum()}
	}
	defer file.Close()

	offset, err := file.Seek(0, 2 /* from end */)

	switch {
	case offset == *upload.Size:
		return &pond.Reply{Status: pond.Reply_FILE_COMPLETE.Enum()}
	case offset > *upload.Size:
		return &pond.Reply{Status: pond.Reply_FILE_LARGER_THAN_SIZE.Enum()}
	}

	size := *upload.Size - offset
	if !account.ReserveFile(offset > 0, size) {
		return &pond.Reply{Status: pond.Reply_OVER_QUOTA.Enum()}
	}

	var resume *int64
	if offset > 0 {
		resume = proto.Int64(offset)
	}

	reply := &pond.Reply{
		Upload: &pond.UploadReply{
			Resume: resume,
		},
	}
	if err := conn.WriteProto(reply); err != nil {
		return nil
	}

	n, err := io.Copy(file, io.LimitReader(conn, size))
	switch {
	case n == 0:
		os.Remove(path)
		account.ReleaseFile(true, size)
	case n < size:
		account.ReleaseFile(false, size-n)
	case n == size:
		if err == nil {
			conn.Write([]byte{0})
		}
	case n > size:
		panic("impossible")
	}

	return nil
}