aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/mono/Camel.cs
blob: 4ad80e7012a121a121a4eb8fc13524aacfec0fe3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278

using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Camel {
    [StructLayout (LayoutKind.Sequential)]
    public struct CamelException {
        public int id;
        public string desc;
    }

    public class Arg {
        public enum Tag : uint {
            END = 0,
            IGNORE = 1,
            FIRST = 1024,

            TYPE = 0xf0000000, /* type field for tags */
            TAG = 0x0fffffff, /* tag field for args */

            OBJ = 0x00000000, /* object */
            INT = 0x10000000, /* int */
            DBL = 0x20000000, /* double */
            STR = 0x30000000, /* c string */
            PTR = 0x40000000, /* ptr */
            BOO = 0x50000000 /* bool */
        }
    }

    public class Exception : System.ApplicationException {
        public enum Type {
            NONE = 0,
            SYSTEM = 1
        }

        public Type id;
        public string desc;

        public Exception(CamelException ex) {
            id = (Type)ex.id;
            desc = ex.desc;
        }

        public Exception(Type _id, string _desc) {
            id = _id;
            desc = _desc;
        }
    }

    public class Util {
        [DllImport("camel-1.2")] static extern int camel_init(string certdir, bool nss);

        public static void Init(string certdir, bool nss) {
            if (camel_init(certdir, nss) != 0)
                throw new Exception(Exception.Type.SYSTEM, "Init failure");
        }

        public static string [] getUIDArray(IntPtr o) {
            GPtrArray pa = (GPtrArray)Marshal.PtrToStructure(o, typeof(GPtrArray));
            string [] uids = new string[pa.len];

            for (int i=0;i<pa.len;i++) {
                IntPtr x = Marshal.ReadIntPtr(pa.pdata, i * Marshal.SizeOf(typeof(IntPtr)));
                uids[i] = Marshal.PtrToStringAuto(x);
            }

            return uids;
        }
/*
        public static IntPtr setUIDs(string [] uids) {
            
        }
*/
        public struct UIDArray {
            public string [] uids;
            public int len;

            public UIDArray(string [] _uids) {
                uids = _uids;
                len = _uids.Length;
            }
            
            public UIDArray(IntPtr raw) {
                uids = new string[0];
                len = 0;
                Marshal.PtrToStructure(raw, this);
            }
        }
    }

    public class Object {
        // should be library scope
        public IntPtr cobject;
        private int finaliseID = -1;

        protected EventHandlerList events = new EventHandlerList();

        // reffing & wrapping stuff.
        struct CamelObject {
            public IntPtr klass;
        }

        struct CamelObjectClass {
            public IntPtr parent;
            int magic;
            IntPtr next;
            IntPtr prev;
            public string name;
        };

        private static Hashtable types = new Hashtable();
        private static Hashtable objects = new Hashtable();

        [DllImport("camel-1.2")] static extern void camel_object_ref(IntPtr raw);
        [DllImport("camel-1.2")] static extern void camel_object_unref(IntPtr raw);

        public Object(IntPtr raw) {
            // ok this is a hack around c# crap to do with unargumented constructors.
            // we can bypass to a null raw so we can properly instantiate new types
            if (raw != (IntPtr)0) {
                cobject = raw;
                toCamel(this);
            }
        }

        public Object() {
            // this is invalid?
        }

        ~Object() {
            System.Console.WriteLine("object disposed " + cobject + " type " + this);

            // well we can never get a finalised event anyway ...
            if (finalise_id != -1)
                camel_object_remove_event(cobject, finalise_id);
            if (meta_changed_id != -1)
                camel_object_remove_event(cobject, meta_changed_id);

            objects.Remove(cobject);
            camel_object_remove_event(cobject, finaliseID);
            finaliseID = -1;
            camel_object_unref(cobject);
            cobject = (IntPtr)0;

            // FIXME: remove any event hooks too
        }

        static Object() {
            types.Add("CamelObject", typeof(Camel.Object));
            types.Add("CamelSession", typeof(Camel.Session));
            types.Add("CamelFolder", typeof(Camel.Folder));
            types.Add("CamelDataWrapper", typeof(Camel.DataWrapper));
            types.Add("CamelMedium", typeof(Camel.Medium));
            types.Add("CamelMimeMessage", typeof(Camel.MimeMessage));
            types.Add("CamelMimePart", typeof(Camel.MimePart));
            types.Add("CamelMultipart", typeof(Camel.Multipart));

            types.Add("CamelStore", typeof(Camel.Store));
            types.Add("CamelTransport", typeof(Camel.Transport));
            types.Add("CamelAddress", typeof(Camel.Address));
            types.Add("CamelInternetAddress", typeof(Camel.InternetAddress));
            types.Add("CamelStream", typeof(Camel.Stream));
            types.Add("CamelStreamMem", typeof(Camel.StreamMem));
            types.Add("CamelStreamFs", typeof(Camel.StreamFS));
        }

        public static void objectFinalised(IntPtr o, IntPtr info, IntPtr data) {
            System.Console.WriteLine("object finalised " + o);
            objects.Remove(o);
        }

        public static Object fromCamel(IntPtr raw) {
            CamelObject o;
            CamelObjectClass klass;
            WeakReference weak = (WeakReference)objects[raw];

            System.Console.WriteLine("object from camel " + raw);

            if (weak != null)
                return (Object)weak.Target;

            o = (CamelObject)Marshal.PtrToStructure(raw, typeof(CamelObject));
            if ((object)o == null)
                return null;

            klass = (CamelObjectClass)Marshal.PtrToStructure(o.klass, typeof(CamelObjectClass));
            while ((object)klass != null) {
                Console.WriteLine(" checking is " + klass.name);
                if (types.ContainsKey(klass.name)) {
                    Console.WriteLine("  yep!");
                    camel_object_ref(raw);
                    return (Camel.Object)Activator.CreateInstance((Type)types[klass.name], new object [] { raw });
                }

                klass = (CamelObjectClass)Marshal.PtrToStructure(klass.parent, typeof(CamelObjectClass));
            }

            Console.WriteLine("  unknown type?");
            camel_object_unref(raw);
            return null;
        }

        /* this just registers an object created on the cil side */
        public static void toCamel(Object res) {
            System.Console.WriteLine("object to camel " + res.cobject);

            objects.Add(res.cobject, new WeakReference(res));
            res.finaliseID = camel_object_hook_event(res.cobject, "finalize", (CamelEventFunc)objectFinalised, (IntPtr)0);
        }

        // Camel event Wrapper and helpers
        public delegate void CamelEventFunc(IntPtr o, IntPtr info, IntPtr data);

        [DllImport("camel-1.2")] public static extern int camel_object_hook_event(IntPtr raw, string name, CamelEventFunc func, IntPtr data);
        [DllImport("camel-1.2")] public static extern void camel_object_remove_event(IntPtr raw, int id);

        protected void addEvent(String name, ref int hookid, CamelEventFunc hook, Delegate value) {
            if (hookid == -1)
                hookid = camel_object_hook_event(cobject, name, hook, (IntPtr)0);
            events.AddHandler(name, value);
        }

        protected void removeEvent(String name, ref int hookid, Delegate value) {
            events.RemoveHandler(name, value);
            if (events[name] == null) {
                camel_object_remove_event(cobject, hookid);
                hookid = -1;
            }
        }   

        // object events
        public delegate void FinaliseEvent(Camel.Object o);
        public delegate void MetaChangedEvent(Camel.Object o, String name);

        // how to remove these, at dispose time?
        private int finalise_id = -1;
        private int meta_changed_id = -1;

        private static void finaliseHook(IntPtr co, IntPtr info, IntPtr data) {
            Object o = fromCamel(co);
            FinaliseEvent f;

            if (o != null
                && (f = (FinaliseEvent)o.events["finalize"]) != null)
                f(o);
        }

        private static void metaChangedHook(IntPtr co, IntPtr info, IntPtr data) {
            Object o = fromCamel(co);
            MetaChangedEvent f;

            if (o != null
                && (f = (MetaChangedEvent)o.events["finalize"]) != null)
                f(o, Marshal.PtrToStringAnsi(info));
        }

        public event FinaliseEvent Finalise {
            add { addEvent("finalize", ref finalise_id, (CamelEventFunc)finaliseHook, value); }
            remove { removeEvent("finalize", ref finalise_id, value); }
        }

        public event MetaChangedEvent MetaChanged {
            add { addEvent("meta_changed", ref meta_changed_id, (CamelEventFunc)metaChangedHook, value); }
            remove { removeEvent("meta_changed", ref meta_changed_id, value); }
        }

        [DllImport("camel-1.2")] static extern IntPtr camel_object_get_ptr(IntPtr raw, ref CamelException ex, int tag);
        [DllImport("camel-1.2")] static extern void camel_object_free(IntPtr raw, int tag, IntPtr val);
        [DllImport("camel-1.2")] static extern int camel_object_get_int(IntPtr raw, ref CamelException ex, int tag);

        // maybe we want an indexer class to get properties?
        // e.g. name = folder.properties[Folder.Tag.NAME]
        public String getString(int type) {
            String s;
            IntPtr o;
            CamelException ex = new CamelException();

            o = camel_object_get_ptr(cobject, ref ex, type);
            if (ex.id != 0)
                throw new Camel.Exception(ex);

            s = Marshal.PtrToStringAuto(o);
            camel_object_free(cobject, type, o);

            return s;
        }

        public Camel.Object getObject(int type) {
            IntPtr o;
            Camel.Object co;
            CamelException ex = new CamelException();

            o = camel_object_get_ptr(cobject, ref ex, type);
            if (ex.id != 0)
                throw new Camel.Exception(ex);

            co = fromCamel(o);
            camel_object_free(cobject, type, o);

            return co;
        }

        public int getInt(int type) {
            int r;
            CamelException ex = new CamelException();

            r = camel_object_get_int(cobject, ref ex, type);
            if (ex.id != 0)
                throw new Camel.Exception(ex);

            return r;
        }

        // meta-data
        [DllImport("camel-1.2")] static extern String camel_object_meta_get(IntPtr raw, string name);
        [DllImport("camel-1.2")] static extern bool camel_object_meta_set(IntPtr raw, string name, string value);

        public String metaGet(String name) {
            return camel_object_meta_get(cobject, name);
        }

        public bool metaSet(String name, String value) {
            return camel_object_meta_set(cobject, name, value);
        }
    }

    public class Provider {
        public enum Type {
            STORE = 0,
            TRANSPORT = 1
        }
    }

    public class Session : Object {
        public Session(IntPtr raw) : base(raw) { }

        [DllImport("camel-provider-1.2")] static extern IntPtr camel_session_get_service(IntPtr o, string uri, int type, ref CamelException ex);
        [DllImport("camel-provider-1.2")] static extern IntPtr camel_session_get_service_connected(IntPtr o, string uri, int type, ref CamelException ex);

        public Service getService(string uri, Provider.Type type) {
            IntPtr s;
            CamelException ex = new CamelException();

            s = camel_session_get_service(cobject, uri, (int)type, ref ex);
            if (ex.id != 0)
                throw new Camel.Exception(ex);

            return (Service)fromCamel(s);
        }
    }

    public class Service : Object {
        public Service(IntPtr raw) : base(raw) { }
        // wrap service shit
    }

    public class Store : Service {
        public Store(IntPtr raw) : base(raw) { }

        [DllImport("camel-provider-1.2")]
                static extern IntPtr camel_store_get_folder(IntPtr o, string name, int flags, ref CamelException ex);

        Folder getFolder(string name, int flags) {
            IntPtr s;
            CamelException ex = new CamelException();

            s = camel_store_get_folder(cobject, name, flags, ref ex);
            if (ex.id != 0)
                throw new Camel.Exception(ex);

            return (Folder)fromCamel(s);
        }

        void createFolder(string name) {
        }
    }

    public class Transport : Service {
        public Transport(IntPtr raw) : base(raw) { }

        // send to (message, from, reciepients);
    }

    public class Folder : Camel.Object {
        public Folder(IntPtr raw) : base(raw) { }

        ~Folder() {
            if (changed_id != -1)
                camel_object_remove_event(cobject, changed_id);
        }

        public enum Tag {
            NAME = (int) (0x1400 + Arg.Tag.STR),
            FULL_NAME = (int) (0x1401 + Arg.Tag.STR),
            STORE = (int) (0x1402 + Arg.Tag.OBJ),
            PERMANENTFLAGS = (int) (0x1403 + Arg.Tag.INT),
            TOTAL = (int) (0x1404 + Arg.Tag.INT),
            UNREAD = (int) (0x1405 + Arg.Tag.INT),
            DELETED = (int) (0x1406 + Arg.Tag.INT),
            JUNKED = (int) (0x1407 + Arg.Tag.INT),
            VISIBLE = (int) (0x1408 + Arg.Tag.INT),
            UID_ARRAY = (int) (0x1409 + Arg.Tag.PTR),
            INFO_ARRAY = (int) (0x140a + Arg.Tag.PTR), // GPtrArray
            PROPERTIES = (int) (0x140b + Arg.Tag.PTR), // GSList of properties
        }

        [DllImport("camel-provider-1.2")] static extern IntPtr camel_folder_get_message(IntPtr o, string uid, ref CamelException ex);
        [DllImport("camel-provider-1.2")] static extern IntPtr camel_folder_get_uids(IntPtr o);
        [DllImport("camel-provider-1.2")] static extern void camel_folder_free_uids(IntPtr o, IntPtr uids);
        [DllImport("camel-provider-1.2")] static extern IntPtr camel_folder_search_by_expression(IntPtr o, string expr, ref CamelException ex);
        [DllImport("camel-provider-1.2")] static extern IntPtr camel_folder_search_by_uids(IntPtr o, string expr, ref Util.UIDArray uids, ref CamelException ex);
        [DllImport("camel-provider-1.2")] static extern void camel_folder_search_free(IntPtr o, IntPtr uids);

        [DllImport("camel-provider-1.2")] static extern IntPtr camel_folder_get_message_info(IntPtr raw, String uid);

        public MimeMessage getMessage(string uid) {
            CamelException ex = new CamelException();
            IntPtr o = camel_folder_get_message(cobject, uid, ref ex);

            if (ex.id != 0)
                throw new Camel.Exception(ex);

            return (MimeMessage)fromCamel(o);
        }

        public MessageInfo getMessageInfo(string uid) {
            IntPtr o = camel_folder_get_message_info(cobject, uid);

            if (o == (IntPtr)0)
                return null;
            else
                return new MessageInfo(o);
        }

        public string [] getUIDs() {
            IntPtr o = camel_folder_get_uids(cobject);
            Util.UIDArray uids = new Util.UIDArray(o);

            camel_folder_free_uids(cobject, o);

            return uids.uids;
        }

        public string [] search(string expr) {
            CamelException ex = new CamelException();
            IntPtr o = camel_folder_search_by_expression(cobject, expr, ref ex);
            Util.UIDArray uids;

            if (ex.id != 0)
                throw new Camel.Exception(ex);

            uids = new Util.UIDArray(o);
            camel_folder_search_free(cobject, o);

            return uids.uids;
        }

        public string [] searchUIDs(string expr, string [] sub) {
            CamelException ex = new CamelException();
            Util.UIDArray uids = new Util.UIDArray(sub);
            IntPtr o = camel_folder_search_by_uids(cobject, expr, ref uids, ref ex);

            if (ex.id != 0)
                throw new Camel.Exception(ex);

            uids = new Util.UIDArray(o);
            camel_folder_search_free(cobject, o);

            return uids.uids;
        }

        public String name {
            get { return getString((int)Folder.Tag.NAME); }
        }

        public String fullName {
            get { return getString((int)Folder.Tag.FULL_NAME); }
        }

        public Camel.Store store {
            get { return (Camel.Store)getObject((int)Folder.Tag.STORE); }
        }

        // Folder events
        public delegate void ChangedEvent(Camel.Folder f);

        private int changed_id = -1;

        private static void changedHook(IntPtr co, IntPtr info, IntPtr data) {
            Camel.Folder o = (Camel.Folder)fromCamel(co);
            ChangedEvent f;
            
            Console.WriteLine("changed hook called for: " + o.cobject);

            if (o != null
                && (f = (ChangedEvent)o.events["folder_changed"]) != null)
                f(o);
        }

        public event ChangedEvent Changed {
            add { addEvent("folder_changed", ref changed_id, (CamelEventFunc)changedHook, value); }
            remove { removeEvent("folder_changed", ref changed_id, value); }
        }
    }

    public class DataWrapper : Camel.Object {
        public DataWrapper(IntPtr raw) : base(raw) { }

        [DllImport("camel-1.2")] static extern int camel_data_wrapper_write_to_stream(IntPtr o, IntPtr s);
        [DllImport("camel-1.2")] static extern int camel_data_wrapper_decode_to_stream(IntPtr o, IntPtr s);
        [DllImport("camel-1.2")] static extern int camel_data_wrapper_construct_from_stream(IntPtr o, IntPtr s);
        [DllImport("camel-1.2")] static extern IntPtr camel_data_wrapper_get_mime_type_field(IntPtr o);

        public void writeToStream(Camel.Stream stream) {
            int res;

            res = camel_data_wrapper_write_to_stream(cobject, stream.cobject);
            if (res == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO Error");
        }

        public void decodeToStream(Camel.Stream stream) {
            int res;

            res = camel_data_wrapper_decode_to_stream(cobject, stream.cobject);
            if (res == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO Error");
        }

        public void constructFromStream(Camel.Stream stream) {
            int res;

            res = camel_data_wrapper_construct_from_stream(cobject, stream.cobject);
            if (res == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO Error");
        }

        public ContentType mimeType { get { return new ContentType(camel_data_wrapper_get_mime_type_field(cobject)); } }
    }

    public class Medium : Camel.DataWrapper {
        public Medium(IntPtr raw) : base(raw) { }

        [DllImport("camel-1.2")] static extern IntPtr camel_medium_get_content_object(IntPtr o);
        [DllImport("camel-1.2")] static extern void camel_medium_set_content_object(IntPtr o, IntPtr s);

        public DataWrapper content {
            get {
                IntPtr o = camel_medium_get_content_object(cobject);

                if (o != (IntPtr)0)
                    return (DataWrapper)Object.fromCamel(o);
                else
                    return null;
            }
            set {
                camel_medium_set_content_object(cobject, value.cobject);
            }
        }
    }

    public class Multipart : Camel.DataWrapper {
        [DllImport("camel-1.2")] static extern IntPtr camel_multipart_new();
        [DllImport("camel-1.2")] static extern void camel_multipart_add_part(IntPtr o, IntPtr p);
        [DllImport("camel-1.2")] static extern void camel_multipart_remove_part(IntPtr o, IntPtr p);
        [DllImport("camel-1.2")] static extern IntPtr camel_multipart_get_part(IntPtr o, int index);
        [DllImport("camel-1.2")] static extern int camel_multipart_get_number(IntPtr o);

        public Multipart(IntPtr raw) : base(raw) { }

        public void addPart(MimePart part) {
            camel_multipart_add_part(cobject, part.cobject);
        }

        public void removePart(MimePart part) {
            camel_multipart_add_part(cobject, part.cobject);
        }

        public MimePart getPart(int index) {
            IntPtr o;

            o = camel_multipart_get_part(cobject, index);
            if (o != (IntPtr)0)
                return (MimePart)Object.fromCamel(o);
            else
                return null;
        }

        public int getNumber() {
            return camel_multipart_get_number(cobject);
        }

        // FIXME: finish
    }

    public class MimePart : Camel.Medium {
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_part_new();
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_part_get_description(IntPtr o);
        [DllImport("camel-1.2")] static extern void camel_mime_part_set_description(IntPtr o, string s);
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_part_get_disposition(IntPtr o);
        [DllImport("camel-1.2")] static extern void camel_mime_part_set_disposition(IntPtr o, string s);
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_part_get_filename(IntPtr o);
        [DllImport("camel-1.2")] static extern void camel_mime_part_set_filename(IntPtr o, string s);

        public MimePart(IntPtr raw) : base(raw) { }

        public string description {
            get { return Marshal.PtrToStringAuto(camel_mime_part_get_description(cobject)); }
            set { camel_mime_part_set_description(cobject, value); }
        }

        public string disposition {
            get { return Marshal.PtrToStringAuto(camel_mime_part_get_disposition(cobject)); }
            set { camel_mime_part_set_disposition(cobject, value); }
        }

        public string filename {
            get { return Marshal.PtrToStringAuto(camel_mime_part_get_filename(cobject)); }
            set { camel_mime_part_set_filename(cobject, value); }
        }

        // FIXME: finish
    }

    public class MimeMessage : Camel.MimePart {
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_message_new();
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_message_get_subject(IntPtr o);
        [DllImport("camel-1.2")] static extern void camel_mime_message_set_subject(IntPtr o, string s);
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_message_get_from(IntPtr o);
        [DllImport("camel-1.2")] static extern void camel_mime_message_set_from(IntPtr o, IntPtr s);
        [DllImport("camel-1.2")] static extern IntPtr camel_mime_message_get_recipients(IntPtr o, string type);
        [DllImport("camel-1.2")] static extern void camel_mime_message_set_recipients(IntPtr o, string type, IntPtr s);

        public MimeMessage(IntPtr raw) : base(raw) { }

        /* We need to use factories to create new objects otherwise the parent will instantiate an instance
           of itself instead during the constructor setup */
        public MimeMessage() : base((IntPtr)0) {
            cobject = camel_mime_message_new();
            toCamel(this);
        }

        public string subject {
            get { return Marshal.PtrToStringAuto(camel_mime_message_get_subject(cobject)); }
            set { camel_mime_message_set_subject(cobject, value); }
        }

        public InternetAddress from {
            get { return new InternetAddress(camel_mime_message_get_from(cobject)); }
            set { camel_mime_message_set_from(cobject, value.cobject); }
        }
        
        public InternetAddress to {
            get { return new InternetAddress(camel_mime_message_get_recipients(cobject, "to")); }
            set { camel_mime_message_set_recipients(cobject, "to", value.cobject); }
        }

        public InternetAddress cc {
            get { return new InternetAddress(camel_mime_message_get_recipients(cobject, "cc")); }
            set { camel_mime_message_set_recipients(cobject, "cc", value.cobject); }
        }

        public InternetAddress bcc {
            get { return new InternetAddress(camel_mime_message_get_recipients(cobject, "bcc")); }
            set { camel_mime_message_set_recipients(cobject, "bcc", value.cobject); }
        }

        public InternetAddress resentTO {
            get { return new InternetAddress(camel_mime_message_get_recipients(cobject, "resent-to")); }
            set { camel_mime_message_set_recipients(cobject, "resent-to", value.cobject); }
        }

        public InternetAddress resentCC {
            get { return new InternetAddress(camel_mime_message_get_recipients(cobject, "resent-cc")); }
            set { camel_mime_message_set_recipients(cobject, "resent-cc", value.cobject); }
        }

        public InternetAddress resentBCC {
            get { return new InternetAddress(camel_mime_message_get_recipients(cobject, "resent-bcc")); }
            set { camel_mime_message_set_recipients(cobject, "resent-bcc", value.cobject); }
        }
    }

    // subclass real streams?  or real stream interfaces?
    public class Stream : Camel.Object {
        public Stream(IntPtr raw) : base(raw) { }

        [DllImport("camel-1.2")] static extern int camel_stream_write(IntPtr o, byte [] data, int len);
        [DllImport("camel-1.2")] static extern int camel_stream_read(IntPtr o, byte [] data, int len);
        [DllImport("camel-1.2")] static extern int camel_stream_eos(IntPtr o);
        [DllImport("camel-1.2")] static extern int camel_stream_close(IntPtr o);
        [DllImport("camel-1.2")] static extern int camel_stream_flush(IntPtr o);
        [DllImport("camel-1.2")] static extern int camel_stream_reset(IntPtr o);

        public int write(byte [] data, int len) {
            int ret;

            ret = camel_stream_write(cobject, data, len);
            if (ret == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO write Error");

            return ret;
        }

        public int write(string value) {
            int ret;
            byte [] data;
            System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();

            data = enc.GetBytes(value);
            ret = camel_stream_write(cobject, data, data.Length);
            if (ret == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO write Error");

            return ret;
        }


        public int read(byte [] data, int len) {
            int ret;

            ret = camel_stream_read(cobject, data, len);
            if (ret == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO read Error");

            return ret;
        }

        public void close() {
            if (camel_stream_close(cobject) == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO close Error");
        }

        public void reset() {
            if (camel_stream_reset(cobject) == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO reset Error");
        }

        public void flush() {
            if (camel_stream_flush(cobject) == -1)
                throw new Exception(Exception.Type.SYSTEM, "IO close Error");
        }

        public bool eos() {
            return (camel_stream_eos(cobject) != 0);
        }
    }

    public class SeekableStream : Camel.Stream {
        public SeekableStream(IntPtr raw) : base(raw) { }
    }

    public class StreamFS : Camel.SeekableStream {
        public enum Flags {
            O_RDONLY = 00,
            O_WRONLY = 01,
            O_RDWR   = 02,
            O_CREAT  = 0100,
            O_EXCL   = 0200,
            O_TRUNC  = 01000,
            O_APPEND = 02000
        }

        public static int STDIN_FILENO = 0;
        public static int STDOUT_FILENO = 1;
        public static int STDERR_FILENO = 2;

        public StreamFS(IntPtr raw) : base(raw) { }

        [DllImport("camel-1.2")] static extern IntPtr camel_stream_fs_new_with_name(string name, int flags, int mode);
        [DllImport("camel-1.2")] static extern IntPtr camel_stream_fs_new_with_fd(int fd);

        public StreamFS(string name, Flags flags, int mode) : base((IntPtr)0) {
            cobject = camel_stream_fs_new_with_name(name, (int)flags, mode);
            toCamel(this);
        }

        public StreamFS(int fd) : base((IntPtr)0) {
            cobject = camel_stream_fs_new_with_fd(fd);
            toCamel(this);
        }
    }

    // this should obviously be extracted at build time
    [StructLayout (LayoutKind.Explicit)]
    struct CamelStreamMem {
        [FieldOffset(44)] public IntPtr buffer;
    }

    struct GByteArray {
        public IntPtr data;
        public int len;
    }

    struct GPtrArray {
        public IntPtr pdata;
        public int len;
    }

    public class StreamMem : Camel.SeekableStream {
        public StreamMem(IntPtr raw) : base(raw) { }

        [DllImport("camel-1.2")]
                static extern IntPtr camel_stream_mem_new();

        /* stupid c# */
        public StreamMem() : base((IntPtr)0) {
            cobject = camel_stream_mem_new();
            toCamel(this);
        }

        // should probably have some sort of interface for incremental/range gets too
        public Byte[] getBuffer() {
            CamelStreamMem mem = (CamelStreamMem)Marshal.PtrToStructure(cobject, typeof(CamelStreamMem));
            GByteArray ba = (GByteArray)Marshal.PtrToStructure(mem.buffer, typeof(GByteArray));
            Byte[] res = new Byte[ba.len];

            Marshal.Copy(ba.data, res, 0, ba.len);

            return res;
        }
    }

    // should do iterators etc?
    public class Address : Camel.Object {
        public Address(IntPtr raw) : base (raw) { }

        [DllImport("camel-1.2")] static extern IntPtr camel_address_new();
        [DllImport("camel-1.2")] static extern int camel_address_length(IntPtr raw);
        [DllImport("camel-1.2")] static extern int camel_address_decode(IntPtr raw, string addr);
        [DllImport("camel-1.2")] static extern string camel_address_encode(IntPtr raw);
        [DllImport("camel-1.2")] static extern int camel_address_unformat(IntPtr raw, string addr);
        [DllImport("camel-1.2")] static extern string camel_address_format(IntPtr raw);
        [DllImport("camel-1.2")] static extern int camel_address_cat(IntPtr raw, IntPtr src);
        [DllImport("camel-1.2")] static extern int camel_address_copy(IntPtr raw, IntPtr src);
        [DllImport("camel-1.2")] static extern void camel_address_remove(IntPtr raw, int index);

        public Address() : base((IntPtr)0) {
            cobject = camel_address_new();
            toCamel(this);
        }

        public int length() {
            return camel_address_length(cobject);
        }

        public void decode(string addr) {
            if (camel_address_decode(cobject, addr) == -1)
                throw new Exception(Exception.Type.SYSTEM, "Invalid address: " + addr);
        }

        public string encode() {
            return camel_address_encode(cobject);
        }

        public void unformat(string addr) {
            if (camel_address_unformat(cobject, addr) == -1)
                throw new Exception(Exception.Type.SYSTEM, "Invalid address: " + addr);
        }

        public string format() {
            return camel_address_format(cobject);
        }

        public void cat(Address from) {
            camel_address_cat(cobject, from.cobject);
        }

        public void copy(Address from) {
            camel_address_copy(cobject, from.cobject);
        }
    }

    public class InternetAddress : Camel.Address {
        public InternetAddress(IntPtr raw) : base (raw) { }
        
        [DllImport("camel-1.2")] static extern IntPtr camel_internet_address_new();
        [DllImport("camel-1.2")] static extern int camel_internet_address_add(IntPtr raw, string name, string addr);
        [DllImport("camel-1.2")] static extern bool camel_internet_address_get(IntPtr raw, out string name, out string addr);
        [DllImport("camel-1.2")] static extern int camel_internet_address_find_name(IntPtr raw, string name, out string addr);
        [DllImport("camel-1.2")] static extern int camel_internet_address_find_address(IntPtr raw, string addr, out string name);
        [DllImport("camel-1.2")] static extern string camel_internet_address_encode_address(out int len, string name, string addr);
        [DllImport("camel-1.2")] static extern string camel_internet_address_format_address(string name, string addr);
        
        public InternetAddress() : base((IntPtr)0) {
            cobject = camel_internet_address_new();
            toCamel(this);
        }

        public void add(string name, string addr) {
            camel_internet_address_add(cobject, name, addr);
        }

        public bool get(out string name, out string addr) {
            name = null;
            addr = null;
            return camel_internet_address_get(cobject, out name, out addr);
        }

        // this is a weird arsed interface ...
        public int findName(string name, out string addr) {
            addr = null;
            // FIXME: addr is const, need to marshal to local
            return camel_internet_address_find_name(cobject, name, out addr);
        }

        public int findAddress(string addr, out string name) {
            name = null;
            return camel_internet_address_find_name(cobject, addr, out name);
        }

        public static string encode(string name, string addr) {
            int len = 0;
            // another weird-arsed interface
            return camel_internet_address_encode_address(out len, name, addr);
        }

        public static string format(string name, string addr) {
            return camel_internet_address_format_address(name, addr);
        }
    }

    public class ContentType {
        public IntPtr cobject;

        public ContentType(IntPtr raw) {
            cobject = raw;
        }

        [DllImport("camel-1.2")] static extern bool camel_content_type_is(IntPtr raw, string type, string subtype);

        ~ContentType() {
        }

        public bool isType(string type, string subtype) {
            return camel_content_type_is(cobject, type, subtype);
        }
    }

    public class MessageInfo {
        public IntPtr cobject;
        private Tags user_tags;
        private Flags user_flags;

        private enum Type {
            SUBJECT,
            FROM,
            TO,
            CC,
            MLIST,

            FLAGS,
            SIZE,

            DATE_SENT,
            DATE_RECEIVED,

            MESSAGE_ID,
            REFERENCES,

            USER_FLAGS,
            USER_TAGS,

            LAST,
        }

        public class Tags {
            private MessageInfo mi;

            [DllImport("camel-provider-1.2")] static extern IntPtr camel_message_info_user_tag(IntPtr mi, String name);
            [DllImport("camel-provider-1.2")] static extern bool camel_message_info_set_user_tag(IntPtr mi, String name, String value);

            public Tags(MessageInfo raw) {
                mi = raw;
            }

            public String this [String tag] {
                get {
                    return Marshal.PtrToStringAnsi(camel_message_info_user_tag(mi.cobject, tag));
                }
                set {
                    camel_message_info_set_user_tag(mi.cobject, tag, value);
                }
            }
        }

        public class Flags {
            private MessageInfo mi;

            [DllImport("camel-provider-1.2")] static extern bool camel_message_info_user_flag(IntPtr miptr, String name);
            [DllImport("camel-provider-1.2")] static extern bool camel_message_info_set_user_flag(IntPtr miptr, String name, bool value);

            // note raw is a pointer to a pointer of tags
            public Flags(MessageInfo raw) {
                mi = raw;
            }

            public bool this [String tag] {
                get {
                    return camel_message_info_user_flag(mi.cobject, tag);
                }
                set {
                    camel_message_info_set_user_flag(mi.cobject, tag, value);
                }
            }
        }

        // only used to calculate offsets
        private struct CamelMessageInfo {
            IntPtr summary;
            uint refcount;
            string uid;
        };

        public MessageInfo(IntPtr raw) {
            cobject = raw;
        }

        [DllImport("camel-provider-1.2")] static extern void camel_folder_free_message_info(IntPtr raw, IntPtr info);
        [DllImport("camel-provider-1.2")] static extern void camel_message_info_free(IntPtr info);

        ~MessageInfo() {
            camel_message_info_free(cobject);
        }

        [DllImport("camel-provider-1.2")] static extern IntPtr camel_message_info_ptr(IntPtr raw, int type);
        [DllImport("camel-provider-1.2")] static extern uint camel_message_info_uint32(IntPtr raw, int type);
        [DllImport("camel-provider-1.2")] static extern uint camel_message_info_time(IntPtr raw, int type);

        public String uid { get { return Marshal.PtrToStringAuto(Marshal.ReadIntPtr(cobject, (int)Marshal.OffsetOf(typeof(CamelMessageInfo), "uid"))); } }

        public String subject { get { return Marshal.PtrToStringAnsi(camel_message_info_ptr(cobject, (int)Type.SUBJECT)); } }
        public String from { get { return Marshal.PtrToStringAnsi(camel_message_info_ptr(cobject, (int)Type.FROM)); } }
        public String to { get { return Marshal.PtrToStringAnsi(camel_message_info_ptr(cobject, (int)Type.TO)); } }
        public String cc { get { return Marshal.PtrToStringAnsi(camel_message_info_ptr(cobject, (int)Type.CC)); } }
        public String mlist { get { return Marshal.PtrToStringAnsi(camel_message_info_ptr(cobject, (int)Type.MLIST)); } }

        public uint flags { get { return camel_message_info_uint32(cobject, (int)Type.FLAGS); } }
        public uint size { get { return camel_message_info_uint32(cobject, (int)Type.SIZE); } }

        public Tags userTags {
            get {
                if (user_tags == null)
                    user_tags = new Tags(this);
                return user_tags;
            }
        }

        public Flags userFlags {
            get {
                if (user_flags == null)
                    user_flags = new Flags(this);
                return user_flags;
            }
        }
    }

    public class URL {
        public IntPtr cobject;
        internal Params param_list;

        // we never instantiate this, we just use it to describe the layout
        internal struct CamelURL {
            internal IntPtr protocol;
            internal IntPtr user;
            internal IntPtr authmech;
            internal IntPtr passwd;
            internal IntPtr host;
            internal int    port;
            internal IntPtr path;
            internal IntPtr pparams;
            internal IntPtr query;
            internal IntPtr fragment;
        };

        public class Params {
            private URL parent;

            internal Params(URL _parent) {
                parent = _parent;
            }

            public string this[string name] {
                set { camel_url_set_param(parent.cobject, name, value); }
                get { return Marshal.PtrToStringAnsi(camel_url_get_param(parent.cobject, name)); }
            }
        }

        [DllImport("camel-1.2")] static extern IntPtr camel_url_new_with_base(IntPtr bbase, string url);
        [DllImport("camel-1.2")] static extern IntPtr camel_url_new(string url, ref CamelException ex);
        [DllImport("camel-1.2")] static extern string camel_url_to_string(IntPtr url, int flags);
        [DllImport("camel-1.2")] static extern void camel_url_free(IntPtr url);

        // this is a shit to wrap, needs accessors or other pain
        [DllImport("camel-1.2")] static extern void camel_url_set_protocol(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_user(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_authmech(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_passwd(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_host(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_port(IntPtr url, int p);
        [DllImport("camel-1.2")] static extern void camel_url_set_path(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_param(IntPtr url, string s, string v);
        [DllImport("camel-1.2")] static extern void camel_url_set_query(IntPtr url, string s);
        [DllImport("camel-1.2")] static extern void camel_url_set_fragment(IntPtr url, string s);

        [DllImport("camel-1.2")] static extern IntPtr camel_url_get_param(IntPtr url, string s);

        [DllImport("camel-1.2")] static extern string camel_url_encode(string url, string escape);
        // ugh we can't do this, it writes to its result??
        // -> use StringBuilder
        [DllImport("camel-1.2")] static extern IntPtr camel_url_decode(ref string url);

        public URL(string uri) {
            CamelException ex = new CamelException();

            cobject = camel_url_new(uri, ref ex);
            if (ex.id != 0)
                throw new Exception(ex);
        }

        public URL(URL bbase, string uri) {
            cobject = camel_url_new_with_base(bbase.cobject, uri);
        }

        ~URL() {
            camel_url_free(cobject);
        }

        /* its ugly but it works */
        private string field(string name) {
            return Marshal.PtrToStringAuto(Marshal.ReadIntPtr(cobject, (int)Marshal.OffsetOf(typeof(CamelURL), name)));
        }

        public string protocol {
            set { camel_url_set_protocol(cobject, value); }
            get { return field("protocol"); }
        }

        public string user {
            set { camel_url_set_user(cobject, value); }
            get { return field("user"); }
        }

        public string authmech {
            set { camel_url_set_authmech(cobject, value); }
            get { return field("authmech"); }
        }

        public string passwd {
            set { camel_url_set_passwd(cobject, value); }
            get { return field("passwd"); }
        }

        public string host {
            set { camel_url_set_host(cobject, value); }
            get { return field("host"); }
        }

        public int port {
            set { camel_url_set_port(cobject, value); }
            get { return (int)Marshal.ReadIntPtr(cobject, (int)Marshal.OffsetOf(typeof(CamelURL), "port")); }
        }

        public string path {
            set { camel_url_set_path(cobject, value); }
            get { return field("path"); }
        }

        public string query {
            set { camel_url_set_query(cobject, value); }
            get { return field("query"); }
        }

        public string fragment {
            set { camel_url_set_fragment(cobject, value); }
            get { return field("fragment"); }
        }

        public Params paramlist {
            get {
                if (param_list == null)
                    param_list = new Params(this);
                return param_list;
            }
        }

        public override string ToString() {
            return camel_url_to_string(cobject, 0);
        }

        public static string encode(string val) {
            return camel_url_encode(val, null);
        }

        public static string encode(string val, string escape) {
            return camel_url_encode(val, escape);
        }
    }
}

namespace Camel.Hash {
    public class Stream : System.IO.Stream {
        protected Camel.Stream substream;

        public Stream(Camel.Stream sub) {
            substream = sub;
        }

        public override bool CanSeek { get { return false; } }
        public override bool CanRead { get { return true; } }
        public override bool CanWrite { get { return true; } }
        public override long Length {
            get {
                throw new System.IO.IOException("Cannot get stream length");
            }
        }
        public override long Position {
            get {
                throw new System.IO.IOException("Cannot get stream position");
            }
            set {
                if (value == 0) {
                    substream.reset();
                } else {
                    throw new System.IO.IOException("Cannot set stream position");
                }
            }
        }
    
        public override int Read(byte[] buffer, int offset, int count) {
            // FIXME: how to add the offset to the buffer?
            return substream.read(buffer, count);
        }

        public override void Write(byte[] buffer, int offset, int count) {
            // FIXME: how to add the offset to the buffer?
            substream.write(buffer, count);
        }

        public override void Flush() {
            substream.flush();
        }

        public override long Seek(long offset, System.IO.SeekOrigin seek) {
            throw new System.IO.IOException("Seeking not supported");
        }

        public override void SetLength(long len) {
            throw new System.IO.IOException("Cannot set stream length");
        }
    }
}

/*
namespace Evolution.Mail {
    class Component : GLib.Object {
        public Component(IntPtr raw) : base(raw) {}
        public Component() : base() {}

        ~Component() {
            Dispose();
        }

        [DllImport("libevolution-mail.so")] static extern IntPtr mail_component_peek();
        [DllImport("libevolution-mail.so")] static extern IntPtr mail_component_peek_base_directory(IntPtr component);
        [DllImport("libevolution-mail.so")] static extern IntPtr mail_component_peek();

        public static Component peek() {
            return new Component(mail_component_peek());
        }

        public String baseDirectory {
            get {}
        }
}
*/