예제 #1
0
func (d *Daemon) saveMessage(message *proto.Message) error {
	// generate metadata file
	metadata := proto.ConversationMetadata{
		Participants: message.Participants,
		Subject:      message.Subject,
	}
	// generate conversation name
	convName := persistence.ConversationName(&metadata)
	messageName := persistence.MessageName(time.Unix(0, message.Date), string(message.Dename))

	// create conversation directory if it doesn't already exist
	convDir := filepath.Join(d.ConversationDir(), convName)
	outboxDir := filepath.Join(d.OutboxDir(), convName)
	_, err := os.Stat(convDir)
	if err != nil && !os.IsNotExist(err) {
		return err
	} else if err != nil && os.IsNotExist(err) {
		// new message in existing conversation
		if err := d.conversationToConversations(&metadata); err != nil {
			return err
		}
	}

	err = d.AtomicWriteFile(filepath.Join(convDir, messageName), message.Contents, 0600)
	if err != nil {
		return err
	}

	// to outbox
	tdir, err := d.MkdirInTemp()
	if err != nil {
		return err
	}
	defer shred.RemoveAll(tdir)
	err = d.MarshalToFile(filepath.Join(tdir, persistence.MetadataFileName), &metadata)
	if err != nil {
		return err
	}
	if err = os.Rename(filepath.Join(tdir), outboxDir); err != nil && !os.IsExist(err) && !strings.Contains(err.Error(), "directory not empty") {
		return err
	}
	return nil
}
예제 #2
0
func (d *Daemon) processOutboxDir(dirname string) error {
	// TODO: refactor: separate message assembly and filesystem access?
	// parse metadata
	metadataFile := filepath.Join(dirname, persistence.MetadataFileName)
	if _, err := os.Stat(metadataFile); err != nil {
		return nil // no metadata --> not an outgoing message
	}

	metadata := proto.ConversationMetadata{}
	err := persistence.UnmarshalFromFile(metadataFile, &metadata)
	if err != nil {
		return err
	}

	metadata.Participants = append(metadata.Participants, d.Dename)
	undupStrings(metadata.Participants)
	sort.Strings(metadata.Participants)
	convName := persistence.ConversationName(&metadata)

	// load messages
	potentialMessages, err := ioutil.ReadDir(dirname)
	if err != nil {
		return err
	}
	messages := make([][]byte, 0, len(potentialMessages))
	for _, finfo := range potentialMessages {
		if !finfo.IsDir() && finfo.Name() != persistence.MetadataFileName {
			msg, err := ioutil.ReadFile(filepath.Join(dirname, finfo.Name()))
			if err != nil {
				return err
			}

			// make protobuf for message; append it
			d.ourDenameLookupMu.Lock()
			payload := proto.Message{
				Dename:       d.Dename,
				DenameLookup: d.ourDenameLookup,
				Contents:     msg,
				Subject:      metadata.Subject,
				Participants: metadata.Participants,
				Date:         finfo.ModTime().UnixNano(),
			}
			d.ourDenameLookupMu.Unlock()
			payloadBytes, err := payload.Marshal()
			if err != nil {
				return err
			}
			messages = append(messages, payloadBytes)
		}
	}
	if len(messages) == 0 {
		return nil // no messages to send, just the metadata file
	}

	if err := d.conversationToConversations(&metadata); err != nil && !os.IsExist(err) && !strings.Contains(fmt.Sprint(err), "directory not empty") {
		log.Fatal(err)
	}

	for _, recipient := range metadata.Participants {
		if recipient == d.Dename {
			continue
		}
		for _, msg := range messages {
			if err != nil {
				return err
			}
			if msgRatch, err := LoadRatchet(d, recipient, d.fillAuth, d.checkAuth); err != nil { //First message to this recipien
				if err := d.sendFirstMessage(msg, recipient); err != nil {
					return err
				}
			} else {
				if err := d.sendMessage(msg, recipient, msgRatch); err != nil {
					return err
				}
			}
		}
	}

	// move the sent messages to the conversation folder
	for _, finfo := range potentialMessages {
		if !finfo.IsDir() && finfo.Name() != persistence.MetadataFileName {
			if err = os.Rename(filepath.Join(dirname, finfo.Name()), filepath.Join(d.ConversationDir(), persistence.ConversationName(&metadata), persistence.MessageName(finfo.ModTime(), string(d.Dename)))); err != nil {
				log.Fatal(err)
			}
		}
	}

	// canonicalize the outbox folder name
	if dirname != filepath.Join(d.OutboxDir(), convName) {
		if err := os.Rename(dirname, filepath.Join(d.OutboxDir(), convName)); err != nil {
			shred.RemoveAll(dirname)
		}
	}

	return nil
}