Skip to content

Instantly share code, notes, and snippets.

@jdeng
Created October 20, 2017 05:49
Show Gist options
  • Save jdeng/a960e982a22c14d90df44a87f9eb0dd0 to your computer and use it in GitHub Desktop.
Save jdeng/a960e982a22c14d90df44a87f9eb0dd0 to your computer and use it in GitHub Desktop.
Save Outlook mail to MIME format using golang
// +build windows
package main
import (
"fmt"
"time"
"syscall"
"unsafe"
ole "github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)
var (
CLSID_IConverterSession = ole.NewGUID("{4e3a7680-b77a-11d0-9da5-00c04fd65685}")
IID_IConverterSession = ole.NewGUID("{4b401570-b77b-11d0-9da5-00c04fd65685}")
IID_IMessage = ole.NewGUID("{00020307-0000-0000-C000-000000000046}")
IID_IStream = ole.NewGUID("{0000000C-0000-0000-C000-000000000046}")
modole32, _ = syscall.LoadDLL("ole32.dll")
pCreateStreamOnHGlobal, _ = modole32.FindProc("CreateStreamOnHGlobal")
pGetHGlobalFromStream, _ = modole32.FindProc("GetHGlobalFromStream")
pCoCreateInstance, _ = modole32.FindProc("CoCreateInstance")
modmapi32, _ = syscall.LoadDLL("mapi32.dll")
pMAPIInitialize, _ = modmapi32.FindProc("MAPIInitialize")
pMAPILogonEx, _ = modmapi32.FindProc("MAPILogonEx")
modkernel32, _ = syscall.LoadDLL("kernel32.dll")
pGlobalLock, _ = modkernel32.FindProc("GlobalLock")
pGlobalUnlock, _ = modkernel32.FindProc("GlobalUnlock")
)
// http://msdn2.microsoft.com/en-us/library/bb905202.aspx
type IConverterSession struct {
ole.IUnknown
}
type IConverterSessionVtbl struct {
ole.IUnknownVtbl
SetAdrBook uintptr
SetEncoding uintptr
PlaceHolder1 uintptr
MIMEToMAPI uintptr
MAPIToMIMEStm uintptr
PlaceHolder2 uintptr
PlaceHolder3 uintptr
PlaceHolder4 uintptr
SetTextWrapping uintptr
SetSaveFormat uintptr
PlaceHolder5 uintptr
SetCharset uintptr
}
func (v *IConverterSession) VTable() *IConverterSessionVtbl {
return (*IConverterSessionVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IConverterSession) SetEncoding(et uint32) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().SetEncoding,
2,
uintptr(unsafe.Pointer(v)),
uintptr(et), 0)
if hr != 0 {
err = ole.NewError(hr)
fmt.Println(err)
}
return
}
func (v *IConverterSession) SetSaveFormat(et uint32) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().SetSaveFormat,
2,
uintptr(unsafe.Pointer(v)),
uintptr(et), 0)
if hr != 0 {
err = ole.NewError(hr)
fmt.Println(err)
}
return
}
func (v *IConverterSession) SetTextWrapping(wrap bool, width uint32) (err error) {
var wrapIt uint32 = 0
if wrap {
wrapIt = 1
}
hr, _, _ := syscall.Syscall(
v.VTable().SetTextWrapping,
3,
uintptr(unsafe.Pointer(v)),
uintptr(wrapIt), uintptr(width))
if hr != 0 {
err = ole.NewError(hr)
fmt.Println(err)
}
return
}
func (v *IConverterSession) MAPIToMIMEStm(msg *ole.IDispatch, stm *IStream, flags uint32) (err error) {
hr, _, _ := syscall.Syscall6(
v.VTable().MAPIToMIMEStm,
4,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(msg)),
uintptr(unsafe.Pointer(stm)),
uintptr(flags), 0, 0)
if hr != 0 {
err = ole.NewError(hr)
fmt.Println(hr, "Error:", err)
}
return
}
func (v *IConverterSession) SetAdrBook(ab *ole.IUnknown) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().SetAdrBook,
2,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(ab)),
0)
if hr != 0 {
err = ole.NewError(hr)
fmt.Println(hr, "Error:", err)
}
return
}
type IStream struct {
ole.IUnknown
}
type IStreamVtbl struct {
ole.IUnknownVtbl
Read uintptr
Write uintptr
Seek uintptr
SetSize uintptr
CopyTo uintptr
Commit uintptr
Revert uintptr
LockRegion uintptr
UnlockRegion uintptr
Stat uintptr
Clone uintptr
}
func (v *IStream) VTable() *IStreamVtbl {
return (*IStreamVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IStream) Seek(move int32, origin uint32) (pos uint64, err error) {
hr, _, _ := syscall.Syscall6(
v.VTable().Seek,
5,
uintptr(unsafe.Pointer(v)),
0,
0,
uintptr(origin),
uintptr(unsafe.Pointer(&pos)), 0)
if hr != 0 {
err = ole.NewError(hr)
}
return
}
func (v *IStream) Write(buf []byte) (written uint64, err error) {
hr, _, _ := syscall.Syscall6(
v.VTable().Write,
4,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(&buf)),
uintptr(len(buf)),
uintptr(unsafe.Pointer(&written)), 0, 0)
if hr != 0 {
err = ole.NewError(hr)
}
return
}
type IMAPISession struct {
ole.IUnknown
}
type IMAPISessionVtbl struct {
ole.IUnknownVtbl
GetLastError uintptr
GetMsgStoresTable uintptr
OpenMsgStore uintptr
OpenAddressBook uintptr
OpenProfileSection uintptr
GetStatusTable uintptr
OpenEntry uintptr
CompareEntryIDs uintptr
Advise uintptr
Unadvise uintptr
MessageOptions uintptr
QueryDefaultMessageOpt uintptr
EnumAdrTypes uintptr
QueryIdentity uintptr
Logoff uintptr
SetDefaultStore uintptr
AdminServices uintptr
ShowForm uintptr
PrepareForm uintptr
}
func (v *IMAPISession) VTable() *IMAPISessionVtbl {
return (*IMAPISessionVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IMAPISession) OpenAddressBook() (ab *ole.IUnknown, err error) {
hr, _, _ := syscall.Syscall6(
v.VTable().OpenAddressBook,
5,
uintptr(unsafe.Pointer(v)),
0,
0,
0,
uintptr(unsafe.Pointer(&ab)), 0)
if hr != 0 {
err = ole.NewError(hr)
}
return
}
func CreateConverterSession() (unk *IConverterSession, err error) {
hr, _, _ := pCoCreateInstance.Call(uintptr(unsafe.Pointer(CLSID_IConverterSession)), 0, ole.CLSCTX_INPROC_SERVER,
uintptr(unsafe.Pointer(IID_IConverterSession)), uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = ole.NewError(hr)
}
return
}
func main() {
ole.CoInitialize(0)
pMAPIInitialize.Call(uintptr(0))
var sess *IMAPISession
var flags uint32 = 0x00000020 // | 0x00000002 |0x00000001 // MAPI_EXTENDED | MAPI_NO_MAIL
pMAPILogonEx.Call(uintptr(0), 0, 0, uintptr(flags), uintptr(unsafe.Pointer(&sess)))
var converter *IConverterSession
hr, _, _ := pCoCreateInstance.Call(uintptr(unsafe.Pointer(CLSID_IConverterSession)),
0,
ole.CLSCTX_INPROC_SERVER,
uintptr(unsafe.Pointer(IID_IConverterSession)),
uintptr(unsafe.Pointer(&converter)))
if hr != 0 {
fmt.Println(ole.NewError(hr))
return
}
const (
SAVE_RFC1521 = 1
IET_QP = 3
CCSF_SMTP = 2
)
converter.SetSaveFormat(SAVE_RFC1521)
converter.SetEncoding(IET_QP)
converter.SetTextWrapping(true, 74)
ab, err := sess.OpenAddressBook()
if err != nil {
fmt.Println(err)
return
}
converter.SetAdrBook(ab)
var stm *IStream
hr, _, _ = pCreateStreamOnHGlobal.Call(uintptr(0), uintptr(0), uintptr(unsafe.Pointer(&stm)))
if hr != 0 {
fmt.Println(ole.NewError(hr))
return
}
unknown, _ := oleutil.CreateObject("Outlook.Application")
outlook, _ := unknown.QueryInterface(ole.IID_IDispatch)
ns := oleutil.MustCallMethod(outlook, "GetNamespace", "MAPI").ToIDispatch()
folders := oleutil.MustGetProperty(ns, "Folders").ToIDispatch()
nfolders := oleutil.MustGetProperty(folders, "Count").Value().(int32)
for i := 1; i <= int(nfolders); i++ {
item, err := oleutil.GetProperty(folders, "Item", i)
if err != nil || item.VT != ole.VT_DISPATCH {
fmt.Println(err)
continue
}
if value, err := oleutil.GetProperty(item.ToIDispatch(), "Name"); err == nil {
fmt.Println(i, value.Value())
}
if value, err := oleutil.GetProperty(item.ToIDispatch(), "FolderPath"); err == nil {
fmt.Println(i, value.Value())
}
}
folder := oleutil.MustCallMethod(ns, "GetDefaultFolder", 6).ToIDispatch()
if folder != nil {
if value, err := oleutil.GetProperty(folder, "Name"); err == nil {
fmt.Println(value.Value())
}
if value, err := oleutil.GetProperty(folder, "FolderPath"); err == nil {
fmt.Println(value.Value())
}
}
contacts := oleutil.MustCallMethod(folder, "Items").ToIDispatch()
count := oleutil.MustGetProperty(contacts, "Count").Value().(int32)
fmt.Println(count, " items")
fmt.Println(time.Now())
for i := 100000; i <= int(count); i++ {
if i >= 100100{ break }
item, err := oleutil.GetProperty(contacts, "Item", i)
if err != nil || item.VT != ole.VT_DISPATCH {
fmt.Println(err)
continue
}
if value, err := oleutil.GetProperty(item.ToIDispatch(), "MessageClass"); err == nil {
// mclass := value.Value().(string)
// if mclass == "IPM.Note" { continue }
fmt.Println(i, value.Value())
}
if value, err := oleutil.GetProperty(item.ToIDispatch(), "Subject"); err == nil {
fmt.Println(i, value.Value())
}
value, err := oleutil.GetProperty(item.ToIDispatch(), "MAPIOBJECT")
if err != nil {
fmt.Println(err)
continue
}
mobj := value.Value().(*ole.IUnknown)
msg, err := mobj.QueryInterface(IID_IMessage)
if err != nil {
fmt.Println(err)
continue
}
err = converter.MAPIToMIMEStm(msg, stm, CCSF_SMTP)
if err != nil {
fmt.Println(err)
continue
}
size, err := stm.Seek(0, 1)
if err != nil || size <= 0 {
fmt.Println(err)
continue
}
fmt.Println("Size:", size)
var handle uintptr
hr, _, _ := pGetHGlobalFromStream.Call(uintptr(unsafe.Pointer(stm)), uintptr(unsafe.Pointer(&handle)))
if hr != 0 {
fmt.Println(ole.NewError(hr))
continue
}
/*
addr, _, _ := pGlobalLock.Call(handle)
if addr == 0 {
fmt.Println("Unable to GlobalLock")
continue
}
buf := (*[1 << 30]byte)(unsafe.Pointer(uintptr(addr)))[0:size]
fmt.Println(string(buf[0:1000]))
pGlobalUnlock.Call(handle)
*/
//reset
stm.Seek(0, 0)
}
// oleutil.MustCallMethod(outlook, "Quit")
fmt.Println(time.Now())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment