Created
October 20, 2017 05:49
-
-
Save jdeng/a960e982a22c14d90df44a87f9eb0dd0 to your computer and use it in GitHub Desktop.
Save Outlook mail to MIME format using golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// +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