Both saving and copying messages begins by calling mailbox_save_alloc(). After that you can set message's metadata fields:
mailbox_save_set_flags() sets flags and keywords.
mailbox_save_set_received_date() sets message's received date (IMAP INTERNALDATE). It also supports specifying timezone, but most backends don't support saving it.
mailbox_save_set_dest_mail() specifies a mail that can be used to access the partially saved mail after save/copy is finished (but not committed). You should be able to do pretty much anything with the mail, but its UID is usually 0 at this point.
mailbox_save_set_from_envelope() sets the envelope sender. Currently this is only used by mbox format to specify the address in From_-line.
When copying, most of the metadata fields are automatically copied from the source message. The only exception is message's flags and keywords. If you want to preserve them, the easiest way is to call mailbox_save_copy_flags().
Some metadata fields are mainly useful when you're replicating or restoring an existing mailbox and want to preserve metadata:
mailbox_save_set_min_modseq() sets message's modseq to at least the specified modseq. If the modseqs are already higher in the mailbox, the resulting modseq is what it would have been without this call.
mailbox_save_set_uid() sets message's UID. If mailbox's next_uid is already higher than the specified UID, the UID request is ignored.
mailbox_save_set_guid() sets message's globally unique ID. A new GUID is generated by default, and if there already exists a message with the same GUID a different one may or may not be given. For example with maildir the GUID is same as the base filename, while dbox has an explicit GUID metadata field.
mailbox_save_set_pop3_uidl() sets POP3 UIDL value. Not all backends support this.
mailbox_save_set_save_date() sets "message saved" date, i.e. the date/time when the message was saved to this mailbox. The default is the current time.
Once you're done with setting the metadata fields, you can either copy an existing mail with mailbox_copy() or provide message body with:
mailbox_save_begin() starts saving from given input stream.
mailbox_save_continue() saves all data from input stream. If input stream is blocking, typically a single call to this function should be enough. If input stream is non-blocking, you need to call this function until you're done. In any case call this until i_stream_is_eof() returns TRUE.
mailbox_save_finish() finishes saving the mail, or mailbox_save_cancel() aborts it.