diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,9 +1,29 @@
syntax: glob
+.vs
+*.pdb
+*.obj
+*.user
+*.VC.db
+*.asm
+
Linux/Aspell/data
Linux/Aspell/dict
Linux/Debug
Linux/Release
-*.asm
Linux/*.idx
Linux/ScribeOptions_*.bak
+
+src\Resource.res
+
+x64ReleaseNoOptimize*
+x64Release*
+x64Debug*
+
+test\Html\*.html
+test\Html\*.jpg
+test\Html\*.png
+test\Html\*.gif
+
+Help\scripting\*.html
+Help\scripting\*.css
\ No newline at end of file
diff --git a/resources/Scribe.lr8 b/resources/Scribe.lr8
--- a/resources/Scribe.lr8
+++ b/resources/Scribe.lr8
@@ -1,5230 +1,5231 @@
-
+
-
+
+
diff --git a/resources/resdefs.h b/resources/resdefs.h
--- a/resources/resdefs.h
+++ b/resources/resdefs.h
@@ -1,1284 +1,1285 @@
// Generated by LgiRes
// This file generated by LgiRes
#define L_FUI_NEW -912
#define L_FUI_LEGEND -911
#define L_FUI_NOT -910
#define L_FUI_OPTIONS -909
#define L_FUI_CONFIGURE -908
#define L_FUI_MOVE_DOWN -907
#define L_FUI_MOVE_UP -906
#define L_FUI_DELETE -905
#define L_FUI_OR -904
#define L_FUI_AND -903
#define L_FUI_NEW_OR -902
#define L_FUI_NEW_AND -901
#define L_FUI_NEW_CONDITION -900
#define L_STORE_RESTART -803
#define L_STORE_MISMATCH -802
#define L_STORE_OS_ERR -801
#define L_STORE_WRITE_ERR -800
#define L_TOOLBAR_SHOW_TEXT -700
#define L_FR_SELECTION_ONLY -608
#define L_FR_REPLACE_WITH -607
#define L_FR_REPLACE_ALL -606
#define L_FR_REPLACE -605
#define L_FR_MATCH_CASE -604
#define L_FR_MATCH_WORD -603
#define L_FR_FIND_NEXT -602
#define L_FR_FIND_WHAT -601
#define L_FR_FIND -600
#define L_COLOUR_NONE -550
#define L_CHANGE_CHARSET -505
#define L_VIEW_IMAGES -504
#define L_VIEW_IN_DEFAULT_BROWSER -503
#define L_COPY_SOURCE -502
#define L_VIEW_SOURCE -501
#define L_COPY_LINK_LOCATION -500
#define L_FONTUI_UNDERLINE -407
#define L_FONTUI_TITLE -406
#define L_FONTUI_STYLE -405
#define L_FONTUI_PTSIZE -404
#define L_FONTUI_PREVIEW -403
#define L_FONTUI_ITALIC -402
#define L_FONTUI_FACE -401
#define L_FONTUI_BOLD -400
#define L_TEXTCTRL_TAB_SIZE -214
#define L_TEXTCTRL_INDENT_SIZE -213
#define L_TEXTCTRL_HARD_TABS -212
#define L_TEXTCTRL_SHOW_WHITESPACE -211
#define L_TEXTCTRL_UNDO -210
#define L_TEXTCTRL_REDO -209
#define L_TEXTCTRL_PASTE -208
#define L_TEXTCTRL_OPENURL -207
#define L_TEXTCTRL_GOTO_LINE -206
#define L_TEXTCTRL_FIXED -205
#define L_TEXTCTRL_EMAIL_TO -204
#define L_TEXTCTRL_CUT -203
#define L_TEXTCTRL_COPYLINK -202
#define L_TEXTCTRL_COPY -201
#define L_TEXTCTRL_AUTO_INDENT -200
#define IDS_ERROR_ESMTP_UNSUPPORTED_AUTHS -101
#define IDS_ERROR_ESMTP_NO_AUTHS -100
#define L_BTN_CANCEL -51
#define L_BTN_OK -50
#define FIELD_FLAGS 1
#define FIELD_TO 2
#define FIELD_CC 3
#define FIELD_FROM 4
#define FIELD_REPLY 5
#define FIELD_SUBJECT 6
#define FIELD_TEXT 7
#define FIELD_MESSAGE_ID 8
#define FIELD_DATE_RECEIVED 9
#define FIELD_INTERNET_HEADER 10
#define FIELD_FIRST_NAME 11
#define FIELD_LAST_NAME 12
#define FIELD_EMAIL 13
#define FIELD_HOME_STREET 14
#define FIELD_HOME_SUBURB 15
#define FIELD_HOME_POSTCODE 16
#define FIELD_HOME_STATE 17
#define FIELD_HOME_COUNTRY 18
#define FIELD_WORK_PHONE 19
#define FIELD_HOME_PHONE 20
#define FIELD_HOME_MOBILE 21
#define FIELD_HOME_IM 22
#define FIELD_HOME_FAX 23
#define FIELD_HOME_WEBPAGE 24
#define FIELD_NICK 25
#define FIELD_SPOUSE 26
#define FIELD_NOTE 27
#define FIELD_PLUGIN_ASSOC 28
#define FIELD_SIZE 29
#define FIELD_DATE_SENT 30
#define FIELD_COLUMN 31
#define FIELD_BCC 32
#define FIELD_MIME_TYPE 33
#define FIELD_PRIORITY 34
#define FIELD_FOLDER_OPEN 35
#define FIELD_CODE_PAGE 36
#define FIELD_MARK_COLOUR 37
#define FIELD_ALTERNATE_HTML 38
#define FIELD_CONTENT_ID 39
#define FIELD_FILTER_NAME 40
#define FIELD_CONDITION 41
#define FIELD_ACTION 42
#define FIELD_COND_FIELD 43
#define FIELD_COND_OPERATOR 44
#define FIELD_COND_VALUE 45
#define FIELD_ACT_TYPE 46
#define FIELD_ACT_ARG 47
#define FIELD_DIGEST_INDEX 48
#define FIELD_COMBINE_OP 49
#define FIELD_FILTER_INDEX 50
#define FIELD_FILTER_INCOMING 55
#define FIELD_FILTER_OUTGOING 56
#define FIELD_FILTER_INTERNAL 59
#define FIELD_CAL_SUBJECT 62
#define FIELD_CAL_LOCATION 63
#define FIELD_CAL_REMINDER_TIME 64
#define FIELD_CAL_REMINDER_ACTION_dep 65
#define FIELD_CAL_REMINDER_ARG_dep 66
#define FIELD_CAL_SHOW_TIME_AS 67
#define FIELD_CAL_RECUR_FREQ 68
#define FIELD_CAL_RECUR_INTERVAL 69
#define FIELD_CAL_RECUR_FILTER_DAYS 70
#define FIELD_CAL_RECUR_FILTER_MONTHS 71
#define FIELD_CAL_RECUR_FILTER_YEARS 72
#define FIELD_CAL_NOTES 73
#define FIELD_CAL_START_UTC 76
#define FIELD_CAL_END_UTC 77
#define FIELD_CAL_RECUR_FILTER_POS 78
#define FIELD_CAL_RECUR_END_DATE 79
#define FIELD_CAL_RECUR_END_COUNT 80
#define FIELD_CAL_RECUR_END_TYPE 81
#define FIELD_CAL_RECUR 82
#define IDC_COLOUR 83
#define FIELD_ATTENDEE_NAME 85
#define FIELD_ATTENDEE_EMAIL 86
#define FIELD_ATTENDEE_ATTENDENCE 87
#define FIELD_ATTENDEE_NOTE 88
#define FIELD_ATTENDEE_RESPONSE 89
#define FIELD_WORK_STREET 90
#define FIELD_WORK_SUBURB 91
#define FIELD_WORK_POSTCODE 92
#define FIELD_WORK_STATE 93
#define FIELD_WORK_COUNTRY 94
#define FIELD_WORK_MOBILE 95
#define FIELD_WORK_IM 96
#define FIELD_WORK_FAX 97
#define FIELD_WORK_WEBPAGE 98
#define FIELD_COMPANY 99
#define FIELD_LABEL 110
#define IDM_SAVE 113
#define FIELD_TITLE 114
#define FIELD_SERVER_UID 117
#define FIELD_GROUP_NAME 120
#define FIELD_GROUP_LIST 123
#define FIELD_FROM_CONTACT_NAME 126
#define IDS_ENCRYPTION_SUPPORT 150
#define FIELD_DATE_MODIFIED 162
#define FIELD_IMAP_SEQ 167
#define FIELD_RECEIVED_DOMAIN 170
#define IDD_FILTER_ITEMS 175
#define IDC_SIGNATURE_TAB 184
#define IDC_RESET_REPLY 186
#define IDC_SIG 187
#define IDC_RESET_FORWARD 189
#define IDS_205 205
#define IDS_SAVE_ITEM 208
#define IDS_DESCRIPTIVE_NAME 209
#define IDS_ERROR_SOFTWARE_UPDATE 210
#define IDS_211 211
#define IDS_235 235
#define IDC_SUB_FOLDERS 275
#define IDM_FEEDBACK 287
#define IDM_SCRIBE_LINK 291
#define IDM_INSCRIBE_LINK 292
#define IDM_FAQ 293
#define IDC_READ 316
#define IDD_SUB_FOLDERS 317
#define IDC_HAS_TEMPLATES 322
#define IDC_INBOX 324
#define IDC_OUTBOX 325
#define IDM_PAGE_SETUP 326
#define IDC_TRASH 327
#define IDC_CONTACT_FLD 328
#define IDS_DEBUG 329
#define IDC_SET_INBOX 330
#define IDC_SET_OUTBOX 331
#define IDC_SET_SENT 332
#define IDC_SET_TRASH 333
#define IDC_SET_CONTACTS 334
#define IDC_SET_FILTERS 335
#define IDC_MAX_SIZE 342
#define IDC_CONFIRM_DEL 343
#define IDS_CONTENT_ID 344
#define IDC_BOLD_UNREAD 346
#define IDC_GRID_LINES 347
#define IDC_TITLE 348
#define IDC_PREVIEW_LINES 349
#define IDC_SET_TEMPLATES 350
#define IDC_351 351
#define IDC_SMTP_AUTH 353
#define IDC_FILTERS_FLD 354
#define IDS_RETRY 355
#define IDS_FOLDER_PROPERTIES_COMPACT 356
#define IDC_HAS_SPAM 357
#define IDS_CALENDAR 359
#define IDS_NO_LOG 360
#define IDC_REC_TYPE 362
#define IDS_FIND 363
#define IDS_NO_ITEMS 364
#define IDS_RECEIVE_ALL_ACCOUNTS 365
#define IDS_NO_ITEMS_IN_FOLDER 366
#define IDS_PREVIEW_ON_SERVER 367
#define IDS_RECEIVE_MAIL 368
#define IDS_NO_TEMPLATES 369
#define IDC_STOP_FILTERING 370
#define IDS_NEW_EMAIL 371
#define IDS_EXIT 372
#define IDS_MARK_ALL_SEND 373
#define IDS_MARK 374
#define IDS_SELECT_MARKED 375
#define IDS_PRIORITY 376
#define IDS_HIGH 377
#define IDS_NORMAL 378
#define IDS_LOW 379
#define IDS_NEW_CONTACT 380
#define IDS_RECEIVE 381
#define IDC_LIMIT_TO 382
#define IDS_PRINT 383
#define IDS_CAL_SDAY_SUN 384
#define IDS_SAVE_CLOSE 385
#define IDS_FILTER 386
#define IDS_PREV_MSG 387
#define IDS_NEXT_MSG 388
#define IDS_CONDITIONS 390
#define IDS_ACTIONS 391
#define IDS_DETAIL 392
#define IDS_FIELDS 393
#define IDS_FILE_NAME 394
#define IDS_MIME_TYPE 395
#define IDS_PREVIEW 396
#define IDS_ERROR_FOLDERS_VERSION 397
#define IDS_ERROR_FOLDERS_STATUS 398
#define IDS_EXPORT 399
#define IDS_STATUS 400
#define IDD_STATUS 401
#define IDS_402 402
#define IDC_ACC_LIST 403
#define IDC_404 404
#define IDC_IN_FOLDER 406
#define IDS_407 407
#define IDS_408 408
#define IDC_STATUS_TXT 409
#define IDC_OP_TXT 410
#define IDS_411 411
#define IDC_EMAIL_TXT 412
#define IDC_EMAIL_PROG 413
#define IDD_MAIL_PROPERTIES 414
#define IDC_STOP 415
#define IDC_SEND_LOG 416
#define IDC_OP_PROG 417
#define IDC_SET_IN 418
#define IDS_HIDE_GNUPG 419
#define IDS_RESIZE 420
#define IDS_COPY_FOLDER 421
#define IDS_GNUPG_ATTACH_PUB_KEY 422
#define IDS_GNUPG_SIGN 423
#define IDS_GNUPG_ENCRYPT 424
#define IDS_GNUPG_DECRYPT 425
#define IDS_GNUPG_ERR_NOMSG 426
#define IDC_CLEAR_KEYWORDS 436
#define IDS_MONTH 437
#define IDC_BAYES_TABLE 439
#define IDS_HIGH_PRIORITY 440
#define IDD_ACCOUNT 441
#define IDS_443 443
#define IDS_444 444
#define IDS_445 445
#define IDC_MAIL_DESCRIPTION 448
#define IDC_SENT 449
#define IDC_RECEIVED 450
#define IDC_FORWARDED 451
#define IDC_REPLIED 452
#define IDC_HAS_ATTACH 453
#define IDC_MARKED 455
#define IDC_READY_SEND 456
#define IDC_OPEN_INSPECTOR 457
#define IDC_CREATED 459
#define IDD_NO_FOLDER 460
#define IDC_MESSAGE 462
#define IDS_470 470
#define IDC_CREATE_FOLDER 471
#define IDS_472 472
#define IDS_475 475
#define IDC_ACCOUNT 476
#define IDC_ACCOUNT_NAME 477
#define IDC_SMTP_PASSWORD 478
#define IDC_EXISTING 486
#define IDC_FOLDER_HISTORY 487
#define IDS_488 488
#define IDS_ASK_TNEF_DECODE 495
#define IDC_RECEIVE_TABLE 500
#define IDC_SEND_SSL 501
#define IDS_OK 502
#define IDC_NEW_FOLDER 503
#define IDC_EXISTING_FOLDER 504
#define IDC_BROWSE_NEW 505
#define IDC_BROWSE_EXISTING 506
#define IDS_YEAR 507
#define IDC_TEXT 508
#define IDC_SEARCH 509
#define IDS_510 510
#define IDC_BAYES_MODE 511
#define IDC_REPEATS 512
#define IDS_YEARS 513
#define IDS_CANCEL 514
#define IDS_SAVE 515
#define IDS_EMAIL 516
#define IDS_REPLY 517
#define IDS_REPLYALL 518
#define IDS_FORWARD 519
#define IDC_SIG_HTML 520
#define IDS_TO 521
#define IDC_DEFAULT_ALT 522
#define IDS_COMPACT_TESTING_DLG 523
#define IDS_FOLDER_COMPACT_ERR_DLG 524
#define IDS_COULDNT_OPEN 525
#define IDC_SEARCH_SUB 526
#define IDC_FIND_CASE 527
#define IDC_FIND_WORD 528
#define IDS_ATTACH_FILE 529
#define IDC_CTRLS_TABLE 530
#define IDC_MAIL_FIELD 531
#define IDC_CONTACT 532
#define IDM_BAYES_STATS 533
#define IDS_EDIT 534
#define IDC_ID_TABLE 535
#define IDC_CONTACT_FIELD 536
#define IDC_ACTION 537
#define IDS_REMOVE 538
#define IDM_UNIT_TESTS 539
#define IDC_LAYOUT 540
#define IDC_TABS 541
#define IDC_275 542
#define IDC_FIRST 543
#define IDS_544 544
#define IDC_OUT_FOLDER 545
#define IDC_SET_OUT 546
#define IDD_PAGE_SETUP 547
#define IDC_PREVIEW 548
#define IDS_ATTACH_WARNING_DLG 549
#define IDC_BROWSE_FONT 550
#define IDS_MAIL_PROPS_DLG 551
#define IDS_552 552
#define IDS_553 553
#define IDS_554 554
#define IDS_555 555
#define IDC_LEFT 556
#define IDC_TOP 557
#define IDC_RIGHT 558
#define IDC_BOTTOM 559
#define IDS_MAIL_PROPS 560
#define IDC_CUSTOM_FIELDS 561
#define IDS_ADD_CONTACTS 562
#define IDS_SUBFLD_NAME_CLASH 563
#define IDC_TREE 564
#define IDS_ERROR_NO_RECIPIENTS 565
#define IDC_USER_PASS_CONFIRM 566
#define IDS_SAVE_ATTACHMENT_AS 567
#define IDS_NO_CONNECTION_TO_SERVER 568
#define IDS_SERVER_ERROR_MSG 569
#define IDS_NO_SMTP_SERVER_SETTINGS 570
#define IDS_ASK_ABOUT_OPTIONS 571
#define IDS_THIS_WEEK 572
#define IDS_UPDATE_PERIOD 573
#define IDS_HEX_LOG 574
#define IDS_OPEN 575
#define IDS_BYTE_LOG 576
#define IDC_CALENDER 577
#define IDS_WINDOWS_SSL_INSTALL 578
#define IDC_KEYWORDS 579
#define IDC_PATH 580
#define IDC_UNREAD 581
#define IDC_LOCATION 582
#define IDS_ADDRESS 583
#define IDS_POPUP 584
#define IDC_PARAM_LABEL 585
#define IDC_DIR 586
#define IDS_LOW_PRIORITY 587
#define IDD_CAL 588
#define IDD_CAL_REMOTE 589
#define IDC_590 590
#define IDS_ADD_CAL 591
#define IDS_ADD_CAL_POPUP 592
#define IDS_SET_READ 593
#define IDS_SET_UNREAD 594
#define IDS_PROPERTIES 595
#define IDS_SUBJECT 596
#define IDS_OPTIONS 597
#define IDS_SMTP 598
#define IDS_SIZE 599
#define IDS_DATE 600
#define IDS_SAVEAS 601
#define IDS_EMPTY 602
#define IDS_ERROR_FOLDERS_DONT_EXIST 603
#define IDS_ERROR_CANT_OPEN_FOLDERS 604
#define IDS_ENTER_FILE_NAME_FOR_FOLDERS 605
#define IDS_SEND_MAIL 606
#define IDS_NEW 607
#define IDS_APPOINTMENT 608
#define IDS_CAL_EVENT 609
#define IDS_CONTACT 610
#define IDS_CREATESUBFOLDER 611
#define IDS_612 612
#define IDS_CAL_VIEW 613
#define IDS_MINUTES 614
#define IDS_HOUR 615
#define IDS_HOURS 616
#define IDS_DAY 617
#define IDS_DAYS 618
#define IDS_FREE 619
#define IDS_TENTATIVE 620
#define IDS_BUSY 621
#define IDS_OUT_OF_OFFICE 622
#define IDS_DOWNLOAD 623
#define IDS_READ_RECEIPT 624
#define IDS_CAL_SDAY_MON 625
#define IDS_RENAMEFOLDER 626
#define IDC_FOLDER_MSG 627
#define IDC_PASS 628
#define IDS_629 629
#define IDS_630 630
#define IDS_631 631
#define IDD_FOLDER_PROPS 632
#define IDS_FIRST 633
#define IDS_LOCAL_CONTACTS 634
#define IDC_ACCOUNT_TBL 635
#define IDS_MOVE_ERROR 636
#define IDS_MESSAGE 637
#define IDS_ACTION 638
#define IDS_MSGS_ERROR 639
#define IDS_CAL_LMONTH_OCT 640
#define IDC_ACCOUNTS_READ 641
#define IDS_CONFIGURE 642
#define IDS_ERROR_FILE_OVERWRITE 643
#define IDC_INTERNAL_FILTERING 644
#define IDC_STORE2_KEEP 645
#define IDS_ERROR_CANT_READ 646
#define IDC_FOLDER_WRITE 647
#define IDS_ERROR_FILE_EXISTS 648
#define IDD_BAYES_SETTINGS 649
#define IDC_USER_LEVEL_PSW 650
#define IDC_USER_PASS 651
#define IDS_ERROR_FOLDERS_ALREADY_EXIST 652
#define IDC_APW_USER 653
#define IDC_COMPACT_MS 654
#define IDD_FIND 655
#define IDC_DEF_SEND 656
#define IDC_TOOLBAR_TEXT 657
#define IDS_ADD 658
#define IDM_SAVEAS 659
#define IDC_URL 660
#define IDC_SUBJECT 661
#define IDD_MAIL_FIELDS 662
#define IDD_KEY 663
#define IDC_LABEL 664
#define IDS_147 665
#define IDC_SENT_DATE 666
#define IDS_148 667
#define IDC_RECEIVED_DATE 668
#define IDD_FILTER 669
#define IDC_PRIVACY_TYPE 670
#define IDS_671 671
#define IDM_NO_IDENTITIES 672
#define IDD_FILTER_ACTION 673
#define IDD_NEWFOLDER_F 674
#define IDD_FILES_IMPORT 675
#define IDS_ADD_LOCAL_CAL_FOLDER 676
#define IDC_UI_FNT_SIZE 677
#define IDC_UI_LANG 678
#define IDS_ERROR_LR8_FAILURE 679
#define IDC_REC_8BIT_CS 680
#define IDC_CLIENT 681
#define IDC_BLINK 682
#define IDC_FOLDER_READ 683
#define IDC_ACCOUNTS_WRITE 684
#define IDC_CUSTOM_TABLE 685
#define IDC_APR_USER 686
#define IDC_NAME_TABLE 687
#define IDC_USER 688
#define IDC_APR_ADMIN 689
#define IDC_APW_NONE 690
#define IDS_ADD_CAL_URL 691
#define IDC_REMINDER_VALUE 692
#define IDS_LIKE 693
#define IDC_PREVIEW_READ 694
#define IDS_ASK_USER_PASS 695
#define IDD_FILTER_SAVE_ATTACH 696
#define IDM_HELP 697
#define IDC_TYPES 698
#define IDC_OTHER_TABLE 699
#define IDC_BROWSE_DIR 700
#define IDC_ONLY_SEND_THIS 701
#define IDC_GROUPS_FLD 702
#define IDC_SET_GROUPS 703
#define IDS_CC 704
#define IDS_RECIPIENTS 705
#define IDS_TEXT 706
#define IDS_ATTACHMENTS 707
#define IDS_INTERNETHEADER 708
#define IDS_FOLDER_PROPERTIES_DLG 709
#define IDS_TOMORROW 710
#define IDS_NEXT_WEEK 711
#define IDC_FILES 712
#define IDC_PICK_FILES 713
#define IDC_USAGE 714
#define IDC_START_TIME 715
#define IDS_ABOUT 716
#define IDC_COLLECT_SUB_MAIL 717
#define IDS_NO_MAIL_TO_FILTER 718
#define IDS_FORWARD_PREFIX 719
#define IDS_REPLY_PREFIX 720
#define IDS_SPAM 721
#define IDS_SEARCH 722
#define IDS_ITEM_FILTER 723
#define IDD_WEBDAV_PROPS 724
#define IDC_725 725
#define IDM_IMPORT_EML 726
#define IDC_CONTACTS_URL 727
#define IDS_DELETE_FOLDER_DLG 728
#define IDS_1035 729
#define IDS_SET_DEFAULT 730
#define IDC_HTML_FONT 731
#define IDC_SET_HTML_FONT 732
#define IDS_APPEARANCE 733
#define IDS_UPDATE 734
#define IDC_EDIT_REPLY 735
#define IDC_EDIT_FORWARD 736
#define IDC_CALENDAR_URL 737
#define IDC_APW_ADMIN 738
#define IDS_ERROR_EXE_FILE 739
#define IDC_USERNAME 740
#define IDS_FROM 741
-#define IDC_PASSWORD 742
#define IDC_DATE_FORMAT 743
#define IDS_1034 744
#define IDS_SORT_SUBFOLDERS 745
#define IDC_DESC 746
#define IDC_948 747
#define IDS_LOADING 748
#define IDS_MOVE_FOLDER 749
#define IDC_FILTER_INDEX 750
#define IDC_FILTER_UP 751
#define IDC_FILTER_DOWN 752
#define IDM_SCRIPT_BREAK_ON_WARN 753
#define IDS_MOVE_ATTACH 754
#define IDS_ERROR_WONT_OVERWRITE_FOLDERS 755
#define IDS_NEXT_FOLDER 756
#define IDC_BAYES_READ 757
#define IDM_DELETE 758
#define IDS_SUB_FOLDER 759
#define IDS_WEEK 760
#define IDS_ERROR_READONLY_FOLDERS 761
#define IDC_TIMEZONE 762
#define IDC_LOCALTIME 763
#define IDC_HIDE_ID 764
#define IDS_SOFTWARE_UPDATE_DOWNLOAD 765
#define IDD_FILTER_SCRIPT 766
#define IDC_SCRIPT 767
#define IDS_ASK_DUPE_CONTACT 768
#define IDS_PERM_DEL_ATTACHMENTS 769
#define IDC_HTTP_PROXY 770
#define IDC_PICK_TZ 771
#define IDS_THREAD 772
#define IDS_SOFTWARE_CURRENT 773
#define IDS_TRANSLATION_AUTHORS 774
#define IDD_CSV_IMPORT 775
#define IDC_THEME 776
#define IDS_BCC 777
#define IDS_CLICK_TO_ADD 778
#define IDS_779 779
#define IDC_BROWSE_THEMES 780
#define IDD_OUTLOOK_EXPORT 781
#define IDS_SUMMARY 782
#define IDC_BROWSE_FOLDER 783
#define IDS_MERGE_TEMPLATE 784
#define IDS_MERGE_FILE 785
#define IDS_COPY 786
#define IDD_SETTINGS 787
#define IDS_DELETE_SYS_FOLDER 788
#define IDS_789 789
#define IDS_CAL_SDAY_TUE 790
#define IDS_791 791
#define IDS_792 792
#define IDS_793 793
#define IDS_794 794
#define IDS_MBOX_IMPORT 795
#define IDC_SET_READ 796
#define IDS_CAL_SDAY_WED 797
#define IDS_CAL_SDAY_THU 798
#define IDS_CAL_SDAY_FRI 799
#define IDS_CAL_SDAY_SAT 800
#define IDS_CAL_LDAY_SUN 801
#define IDS_CAL_LDAY_MON 802
#define IDS_CAL_LDAY_TUE 803
#define IDS_CAL_LDAY_WED 804
#define IDS_CAL_LDAY_THU 805
#define IDS_CAL_LDAY_FRI 806
#define IDS_CAL_LDAY_SAT 807
#define IDS_CAL_SMONTH_JAN 808
#define IDS_CAL_SMONTH_FEB 809
#define IDS_CAL_SMONTH_MAR 810
#define IDS_CAL_SMONTH_APR 811
#define IDS_CHANGED 812
#define IDS_CAL_SMONTH_MAY 813
#define IDS_CAL_SMONTH_JUN 814
#define IDS_CAL_SMONTH_JUL 815
#define IDS_CAL_SMONTH_AUG 816
#define IDS_CAL_SMONTH_SEP 817
#define IDS_CAL_SMONTH_OCT 818
#define IDS_CAL_SMONTH_NOV 819
#define IDS_CAL_SMONTH_DEC 820
#define IDS_CAL_LMONTH_JAN 821
#define IDS_EVENT_DUE 822
#define IDC_BAYES_THRESHOLD 823
#define IDS_CAL_LMONTH_FEB 824
#define IDS_CAL_LMONTH_MAR 825
#define IDS_CAL_LMONTH_APR 826
#define IDS_CAL_LMONTH_MAY 827
#define IDS_CAL_LMONTH_JUN 828
#define IDS_CAL_LMONTH_JUL 829
#define IDS_CAL_LMONTH_AUG 830
#define IDC_PREVIEW_SECONDS 831
#define IDS_CAL_LMONTH_SEP 832
#define IDC_SOCKS5_PASSWORD 833
#define IDS_CAL_LMONTH_NOV 834
#define IDS_CAL_LMONTH_DEC 835
#define IDC_RECEIVE_SSL 836
#define IDC_SEND_CHARSET1 837
#define IDC_SEND_CHARSET2 838
#define IDS_DELETE_ASK 839
#define IDS_DONT_ADJUST_TZ 840
#define IDC_DELETE_AFTER 841
#define IDC_HTML_IMAGES 842
#define IDC_AUTO_DELETE_EXE 843
#define IDD_GROUP 844
#define IDC_SEND_AUTH_TYPE 845
#define IDM_HOMEPAGE 846
#define IDC_GROUP_NAME 847
#define IDC_GROUP_LIST 848
#define IDC_START_DATE 849
#define IDC_850 850
#define IDS_TODAY 851
#define IDC_REC_ASCII_CP 852
#define IDS_FOLDER_OUTBOX 853
#define IDS_FOLDER_SENT 854
#define IDS_780 855
#define IDS_FOLDER_FILTERS 856
#define IDS_FOLDER_CONTACTS 857
#define IDS_ATTACHMENTS_NAME 858
#define IDS_ATTACHMENTS_DATA 859
#define IDS_ANYWHERE 860
#define IDS_TODO 861
#define IDS_FOLDER_TEMPLATES 862
#define IDS_FOLDER_GROUPS 863
#define IDS_FOLDER_CALENDAR 864
#define IDS_DEFAULT_CHARSET_RECEIVE 865
#define IDS_DUE 866
#define IDS_CLICK_TO_CREATE 867
#define IDC_REPEAT 868
#define IDS_1101 869
#define IDS_ERROR_ALREADY_ONLINE 870
#define IDS_NOT_LOADED 871
#define IDC_SHOW_LOCATION 872
#define IDS_ERROR_MAPI_NOT_INSTALLED 873
#define IDC_CALENDAR 874
#define IDS_PLUGINS_NO_CONF 875
#define IDC_TAB1 876
#define IDC_TAB2 877
#define IDC_CHECK_EVERY 878
#define IDS_1116 879
#define IDC_FILTER_ACTIONS 880
#define IDC_LOGIN 881
#define IDC_MSG_STORE 882
#define IDC_SRC_FOLDERS 883
#define IDC_FOLDER 884
#define IDS_ADD_ALL_CONTACTS 885
#define IDS_MAPI_LOGIN_FAILED 886
#define IDS_ACCSTAT_IDLE 887
#define IDS_BROWSE_FOLDER 888
#define IDS_3 889
#define IDS_ASK_ADMIN_PASS 890
#define IDS_LINKS 891
#define IDC_EXTRA_HEADERS 892
#define IDC_FORMAT 893
#define IDM_TABLE2 894
#define IDC_RECEIVE_AUTH_TYPE 895
#define IDS_896 896
#define IDC_SUSPECT_FOLDER 897
#define IDM_SET_UNREAD 898
#define IDS_HTML_TAGS 899
#define IDC_PROGRESS_TBL 900
#define IDS_DELETE 901
#define IDC_SET_SUSPECT_FOLDER 902
#define IDC_SEND_TABLE 903
#define IDS_838 904
#define IDC_TEMPLATE 905
#define IDS_1082 906
#define IDC_TXT_DAYS 907
#define IDC_FOLDERS 908
#define IDD_SECURITY 909
#define IDS_9 910
#define IDS_REMOVE_DUPES 911
#define IDC_SHOW_TOTALS 912
#define IDC_913 913
#define IDC_REMINDER_TYPE 914
#define IDS_ALWAYS_SHOW_REMOTE_CONTENT 915
#define IDC_TXT_SIZE 916
#define IDS_ERROR_FILE_EMPTY 917
#define IDC_REMINDER_PARAM 918
#define IDC_REMINDER_UNIT 919
#define IDD_EML_IMPORT 920
#define IDC_HOME_TABLE 921
#define IDC_APR_NONE 922
#define IDS_OPEN_CONTACT 923
#define IDS_THREAD_SELECT 924
#define IDS_THREAD_DELETE 925
#define IDS_THREAD_IGNORE 926
#define IDS_NONE 927
#define IDS_ALL 928
#define IDS_CONVERT_SELECTION 929
#define IDS_MULTIPLE_ITEMS 930
#define IDC_GROUP 931
#define IDC_MAPPING 932
#define IDS_933 933
#define IDS_934 934
#define IDC_TAB 935
#define IDC_HAS_FILTERS 936
#define IDC_MODE 937
#define IDC_MERGE 938
#define IDC_APPEND 939
#define IDC_CHECK_DEFAULT 940
#define IDC_EVERY 941
#define IDC_LOAD 942
#define IDS_ERROR_CANT_WRITE 943
#define IDS_944 944
#define IDS_SAVE_TO_OUTBOX 945
#define IDS_INC_BETA 946
#define IDS_947 947
#define IDC_TIME_TYPE 948
#define IDS_FOLDER_TRASH 949
#define IDD_FILES_EXPORT 950
#define IDS_1022 951
#define IDC_END_DATE 952
#define IDC_HAM 953
#define IDC_SPAM 954
#define IDC_FALSE_NEG 955
#define IDC_FALSE_POS 956
#define IDC_EFFICIENCY 957
#define IDS_RECEIPT_ASK 958
#define IDS_ERROR_NO_HELP 959
#define IDC_END_TIME 960
#define IDC_869 961
#define IDS_ERROR_CONNECTION 962
#define IDS_CREATE_FOLDER 963
#define IDC_ALL_DAY 964
#define IDC_DEF_REPLYALL_SETTING 965
#define IDS_DEFAULT_CHARSET_SEND 966
#define IDC_WEDNESDAY 967
#define IDS_WARN_NO_SECURITY 968
#define IDC_969 969
#define IDC_LAUNCH_HELP 970
#define IDC_THURSDAY 971
#define IDD_LANG 972
#define IDC_LANG 973
#define IDC_FRIDAY 974
#define IDS_975 975
#define IDS_976 976
#define IDD_FILTER_REPLY 977
#define IDD_FILTER_FORWARD 978
#define IDC_SATURDAY 979
#define IDC_SUNDAY 980
#define IDC_REC_PORT 981
#define IDS_INSPECT 982
#define IDS_FOLDER_INBOX 983
#define IDC_OCCURRENCES 984
#define IDC_CONFIGURE_REMOTE_CONTENT 985
#define IDC_TABLE3 986
#define IDC_BLACKLIST 987
#define IDC_ATTACHMENTS 988
#define IDS_GNUPG_ERR_NO_BOUNDARY 989
#define IDS_GNUPG_ERR_NO_MIME 990
#define IDS_ASK_FORWARD_ATTACHMENTS 991
#define IDC_ALL 992
#define IDC_MARK_REPLIED 993
#define IDC_MARK_FORWARDED 994
#define IDS_BOUNCE 995
#define IDS_REMOVE_WHITELIST 996
#define IDC_UNDELETE 997
#define IDS_ERROR_NO_CONFIG_SEND 998
#define IDS_ERROR_NO_CONFIG_RECEIVE 999
#define IDC_MAIL 1000
#define IDC_CONTACTS 1001
#define IDC_NAME 1002
#define IDC_TYPE 1003
#define IDC_EMAIL 1004
#define IDS_SEND 1005
#define IDC_REPLY_TO 1006
#define IDC_NICK 1007
#define IDM_SCRIBE_FAQ 1008
#define IDC_SPOUSE 1009
#define IDC_HAS_CAL_EVENTS 1010
#define IDC_QUOTE_STR 1011
#define IDC_WRAP_COLS 1012
#define IDS_ASK_DELETE_DUPES 1013
#define IDS_GNUPG_ERR_NO_RECIP 1014
#define IDC_START_IN 1015
#define IDC_SET_START_IN 1016
#define IDM_LAYOUT4 1017
#define IDC_DOWN 1018
#define IDM_REFRESH 1019
#define IDS_ERROR_REG_WRITE 1020
#define IDS_ERROR_NEED_INSTALL 1021
#define IDC_SMTP_SERVER 1022
#define IDC_SMTP_NAME 1023
#define IDC_REC_SERVER 1024
#define IDC_REC_NAME 1025
#define IDC_REC_PASSWORD 1026
#define IDC_REC_CHECK 1027
#define IDC_POP3_LEAVE 1028
#define IDS_ERROR_SERVER_CONNECT 1029
#define IDC_LAST 1030
#define IDC_QUOTE 1031
#define IDC_HOME_FAX 1032
#define IDC_HOME_STREET 1033
#define IDC_ACCOUNTS 1034
#define IDC_HOME_SUBURB 1035
#define IDC_FONT 1036
#define IDC_DESCRIPTION 1037
#define IDC_NEW_MAIL_SOUND 1038
#define IDS_MAIL_MESSAGE 1039
#define IDC_SMTP_DOMAIN 1040
#define IDC_HOME_STATE 1041
#define IDC_REPLYWITHSIG 1042
#define IDC_HOME_COUNTRY 1043
#define IDS_POP3 1044
#define IDC_HOME_PHONE 1045
#define IDC_HOME_MOBILE 1046
#define IDC_HOME_IM 1047
#define IDC_HOME_WEBPAGE 1048
#define IDC_NOTE 1049
#define IDC_DEFAULT 1050
#define IDC_TRAY 1051
#define IDS_DELETEFOLDER 1052
#define IDC_CHECKDIALUP 1053
#define IDC_SET_FONT 1054
#define IDC_POP3_ON_START 1055
#define IDC_LIST 1056
#define IDC_START_WEEK 1057
#define IDC_REFRESH 1058
#define IDS_COPY_PATH 1059
#define IDC_SET_SOUND 1060
#define IDS_OE_IMPORT 1061
#define IDS_1062 1062
#define IDM_MENU_1063 1063
#define IDC_DEL_SRC_FOLDER 1064
#define IDC_PICK_FOLDER 1065
#define IDC_WRAP 1066
#define IDC_NO_SPAM_TRASH 1067
#define IDC_ADD 1068
#define IDC_MSG 1069
#define IDC_KEY 1070
#define IDC_DONT_WARN 1071
#define ID_YES 1072
#define ID_NO 1073
#define IDC_REGISTER_CLIENT 1074
#define IDC_SOCKS5_SERVER 1075
#define IDC_SOCKS5_USER 1076
#define IDC_REMINDER_ADD 1077
#define IDC_USE_TEMPLATE 1078
#define IDC_SET_FOLDER 1079
#define IDC_REMINDERS 1080
#define IDC_AVAILABLE_TYPE 1081
#define IDC_AVAILABLE 1082
#define IDS_ERROR_NO_SPACE 1083
#define IDC_TEMPLATES 1084
#define IDS_RECEIPT_BODY 1085
#define IDS_SPAM_PROB 1086
#define IDS_IS_WHITELIST 1087
#define IDC_BUSY 1088
#define IDS_ERROR_MAPI_DEFAULT 1089
#define IDS_ERROR_SOFTWARE_KEY 1090
#define IDM_MANAGE_MAIL_STORES 1091
#define IDC_PUBLIC 1092
#define IDC_PRIVATE 1093
#define IDC_NEW_FILTER_ACTION 1094
#define IDC_DELETE_FILTER_ACTION 1095
#define IDS_MAIL 1096
#define IDC_RADIO1 1097
#define IDC_RADIO2 1098
#define IDC_DEF_ACTION 1099
#define IDC_WORK_COUNTRY 1100
#define IDC_WORK_PHONE 1101
#define IDC_WORK_MOBILE 1102
#define IDC_WORK_IM 1103
#define IDC_WORK_STATE 1104
#define IDC_WORK_POSTCODE 1105
#define IDC_WORK_STREET 1106
#define IDC_WORK_FAX 1107
#define IDC_WORK_SUBURB 1108
#define IDC_HOME_POSTCODE 1109
#define IDC_COMPANY 1110
#define IDC_WORK_WEBPAGE 1111
#define IDC_SOCKS5 1112
#define IDC_DELETE 1113
#define IDS_MAIL_MERGE_Q 1114
#define IDC_ADD_SRC_FOLDER 1115
#define IDC_PROPERTIES 1116
#define IDD_OUTLOOK_ADD_FLD 1117
#define IDS_ASK_FOLDER_PASS 1118
#define IDC_DEF_SEND_TXT 1119
#define IDS_ERROR_SCRIPT_COMPILE 1120
#define IDS_ANY 1121
#define IDS_ERROR_NOTHING_TO_UPGRADE 1122
#define IDC_PAGE_ALL 1123
#define IDC_SAVE 1124
#define IDC_PAGE_RANGES 1125
#define IDS_PASSWORD_SAVED 1126
#define IDS_RESIZE_JPEG_QUAL 1127
#define IDC_TXT_MIN 1128
#define IDC_TXT_DOWNLOAD 1129
#define IDC_TXT_KBS 1130
#define IDM_SCRIPTING_CONSOLE 1131
#define IDD_PRINT_PREVIEW 1132
#define IDC_SAVE_IMG 1133
#define IDS_ACCSTAT_SETUP 1134
#define IDS_ACCSTAT_CONNECT 1135
#define IDS_ACCSTAT_TRANS 1136
#define IDS_ACCSTAT_WAIT 1137
#define IDS_ACCSTAT_DELETING 1138
#define IDS_ACCSTAT_FINISHED 1139
#define IDS_ACCSTAT_CANCELLED 1140
#define IDS_ERROR 1141
#define IDS_ENTER_KEY 1142
#define IDM_SCRIPT_DEBUG 1143
#define IDC_UPDATE 1144
#define IDC_1145 1145
#define IDS_CONTAINS 1146
#define IDS_STARTS_WITH 1147
#define IDS_ENDS_WITH 1148
#define IDS_SERVER 1149
#define IDS_1150 1150
#define IDM_MENU_1151 1151
#define IDS_TIME 1152
#define IDM_HTML_EDITOR 1153
#define IDS_ERROR_MAPI_INIT_FAILED 1154
#define IDD_CAL_RECUR 1155
#define IDS_WEEKS 1156
#define IDC_REMOVE_FILES 1157
#define IDC_SHOW_EXTRA 1158
#define IDC_IMAGE 1159
#define IDC_GUEST_ENTRY 1160
#define IDC_ADD_GUEST 1161
#define IDS_MISSING_PARENT 1162
#define IDS_ORIGINAL_MESSAGE 1163
#define IDS_PORTABLE_Q 1164
#define IDC_TABLE 1165
#define IDS_NO_EVENTS 1166
#define IDS_UNREAD_MAIL 1167
#define IDS_1168 1168
#define IDC_SHOW_ADDR 1169
#define IDS_AND_OPERATOR 1170
#define IDS_OR_OPERATOR 1171
#define IDS_MEMBER_OF_GROUP 1172
#define IDS_ACTION_MOVE_FOLDER 1173
#define IDS_ACTION_SOUND 1174
#define IDS_ACTION_OPEN 1175
#define IDS_ACTION_EXECUTE 1176
#define IDS_ACTION_SET_COLOUR 1177
#define IDS_ACTION_SET_LABEL 1178
#define IDS_ACTION_EMPTY_FOLDER 1179
#define IDS_ACTION_MARK_SPAM 1180
#define IDS_ACTION_SAVE_ATTACHMENTS 1181
#define IDS_ACTION_DELETE_ATTACHMENTS 1182
#define IDS_ACTION_CREATE_FOLDER 1183
#define IDS_SELECT_FOLDER 1184
#define IDC_BAYES_INCREMENTAL 1185
#define IDC_BAYES_DEBUG 1186
#define IDC_IMG_NOTES_TBL 1187
#define IDC_WORK_TABLE 1188
#define IDC_OUTGOING 1189
#define IDS_ERROR_FILE_DOESNT_EXIST 1190
#define IDS_ERROR_FOLDER_DOESNT_EXIST 1191
#define IDS_IMPORT 1192
#define IDD_SCRIBE_EXPORT 1193
#define IDC_INCOMING 1194
#define IDM_MENU_1195 1195
#define IDC_DEST 1196
#define IDC_SET_DEST 1197
#define IDC_HAS_GROUPS 1198
#define IDC_CAL_URL 1199
#define IDC_SPAM_FLD 1200
#define IDC_SET_SPAM 1201
#define IDC_SPAM_FOLDER 1202
#define IDC_SET_SPAM_FOLDER 1203
+#define IDC_PASSWORD 1204
#define IDS_ERROR_PRINT_FAILED 1214
#define IDS_ASK_ACCOUNT_PASSWORD 1215
#define IDS_SELECT_IO 1216
#define IDC_GUESTS 1217
#define IDS_DONT_SHOW_AGAIN 1218
#define IDS_INSTALL 1219
#define IDC_1220 1220
#define IDS_EDIT_MAIL_STORES 1224
#define IDS_ERROR_CANT_CREATE_FOLDER 1225
#define IDS_ERROR_CANT_CREATE_DB 1226
#define IDS_SHOW_CONSOLE 1227
#define IDC_STATUS 1228
#define IDS_SELECT_ACCOUNT 1229
#define IDS_1235 1235
#define IDC_MONDAY 1238
#define IDC_TUESDAY 1239
#define IDS_MONTHS 1246
#define IDC_NEVER 1249
#define IDC_1250 1250
#define IDC_AFTER 1251
#define IDC_AFTER_COUNT 1252
#define IDC_1254 1254
#define IDC_ON 1255
#define IDC_ON_DATE 1256
#define IDC_SUMMARY 1258
#define IDS_ALWAYS_SHOW 1260
#define IDS_OPEN_SOURCE 1262
#define IDS_DEFAULT 1263
#define IDS_IMPORT_SETTINGS_Q 1264
#define IDS_IMPORT_SETTINGS_DONE 1265
#define IDS_DELETE_ATTACHMENTS 1266
#define IDS_LANGUAGE 1267
#define IDS_1268 1268
#define IDS_SHOW_REMOTE_CONTENT 1274
#define IDC_DELETE_DAYS 1275
#define IDS_RESIZE_IMGS 1277
#define IDD_REMOTE_CONTENT 1279
#define IDC_WHITELIST 1282
#define IDS_REMOTE_CONTENT_MSG 1287
#define IDS_GNUPG_ERR_SAVE_FAILED 1291
#define IDS_MESSAGES_SENT 1293
#define IDS_MAIL_MERGE_EMPTY 1294
#define IDS_GNUPG_ERR_NO_OUTPUT 1295
#define IDS_FILTER_MAILINGLIST 1299
#define IDC_ADVANCED 1300
#define IDS_ACTION_COPY 1301
#define IDC_PAGE_PARTIAL 1302
#define IDD_CSV_EXPORT 1304
#define IDS_1305 1305
#define IDS_EXPORT_MSG 1312
#define IDS_MARK_ALL_READ 1313
#define IDD_MANAGE_FOLDERS 1314
#define IDC_MS_TBL 1315
#define IDC_OPEN_MS 1316
#define IDC_CLOSE_MS 1317
#define IDC_CREATE_MS 1318
#define IDC_CONVERT_MS 1320
#define IDC_REPAIR_MS 1321
#define IDC_MAIL_STORES 1324
#define IDS_1325 1325
#define IDS_1326 1326
#define IDC_MSG_ID 1327
#define IDC_FILTER 1328
#define IDS_GNUPG_ERR_WRONG_SEGS 1329
#define IDS_RELATIVE_TIMES 1333
#define IDS_1335 1335
#define IDS_CHECKING_OBJECTS 1336
#define IDS_ERROR_FOLDER_NOT_LOADED 1337
#define IDS_1338 1338
#define IDS_1339 1339
#define IDS_1340 1340
#define IDS_1341 1341
#define IDD_REPLICATE 1342
#define IDC_SRC 1346
#define IDC_DST 1348
#define IDM_CHECK_UPDATE 1350
#define IDS_1353 1353
#define IDS_1354 1354
#define IDS_ERROR_FONT_SETTINGS 1356
#define IDC_SEC_AUTH 1357
#define IDC_EDIT_CONTROL 1359
#define IDC_DELETE_LARGER 1360
#define IDC_DELETE_SIZE 1361
#define IDC_CLEAR 1364
#define IDS_ADD_TO_CAL 1367
#define IDS_ADD_TO_DICTIONARY 1368
#define IDS_ERROR_CANT_ADD_DICT 1370
#define IDS_SPELL_CHECK 1371
#define IDS_SPELL_DICT 1372
#define IDS_MAILSTORE_UPGRADE_Q 1373
#define IDS_ERROR_IMPORT 1374
#define IDS_ERROR_REPLICATION_FAILED 1375
#define IDS_ERROR_IMPORT_COUNT 1388
#define IDS_GNUPG_ERR_NOT_INSTALLED 1458
#define IDD_FOLDER_SELECT 1469
#define IDS_REPARSE 1471
#define IDD_FOLDER_FORMAT 1472
+#define IDS_GNUPG_PSW_PROMPT 1473
#define IDC_V1 1481
#define IDC_V2 1482
#define IDC_UP 1483
#define IDS_GROWL_SUPPORT 1485
#define IDS_GROWL 1486
#define IDC_MSG_DEL_ACTION 1487
#define IDC_MSG_DEL_NEXT 1488
#define IDC_MSG_DEL_CLOSE 1489
#define IDC_MSG_DEL_PREV 1490
#define IDC_RECEIVE_LOG 1514
#define IDS_EMAIL_PROGRESS 1516
#define IDS_COPY_LOG_TO_CLIP 1517
#define IDC_SMTP_PORT 1519
#define IDS_RESIZE_MAX_PX 1522
#define IDS_RESIZING 1523
#define IDS_RESIZE_MAX_SIZE 1524
#define IDS_UPGRADE_KEY 1525
#define IDC_UPGRADE 1526
#define IDS_MAIL2_DEPRECATED 1527
#define IDS_SSL_DEBUG 1528
#define IDS_CONSOLE 1529
#define IDC_SPELL_LANG 1531
#define IDC_WIDTH 1535
#define IDC_SLIDE 1537
#define IDC_BAYES_DELETE_ON_SERVER 1541
#define IDC_BAYES_DELETE_ATTACHMENTS 1543
#define IDC_TENTATIVE 1548
#define IDS_DELETING 1549
#define IDS_DONT_SHOW_EMOJI 1550
#define IDS_HELP 1551
#define IDS_DESKTOP 1552
#define IDS_PORTABLE 1553
#define IDC_BTNS_TABLE 1556
#define IDS_GNUPG_CHECKING 1558
#define IDS_GNUPG_ERR_NO_SIGN_ENC 1561
#define IDS_GNUPG_ERR_CANT_START 1565
#define IDS_GNUPG_ERR_CODE 1568
#define IDS_GNUPG_ERR_NO_CONTENT 1570
#define IDS_GNUPG_ERR_READ_FAIL 1572
#define IDS_GNUPG_ERR_NO_MAIL 1574
#define IDS_GNUPG_ERR_NO_ROOT 1578
#define IDS_GNUPG_ERR_WRONG_MIME 1580
#define IDS_GNUPG_ERR_NO_ENC_MIME 1581
#define IDS_GNUPG_ERR_NO_ID 1582
#define IDS_GNUPG_DECRYPT_CANCEL 1583
#define IDS_GNUPG_ERR_NO_DATA 1584
#define IDS_GNUPG_ERR_DECRYPT_FAIL 1585
#define IDS_GNUPG_ERR_NO_SENDER_EMAIL 1586
#define IDS_GNUPG_ERR_NO_PRIV_KEY 1587
#define IDS_GNUPG_ERR_NO_PUB_KEY 1588
#define IDS_GNUPG_ERR_IMPORT_FAIL 1589
#define IDS_GNUPG_ERR_SIGN_ENC_CANCEL 1590
#define IDS_GNUPG_ERR_TEMP_WRITE 1591
#define IDS_GNUPG_ERR_EXPORT_TEMP 1592
#define IDS_GNUPG_ERR_NEW_ATTACH_FAIL 1593
#define IDS_GNUPG_ERR_REPARENT 1594
#define IDS_GNUPG_ERR_RECIP_NO_EMAIL 1595
#define IDS_GNUPG_ERR_WRITE 1596
#define IDS_GNUPG_ERR_ENCRYPT_FAIL 1597
#define IDS_GNUPG_ERR_ONE_OR_MORE 1598
#define IDS_GNUPG_DECRYPT_OK 1599
#define IDS_GNUPG_ENCRYPTED 1600
#define IDS_GNUPG_ENCRYPTED_AND_SIGNED 1601
#define IDS_GNUPG_SIGNED 1602
#define IDS_GNUPG_GOOD_SIG 1603
#define IDS_GNUPG_BAD_SIG 1604
#define IDS_GNUPG_SIG_MSG 1605
#define IDS_GNUPG_SIG_NO_ID 1606
#define IDS_GNUPG_ERR_INVALID_DECRYPTION 1607
#define IDS_GNUPG_CHECK_MSG 1608
#define IDS_SHOW_SCRIPT_DEBUGGER 1609
#define IDC_ACC_LOG 1613
#define IDC_1615 1615
#define IDC_1616 1616
#define IDS_SHOW_SIZE_IN_KIB 1617
#define IDS_GNUPG_GET_GPG 1619
#define IDC_START_REL 1620
#define IDC_END_REL 1621
#define IDS_YESTERDAY 1622
#define IDC_1626 1626
#define IDS_MBOX_SELECT_FOLDER 2000
#define IDS_MBOX_READING 2001
#define IDS_MBOX_EXPORT 2002
#define IDS_MBOX_EXPORT_FOLDER 2003
#define IDS_MBOX_WRITING 2004
#define IDS_STORE2_ERROR_COMPACT 2005
#define IDS_STORE2_DISCARD_FIX 2006
#define IDS_STORE2_SAVE_DEBUG 2007
#define IDS_NAME 2008
#define IDS_NO_MAIL_TO_SEND 2009
#define IDS_NO_OUTGOING_FOLDER 2010
#define IDS_ERROR_NO_NAME_EMAIL 2011
#define IDS_APPNAME 2012
#define IDS_SURNAME 2013
#define IDC_SET_CALENDER 2014
#define IDC_NEW_MAIL_NOTIFY 2015
#define IDD_NEW_MAIL_NOTIFY 2016
#define IDC_NEW_MAIL 2017
#define IDC_REMEMBER_PSW 2020
#define IDS_36 2021
#define IDS_37 2022
#define IDC_OPEN 2023
#define IDC_GLYPH_SUB 2031
#define IDC_FILTERS 2032
#define IDD_OUTLOOK_IMPORT 2040
#define IDD_WARN_DEFAULT 2041
#define IDC_POP_RECIP 2042
#define IDD_ADDCONTACT 2043
#define IDD_CONTACT 2044
#define IDD_FOLDER_NAME 2045
#define IDD_POPVIEW 2047
#define IDC_FPR_NONE 3000
#define IDC_FPR_USER 3001
#define IDC_FPR_ADMIN 3002
#define IDC_FPW_NONE 3003
#define IDC_FPW_USER 3004
#define IDC_FPW_ADMIN 3005
#define IDM_BOUNCE 40000
#define IDM_SOFTWARE_KEY 40001
#define IDM_UPGRADE_MAIL_STORES 40002
#define IDM_OPTIONS 40004
#define IDM_PRINT 40005
#define IDM_PRINTSETUP 40006
#define IDM_EXIT 40007
#define IDM_NEW_EMAIL 40008
#define IDM_REPLY 40009
#define IDM_REPLY_ALL 40010
#define IDM_FORWARD 40011
#define IDM_SEND_MAIL 40012
#define IDM_NEW_CONTACT 40014
#define IDM_ABOUT 40019
#define IDM_IMPORT_NS_CONTACTS 40020
#define IDM_IMPORT_OUTLOOK_PAB 40025
#define IDM_IMPORT_OUTLOOK_ITEMS 40027
#define IDM_NEW_FILTER 40038
#define IDM_IMPORT_TEXT_MBOX 40047
#define IDM_EXPORT_TEXT_MBOX 40048
#define IDM_IMP_MBX_EMAIL 40049
#define IDM_IMP_DBX_EMAIL 40050
#define IDM_NEW_DRAFT 40060
#define IDM_NO_TEMPLATES 40061
#define IDM_FILTER_CURRENT_FOLDER 41010
#define IDM_IMP_EUDORA_ADDR 41043
#define IDM_IMP_MOZILLA_ADDR 41045
#define IDM_FIND 41055
#define IDM_IMPORT_CSV 41065
#define IDM_WORK_OFFLINE 41075
#define IDM_EXPORT_CSV 41085
#define IDM_NEW_GROUP 41095
#define IDM_SECURITY 41105
#define IDM_BAYES_SETTINGS 41116
#define IDM_BAYES_CHECK 41117
#define IDM_BUILD_BAYES_DB 41118
#define IDM_LOGOUT 41129
#define IDM_FOLDERS_DOWN_LEFT 41142
#define IDM_PREVIEW_ALONG_BOTTOM 41143
#define IDM_LAYOUT1 41144
#define IDM_LAYOUT2 41145
#define IDM_LAYOUT3 41146
#define IDM_EXPORT_OUTLOOK_ITEMS 41147
#define IDM_CRASH 41148
#define IDM_TUTORIALS 41149
#define IDM_DEBUG_INFO 41150
#define IDM_VERSION_HISTORY 41151
#define IDM_MEMECODE 41152
#define IDM_SET_READ 41155
#define IDM_IMP_MOZILLA_MAIL 41156
#define IDM_DEBUG_FILTERS 41157
#define IDM_FILTERS_DISABLE 41158
#define IDM_DUMP_MEM 41159
#define IDM_FILTER_SELECTION 41160
#define IDM_EXPORT_SCRIBE 41161
#define IDM_TOOLS_MENU 41163
#define IDM_REPLICATE 41164
#define IDM_EXPORT 41165
#define IDM_COPY 'copy'
#define IDM_CUT 'cut '
#define IDM_PASTE 'past'
diff --git a/src/Encryption/GnuPG.cpp b/src/Encryption/GnuPG.cpp
--- a/src/Encryption/GnuPG.cpp
+++ b/src/Encryption/GnuPG.cpp
@@ -1,1973 +1,1980 @@
/*
https://lists.gnupg.org/pipermail/gnupg-users/2011-November/043223.html
gpg --list-packets ?
Mime hierarchy:
Signed:
multipart/signed "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)"
{
multipart/mixed
text/plain Body of the message
}
application/pgp-signature "-----BEGIN PGP SIGNATURE-----"
Signed, key attached:
multipart/signed "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)"
{
multipart/mixed
text/plain Body of the message
application/pgp-keys "-----BEGIN PGP PUBLIC KEY BLOCK-----"
}
application/pgp-signature "-----BEGIN PGP SIGNATURE-----"
Signed, HTML, attachment, key:
multipart/signed
{
multipart/mixed
multipart/mixed
multipart/alternative
text/plain
text/html
application/octet-stream
application/pgp-keys
}
application/pgp-signature
Encrypted:
multipart/encrypted
application/pgp-encrypted
application/octet-stream
{
multipart/mixed
multipart/mixed
text/plain
}
Encrypted + key attached:
multipart/encrypted
application/pgp-encrypted
application/octet-stream
{
multipart/mixed
multipart/mixed
text/plain
application/pgp-keys
}
*/
#include "lgi/common/Lgi.h"
#include "Scribe.h"
#include "GnuPG.h"
#include "lgi/common/TextLabel.h"
#include "lgi/common/Button.h"
#include "lgi/common/CheckBox.h"
#include "lgi/common/Combo.h"
#include "lgi/common/Css.h"
#include "lgi/common/ThreadEvent.h"
#include "lgi/common/SubProcess.h"
#include "ScribeListAddr.h"
#include "Store3Common.h"
#include "lgi/common/LgiRes.h"
#include "resdefs.h"
#include "lgi/common/CssTools.h"
//////////////////////////////////////////////////////////////////////////////////////////
enum Ctrls
{
IDC_SIGN = 700,
IDC_ENCRYPT,
IDC_ATTACH_PUB_KEY,
IDC_DECRYPT,
IDC_INSTALL
};
static LColour cGood (0, 204, 0);
static LColour cWarn (255, 154, 0);
static LColour cError (255, 0, 0);
static LColour cTxt (L_TEXT);
#define cDefaultListItemColour 0
#define PANEL_BORDER_PX 2
static const char *GpgInstall = "https://www.gnupg.org/download/index.en.html";
static const char *GpgBin = "gpg" LGI_EXECUTABLE_EXT;
static LString GpgBinPath;
#define SECONDS * 1000
#define MINUTES * 60
#define GPG_KEY_STALE_TIMEOUT (10 MINUTES)
#define DecryptStatus(val) \
{ \
if (callback) callback(val); \
return; \
}
typedef LArray KeyArr;
typedef LAutoPtr KeyArrAuto;
struct GpgJob
{
enum JobType
{
JobGetKeys,
JobCheckSig,
JobDecrypt,
} Type;
LViewI *Owner;
GpgJob(JobType t)
{
Type = t;
Owner = NULL;
}
// Get keys:
LAutoPtr Emails;
// Check sig:
LAutoStreamI Msg;
LMessage::Param UserValue;
// Decrypt
LString Password;
};
struct LTempFile : public LFile
{
LString Path;
LTempFile(const char *path)
{
if (Open(path, O_READ))
{
Path = path;
}
}
~LTempFile()
{
if (Path)
{
Close();
FileDev->Delete(Path, false);
}
}
};
struct GpgConnectorPriv : public LThread, public LMutex
{
LThreadEvent Event;
private:
bool Loop;
KeyArr Keys;
LArray Work;
uint64 KeysTs;
public:
GpgConnectorPriv() :
LThread("GpgConnectorPrivThread"),
LMutex("GpgConnectorPrivMutex")
{
Loop = true;
KeysTs = 0;
Run();
}
~GpgConnectorPriv()
{
Loop = false;
Event.Signal();
while (!IsExited())
LSleep(1);
}
void AddWork(GpgJob *j)
{
if (Lock(_FL))
{
Work.Add(j);
Unlock();
}
}
private:
LString RunGpg(const char *Args)
{
LString Output;
LSubProcess p(GpgBinPath, Args);
if (p.Start(true, false))
{
char Buf[256];
while (true)
{
ssize_t r = p.Read(&Buf, sizeof(Buf)-1);
if (r > 0)
{
Buf[r] = 0;
Output += Buf;
}
else break;
}
}
return Output;
}
void ParseKeys(KeyArr &k, LString &str)
{
bool GotDash = false;
LString::Array a = str.Split(EOL_SEQUENCE);
LString KeyId;
for (unsigned i=0; i 0)
{
if (b[0].Equals("sec") ||
b[0].Equals("pub"))
{
if (b.Length() == 4)
KeyId = b[2];
}
else if (b[0].Equals("uid"))
{
LString Nm = Ln(21, -1).Strip();
LAutoString Name, Addr;
DecodeAddrName(Nm, Name, Addr, NULL);
if (ValidStr(Name) && ValidStr(Addr))
{
GpgConnector::KeyInfo &Cur = k.New();
Cur.KeyId = KeyId;
Cur.Name = Name;
Cur.Email = Addr;
}
else LgiTrace("%s:%i - Error parsing '%s'\n", _FL, Nm.Get());
}
}
}
else if (stristr(Ln, "--------"))
{
GotDash = true;
}
}
}
bool GetKeys()
{
uint64 Now = LCurrentTime();
if (Now - KeysTs > GPG_KEY_STALE_TIMEOUT)
{
KeysTs = Now;
LString s = RunGpg("-k");
if (s)
ParseKeys(Keys, s);
s = RunGpg("-K");
if (s)
{
KeyArr Priv;
ParseKeys(Priv, s);
LHashTbl, GpgConnector::KeyInfo*> Hash;
for (auto &k: Keys)
{
if (k.Email)
Hash.Add(k.Email, &k);
else
LgiTrace("%s:%i - No email for key?\n", _FL);
}
for (unsigned i=0; iFlags |= GPG_HAS_PRIV_KEY;
}
}
}
return Keys.Length() > 0;
}
bool Save(const char *Path, LString &s)
{
LFile f;
if (!f.Open(Path, O_WRITE))
return false;
f.SetSize(0);
return f.Write(s, s.Length()) == s.Length();
}
bool Save(const char *Path, LStreamI *s)
{
LFile f;
if (!f.Open(Path, O_WRITE))
return false;
f.SetSize(0);
LCopyStreamer Cp;
int64 SrcSz = s->GetSize();
int64 Copied = Cp.Copy(s, &f);
return SrcSz == Copied;
}
void CheckSignature(LViewI *Owner, LAutoStreamI UserMsg, LMessage::Param UserVal)
{
LString Msg;
int64 Sz = 0;
int64 Rd = 0;
ptrdiff_t HdrSize = 0;
LString Start;
LArray Segs;
LMime Tmp;
LString Hdrs;
char *Boundary = NULL;
LAutoPtr Resp(new GpgSigCheckResponse);
if (!Resp)
return; // Not much else we can do.
Resp->UserValue = UserVal;
if (!UserMsg)
{
Resp->Error.Printf(LLoadString(IDS_GNUPG_ERR_NOMSG));
goto OnSigCheckError;
}
// We have to parse out just the part of the email that is signed. And we
// can't use the LMime class because that would decode it too much and lose
// the exact formatting. Read the whole thing into memory and find the MIME
// boundary.
Sz = UserMsg->GetSize();
UserMsg->SetPos(0);
if (!Msg.Length((int)Sz))
{
Resp->Error.Printf("Can't size string to " LPrintfInt64, Sz);
goto OnSigCheckError;
}
for (int64 i=0; iRead(Msg.Get() + i, Msg.Length() - i);
if (Rd <= 0)
{
Resp->Error.Printf("Read error: i=%i, Rd=%i, Sz=" LPrintfInt64, i, Rd, Sz);
goto OnSigCheckError;
}
i += Rd;
}
HdrSize = Msg.Find("\r\n\r\n");
Hdrs = Msg(0, HdrSize);
Tmp.SetHeaders(Hdrs);
Boundary = Tmp.GetBoundary();
if (!Boundary)
{
Resp->Error = LLoadString(IDS_GNUPG_ERR_NO_BOUNDARY);
goto OnSigCheckError;
}
// Now look through the message and find all the segments...
Start.Printf("\r\n--%s", Boundary);
for (ptrdiff_t i = 0; i < (ptrdiff_t)Msg.Length(); )
{
ptrdiff_t Next = Msg.Find(Start, i);
if (Next > 0)
{
// Is it an end or starting boundary?
ptrdiff_t k = Next + Start.Length();
LString Chars = Msg(k, k + 2);
bool IsEnd = Chars == "--";
if (IsEnd)
{
if (Segs.Length() == 0)
{
Resp->Error = LLoadString(IDS_GNUPG_ERR_NO_MIME);
goto OnSigCheckError;
}
Segs.Last().Len = Next - Segs.Last().Start;
i = k + 2;
}
else
{
if (Segs.Length() > 0)
{
// Finish last segment
Segs.Last().Len = Next - Segs.Last().Start;
}
char *m = Msg;
while (IsWhiteSpace(m[k]))
k++;
LRange &r = Segs.New();
r.Start = k;
r.Len = 0;
i = k;
}
}
else break;
}
if (Segs.Length() == 2)
{
// char *m = Msg;
LRange &Body = Segs[0];
LRange &Sig = Segs[1];
// Get the 2 parts from the whole message..
LString SignedText = Msg(Body.Start, Body.Start + Body.Len);
LString Signature = Msg(Sig.Start, Sig.Start + Sig.Len);
ptrdiff_t Break = Signature.Find("\r\n\r\n");
if (Break > 0)
Signature = Signature(Break + 4, -1);
// Save them to files:
LFile::Path TextPath = ScribeTempPath(), SigPath = ScribeTempPath();
TextPath += "signed.txt";
SigPath += "signature.txt";
if (!Save(TextPath, SignedText) ||
!Save(SigPath, Signature))
{
Resp->Error = LLoadString(IDS_GNUPG_ERR_SAVE_FAILED);
goto OnSigCheckError;
}
// Now call GnuPG to verify the signature...
LString Args;
Args.Printf("--verify \"%s\" \"%s\"", SigPath.GetFull().Get(), TextPath.GetFull().Get());
LString Output = RunGpg(Args);
if (Output)
{
LString::Array a = Output.SplitDelimit("\n");
for (unsigned i=0; i= 0)
{
LString::Array w = Line.SplitDelimit(" ");
// Check if the signature is good...
if (w.Length() > 2)
Resp->SignatureMatch = w[1].Lower() == "good";
// Get identity...
w = Line.SplitDelimit("\"");
if (w.Length() >= 2 &&
w[1].Find("@") >= 0)
{
Resp->Identity = w[1];
}
}
else if (Line.Lower().Find("signature made") >= 0)
{
// Get date...
LString::Array w = Line.SplitDelimit(" ");
int MadeIdx = -1;
LString::Array DateParts;
for (unsigned i=0; i 0)
Resp->TimeStamp.SetDate(w[i]);
else if (w[i].Find(":") > 0)
{
Resp->TimeStamp.SetTime(w[i]);
MadeIdx = -1;
}
else if (w[i].Lower() == "made")
MadeIdx = i;
else if (MadeIdx >= 0 && (int)i > MadeIdx)
DateParts.New() = w[i];
}
if (Resp->TimeStamp.Year() == 0 &&
DateParts.Length() > 0)
{
LString s = LString(" ").Join(DateParts);
Resp->TimeStamp.SetDate(s);
}
}
}
}
else
{
Resp->Error = LLoadString(IDS_GNUPG_ERR_NO_OUTPUT);
}
#ifndef _DEBUG
// Clean up temporary files...
FileDev->Delete(TextPath, false);
FileDev->Delete(SigPath, false);
#endif
printf("Txt=%s\nSig=%s\n", (const char*)TextPath, (const char*)SigPath);
}
else
{
Resp->Error.Printf(LLoadString(IDS_GNUPG_ERR_WRONG_SEGS), Segs.Length());
}
OnSigCheckError:
Owner->PostEvent(M_GNUPG_SIG_CHECK, (LMessage::Param) Resp.Release());
}
void Decrypt(GpgJob *j)
{
LAutoPtr Resp(new GpgDecryptResponse);
if (!Resp || !j)
return;
Resp->UserValue = j->UserValue;
LFile::Path InPath = ScribeTempPath();
InPath += "encrypted.txt";
LFile::Path OutPath = ScribeTempPath();
OutPath += "decrypted.txt";
if (!Save(InPath, j->Msg))
{
Resp->Error = "Failed to save file for decrypting.";
}
else
{
LString Args;
Args.Printf("--batch --passphrase-fd 0 --output \"%s\" --decrypt \"%s\"",
OutPath.GetFull().Get(),
InPath.GetFull().Get());
LSubProcess Proc(GpgBinPath, Args);
if (!Proc.Start(true, true))
{
Resp->Error = "Can't start the GnuPG sub-process.";
}
else
{
char Buf[256];
LVariant v;
if (Proc.GetValue(LDomPropToString(StreamReadable), v) &&
v.CastInt32() != 0)
{
ssize_t r = Proc.Read(Buf, sizeof(Buf)-1);
Buf[MAX(r, 0)] = 0;
}
LString PswStr;
PswStr.Printf("%s\n", j->Password.Get());
ssize_t w = Proc.Write(PswStr.Get(), PswStr.Length());
if (w < 0)
{
ssize_t r = Proc.Read(Buf, sizeof(Buf)-1);
Buf[MAX(r, 0)] = 0;
Resp->Error = Buf[0] ? Buf : "Can't write to the GnuPG sub-process.";
}
else
{
ssize_t r = Proc.Read(Buf, sizeof(Buf)-1);
Buf[MAX(r, 0)] = 0;
int Result = Proc.Wait();
LFile *f;
if (Result)
{
if (Buf[0])
Resp->Error = Buf[0];
else
Resp->Error.Printf("GnuPG encryption process failed with code: %i", Result);
}
else if
(
!Resp->Data.Reset(f = new LTempFile(OutPath)) ||
!f->IsOpen())
{
Resp->Data.Reset();
Resp->Error.Printf("Decrypt failed: Can't open '%s' for reading.", OutPath.GetFull().Get());
}
else
{
// Success?
}
}
}
}
// Clean up input file (encrypted)
// The output file will be deleted by the M_GNUPG_DECRYPT handler.
if (LFileExists(InPath))
{
FileDev->Delete(InPath, false);
}
j->Owner->PostEvent(M_GNUPG_DECRYPT, (LMessage::Param) Resp.Release());
}
int Main()
{
LThreadEvent::WaitStatus s;
while ((s = Event.Wait()) == LThreadEvent::WaitSignaled)
{
if (!Loop) break;
LAutoPtr j;
if (Lock(_FL))
{
if (Work.Length())
{
j.Reset(Work[0]);
Work.DeleteAt(0, true);
}
Unlock();
}
if (j)
{
switch (j->Type)
{
case GpgJob::JobGetKeys:
{
if (Keys.Length() == 0)
GetKeys();
LHashTbl,bool> Map;
for (unsigned i=0; iEmails->Length(); i++)
{
Map.Add((*j->Emails)[i], true);
}
KeyArrAuto Inf(new KeyArr);
for (unsigned i=0; iNew();
out.Email = in.Email.Get();
out.Name = in.Name.Get();
out.KeyId = in.KeyId.Get();
out.Flags = in.Flags;
}
j->Owner->PostEvent(M_GNUPG_KEY_INFO, 0, (LMessage::Param)Inf.Release());
break;
}
case GpgJob::JobCheckSig:
{
CheckSignature(j->Owner, j->Msg, j->UserValue);
break;
}
case GpgJob::JobDecrypt:
{
Decrypt(j);
break;
}
default:
{
LAssert(!"Invalid type.");
break;
}
}
}
}
return 0;
}
};
bool GpgConnector::IsInstalled()
{
#ifdef WINNATIVE
char *Str = NULL;
errno_t Err = _dupenv_s(&Str, NULL, "PATH");
if (Err)
{
LgiTrace("%s:%i - _dupenv_s failed with %i\n", _FL, Err);
return false;
}
LString Path = Str;
free(Str);
#else
LString Path = getenv("PATH");
#ifdef MAC
Path += LGI_PATH_SEPARATOR"/opt/local/bin";
#endif
#endif
LString::Array Parts = Path.Split(LGI_PATH_SEPARATOR);
for (unsigned i = 0; i < Parts.Length(); i++)
{
LFile::Path p(Parts[i]);
p += GpgBin;
if (p.IsFile())
{
GpgBinPath = p;
return true;
}
}
return false;
}
GpgConnector::GpgConnector()
{
d = new GpgConnectorPriv;
}
GpgConnector::~GpgConnector()
{
delete d;
}
bool GpgConnector::GetKeyInfo(LViewI *Target, LString::Array &Emails)
{
if (!Target
#if LGI_VIEW_HANDLE
|| !Target->Handle()
#endif
)
{
LAssert(!"Invalid target.");
return false;
}
if (!d->Lock(_FL))
return false;
GpgJob *j = new GpgJob(GpgJob::JobGetKeys);
if (j)
{
j->Emails.Reset(new LString::Array(Emails));
j->Owner = Target;
d->AddWork(j);
}
d->Unlock();
return d->Event.Signal();
}
bool GpgConnector::CheckSignature(LViewI *Target, LAutoStreamI Rfc822Msg, LMessage::Param UserVal)
{
if (!Target
#if LGI_VIEW_HANDLE
|| !Target->Handle()
#endif
)
{
LAssert(!"Invalid target.");
return false;
}
if (!d->Lock(_FL))
return false;
GpgJob *j = new GpgJob(GpgJob::JobCheckSig);
if (j)
{
j->Owner = Target;
j->Msg = Rfc822Msg;
j->UserValue = UserVal;
d->AddWork(j);
}
d->Unlock();
return d->Event.Signal();
}
bool GpgConnector::Decrypt(LViewI *Target, LAutoStreamI Data, LString Password, LMessage::Param UserVal)
{
if (!Target
#if LGI_VIEW_HANDLE
|| !Target->Handle()
#endif
)
{
LAssert(!"Invalid target.");
return false;
}
if (!d->Lock(_FL))
return false;
GpgJob *j = new GpgJob(GpgJob::JobDecrypt);
if (j)
{
j->Owner = Target;
j->Msg = Data;
j->Password = Password;
j->UserValue = UserVal;
d->AddWork(j);
}
d->Unlock();
return d->Event.Signal();
}
////////////////////////////////////////////////////////////////////////////////////////////////
struct MailUiGpgPriv
{
struct UserPassword
{
uint64 Ts;
LString Email;
LString Password;
};
// Objs
ScribeWnd *App;
MailUi *Ui;
// UI
LCheckBox *Enc;
LCheckBox *Sign;
LCheckBox *Attach;
LTextLabel *Msg;
KeyArrAuto Inf;
LTableLayout *Table;
LButton *Decrypt, *Install;
// Options
bool WritingEmail;
bool Encrypted;
bool Signed;
// Passwords
LArray Psw;
MailUiGpgPriv(ScribeWnd *app, MailUi *ui, bool writingEmail)
{
App = app;
Ui = ui;
Enc = NULL;
Sign = NULL;
Attach = NULL;
Msg = NULL;
Table = NULL;
Decrypt = NULL;
Install = NULL;
WritingEmail = writingEmail;
Encrypted = false;
Signed = false;
}
AddressList *GetAddrLst()
{
AddressList *AddrLst = NULL;
Ui->GetViewById(IDC_TO, AddrLst);
return AddrLst;
}
void NotInstalled()
{
SetError(LLoadString(IDS_GNUPG_ERR_NOT_INSTALLED));
if (Install)
Install->Visible(true);
if (Enc)
Enc->Enabled(false);
if (Sign)
Sign->Enabled(false);
if (Attach)
Attach->Enabled(false);
}
void Set(const char *Str, LColour &Col)
{
if (!Msg || !Table)
{
LgiTrace("%s:%i - Can't set message.\n", _FL);
return;
}
Msg->GetCss(true)->Color(Col);
Msg->Name(Str);
LNotification note(LNotifyTableLayoutRefresh);
Table->OnNotify(Msg, note);
}
void SetError(const char *Str) { Set(Str, cError); }
void SetWarning(const char *Str) { Set(Str, cWarn); }
void SetStatus(const char *Str) { Set(Str, cTxt); }
void SetSuccess(const char *Str) { Set(Str, cGood); }
void GetPassword(LViewI *Parent, LString Addr, std::function Callback)
{
for (unsigned i=0; iDoModal([this, Dlg, Addr, Callback](auto dlg, auto ctrlId)
+ auto p = Parent->GetGView();
+ auto Dlg = new LInput(Parent, "", Msg, LLoadString(IDC_PASSWORD), true);
+ Dlg->DoModal([this, Dlg, Addr, Callback](auto dlg, auto ok)
{
- if (ctrlId)
+ if (ok)
{
UserPassword &p = Psw.New();
p.Email = Addr;
p.Password = Dlg->GetStr();
p.Ts = LCurrentTime();
if (Callback)
Callback(p.Password);
}
delete dlg;
});
}
};
LCss::Len Px(int px)
{
return LCss::Len(LCss::LenPx, (float)px);
}
MailUiGpg::MailUiGpg(ScribeWnd *App, MailUi *Ui, int ColX1, int ColX2, bool WritingEmail)
{
d = new MailUiGpgPriv(App, Ui, WritingEmail);
int TextY = 6;
#ifdef MAC
int ChkY = 1;
#else
int ChkY = 6;
#endif
if (AddView(d->Table = new LTableLayout(50)))
{
Mail *m = Ui->GetItem();
LDataI *obj = m ? m->GetObject() : NULL;
int x = 0;
LTextLabel *Txt;
auto *c = d->Table->GetCell(x++, 0);
c->Position(LCss::PosAbsolute);
c->Left(Px(ColX1 - PANEL_BORDER_PX));
c->Top(Px(TextY - PANEL_BORDER_PX));
c->Add(Txt = new LTextLabel(IDC_STATIC, ColX1, TextY, -1, -1, "GnuPG:"));
if (WritingEmail)
{
c = d->Table->GetCell(x++, 0);
c->Position(LCss::PosAbsolute);
c->Left(Px(ColX2 - PANEL_BORDER_PX + 1));
c->Top(Px(ChkY - PANEL_BORDER_PX));
c->Add(d->Enc = new LCheckBox(IDC_ENCRYPT, 0, 0, -1, -1, LLoadString(IDS_GNUPG_ENCRYPT)));
c = d->Table->GetCell(x++, 0);
c->PaddingTop(Px(ChkY - PANEL_BORDER_PX));
c->Add(d->Sign = new LCheckBox(IDC_SIGN, 0, 0, -1, -1, LLoadString(IDS_GNUPG_SIGN)));
c = d->Table->GetCell(x++, 0);
c->PaddingTop(Px(ChkY - PANEL_BORDER_PX));
c->Add(d->Attach = new LCheckBox(IDC_ATTACH_PUB_KEY, 0, 0, -1, -1, LLoadString(IDS_GNUPG_ATTACH_PUB_KEY)));
}
else
{
c = d->Table->GetCell(x++, 0);
c->Position(LCss::PosAbsolute);
c->Left(Px(ColX2 - PANEL_BORDER_PX + 1));
if (c->Add(d->Decrypt = new LButton(IDC_DECRYPT, 0, 0, -1, -1, LLoadString(IDS_GNUPG_DECRYPT))))
{
LDataPropI *seg = obj ? obj->GetObj(FIELD_MIME_SEG) : NULL;
auto mimeType = seg ? seg->GetStr(FIELD_MIME_TYPE) : NULL;
if (mimeType)
{
d->Signed = !_stricmp(mimeType, sMultipartSigned);
d->Encrypted = !_stricmp(mimeType, sMultipartEncrypted);
}
d->Decrypt->Enabled(d->Encrypted);
}
}
c = d->Table->GetCell(x++, 0);
c->PaddingTop(Px(TextY - PANEL_BORDER_PX));
c->PaddingLeft(LCss::Len(LCss::LenEm, 1.0f));
c->Add(d->Msg = new LTextLabel(IDC_MSG, 0, 0, 300, -1, "..."));
c = d->Table->GetCell(x++, 0);
c->TextAlign(LCss::AlignRight);
if ((d->Install = new LButton(IDC_INSTALL, 0, 0, -1, -1, LLoadString(IDS_GNUPG_GET_GPG))))
{
d->Install->Visible(false);
c->Add(d->Install);
}
if (d->Encrypted)
{
d->SetWarning("Message is encrypted.");
}
}
}
MailUiGpg::~MailUiGpg()
{
delete d;
if (GetParent())
{
LNotification note(LNotifyItemDelete);
GetParent()->OnNotify(this, note);
}
}
void MailUiGpg::OnRecipientChange()
{
if (!d->WritingEmail || !d->Enc || !d->Sign)
return; // Don't care...
AddressList *AddrLst = d->GetAddrLst();
if (!AddrLst)
{
d->SetError("No AddressList.");
return;
}
GpgConnector *Gpg = d->App->GetGpgConnector();
if (!Gpg)
{
d->NotInstalled();
return;
}
List a;
if (!AddrLst->GetAll(a))
{
d->SetStatus(LLoadString(IDS_GNUPG_ERR_NO_RECIP));
return;
}
if (d->Enc->Value() ||
d->Sign->Value())
{
// Do we have public keys for each of the recipients?
d->SetWarning(LLoadString(IDS_GNUPG_CHECKING));
LString::Array Emails;
for (auto i: a)
{
if (i->sAddr)
Emails.New() = i->sAddr;
}
LCombo *cbo;
if (d->Ui->GetViewById(IDC_FROM, cbo))
{
const char *Frm = cbo->Name();
LAutoString Name, Email;
DecodeAddrName(Frm, Name, Email, NULL);
if (Email)
Emails.New() = Email;
}
Gpg->GetKeyInfo(this, Emails);
}
else
{
d->SetStatus(LLoadString(IDS_GNUPG_ERR_NO_SIGN_ENC));
// Revert list items to no colour...
for (auto i: a)
{
i->SetInt(FIELD_COLOUR, cDefaultListItemColour);
i->Update();
}
}
}
// This gets all the events from MailUi::OnNotify as well as any events
// from MailUiGpg's child controls.
// \returns non-zero if further processing in MailUi::OnNotify should be blocked.
int MailUiGpg::OnNotify(LViewI *Ctrl, LNotification n)
{
switch (Ctrl->GetId())
{
case IDC_TO:
{
switch (n.Type)
{
case LNotifyItemInsert:
case LNotifyItemDelete:
// Recipients changed... check for keys...
OnRecipientChange();
break;
default:
break;
}
break;
}
case IDC_ENCRYPT:
case IDC_SIGN:
{
OnRecipientChange();
break;
}
case IDC_DECRYPT:
{
Decrypt(NULL);
break;
}
case IDC_INSTALL:
{
LExecute(GpgInstall);
break;
}
}
return 0;
}
void DeleteChildSegments(LDataPropI *d)
{
LDataIt It = d->GetList(FIELD_MIME_SEG);
for (unsigned i=0; iLength(); )
{
LDataPropI *c = (*It)[i];
DeleteChildSegments(c);
LDataI *cdi = dynamic_cast(c);
if (cdi)
{
// This deletes the on disk representation.
cdi->Delete();
// This removes the object from the segment tree and
// frees the memory
delete cdi;
}
else
{
LgiTrace("%s:%i - DeleteChildSegments: Wrong object type.\n", _FL);
LAssert(0);
i++; // Skip this? Maybe we should break?
}
}
}
LString MailUiGpg::GetPublicKey(const char *Email)
{
LString s;
if (Email)
{
LString Args;
Args.Printf("-a --export %s", Email);
LSubProcess Proc(GpgBinPath, Args);
if (!Proc.Start(true, false))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_CANT_START));
return 1;
}
ssize_t r;
char Buf[256] = "";
while ((r = Proc.Read(Buf, sizeof(Buf)-1)) > 0)
{
Buf[r] = 0;
s += Buf;
}
int Result = Proc.Wait();
if (Result)
{
LString Msg;
Msg.Printf(LLoadString(IDS_GNUPG_ERR_CODE), Result);
d->SetError(Buf[0] ? Buf : Msg);
s.Empty();
}
}
return s;
}
bool MailUiGpg::ReadFile(LArray &Data, const char *Path)
{
LFile f;
if (!f.Open(Path, O_READ))
{
LString s;
s.Printf(LLoadString(IDS_ERROR_CANT_READ), Path);
d->SetError(s);
return false;
}
if (f.GetSize() < 0)
{
LString s;
s.Printf(LLoadString(IDS_GNUPG_ERR_NO_CONTENT), Path);
d->SetError(s);
return false;
}
if (!Data.Length((uint32_t)f.GetSize()))
{
d->SetError("Memory alloc failed.");
return 1;
}
if (f.Read(&Data[0], Data.Length()) != Data.Length())
{
LString s;
s.Printf(LLoadString(IDS_GNUPG_ERR_READ_FAIL), Path);
d->SetError(s);
return false;
}
return true;
}
void MailUiGpg::Decrypt(std::function callback)
{
// Get the connector...
GpgConnector *Conn = d->App->GetGpgConnector();
if (!Conn)
{
d->NotInstalled();
DecryptStatus(1);
}
// Find the right attachment...
Mail *m = d->Ui->GetItem();
if (!m)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_MAIL));
DecryptStatus(1);
}
LDataPropI *Root = m->GetObject()->GetObj(FIELD_MIME_SEG);
if (!Root)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_ROOT));
DecryptStatus(1);
}
auto Mt = Root->GetStr(FIELD_MIME_TYPE);
if (!Mt || _stricmp(Mt, sMultipartEncrypted))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_WRONG_MIME));
DecryptStatus(1);
}
LDataI *EncryptedObj = NULL;
LArray Objs;
if (!m->GetAttachmentObjs(Objs))
DecryptStatus(1);
for (unsigned i=0; iGetStr(FIELD_MIME_TYPE);
if (Mt && !_stricmp(Mt, sAppOctetStream))
{
EncryptedObj = Objs[i];
break;
}
}
if (!EncryptedObj)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_ENC_MIME));
DecryptStatus(1);
}
// Get the password for decryption...
LHashTbl,ScribeAccount*> Map;
for (auto a : *m->App->GetAccounts())
{
LVariant v = a->Identity.Email();
if (v.Str())
{
char *Email = v.Str();
if (Email)
Map.Add(Email, a);
}
}
LDataIt ToLst = m->GetTo();
LString::Array ToEmail;
if (ToLst)
{
for (LDataPropI *t = ToLst->First(); t; t = ToLst->Next())
{
auto Email = t->GetStr(FIELD_EMAIL);
if (Email)
{
if (Map.Find(Email))
ToEmail.New() = Email;
}
}
}
if (ToEmail.Length() == 0)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_ID));
DecryptStatus(1);
}
d->GetPassword(d->Ui, ToEmail[0], [this, callback, EncryptedObj, Conn, m](auto Pass)
{
if (!Pass)
{
d->SetWarning(LLoadString(IDS_GNUPG_DECRYPT_CANCEL));
DecryptStatus(1);
}
// Send the file to by decrypted...
LAutoStreamI Data = EncryptedObj->GetStream(_FL);
if (!Data)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_DATA));
DecryptStatus(1);
}
if (!Conn->Decrypt(this, Data, Pass, (LMessage::Param) m))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_DECRYPT_FAIL));
DecryptStatus(1);
}
DecryptStatus(0);
});
}
void MailUiGpg::SignEncrypt(bool uSign, bool uEncrypt, bool uAttachPublicKey, std::function callback)
{
// Save the message normally
Mail *m = d->Ui->GetItem();
bool IsInPublicFolder = m->GetFolder() && m->GetFolder()->IsPublicFolders();
if (IsInPublicFolder)
{
LDateTime n;
n.SetNow();
m->SetDateSent(&n);
m->Update();
}
d->Ui->OnDataEntered();
d->Ui->OnSave();
LDataI *Root = dynamic_cast(m->GetObject()->GetObj(FIELD_MIME_SEG));
if (!Root)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_ROOT));
DecryptStatus(1);
}
// Check that the send has a private key setup...
LString FromEmail = m->GetFromStr(FIELD_EMAIL);
if (!FromEmail)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_SENDER_EMAIL));
DecryptStatus(1);
}
LString PrivKeyId;
if (d->Inf)
{
for (unsigned i=0; iInf->Length(); i++)
{
GpgConnector::KeyInfo &ki = (*d->Inf)[i];
if (!_stricmp(ki.Email, FromEmail))
{
PrivKeyId = ki.KeyId;
break;
}
}
}
if (!PrivKeyId)
{
LString s;
s.Printf(LLoadString(IDS_GNUPG_ERR_NO_PRIV_KEY), FromEmail.Get());
d->SetError(s);
DecryptStatus(1);
}
if (uAttachPublicKey)
{
LString PubKey = GetPublicKey(FromEmail);
if (!PubKey)
{
LString s;
s.Printf(LLoadString(IDS_GNUPG_ERR_NO_PUB_KEY), FromEmail.Get());
d->SetError(s);
DecryptStatus(1);
}
Attachment *a = new Attachment(m->App);
if (a)
{
LAutoStreamI Data(new LMemStream(PubKey, PubKey.Length()));
if (Data)
{
if (!a->ImportStream("public-key.asc", "application/pgp-keys", Data))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_IMPORT_FAIL));
DecryptStatus(1);
}
else
{
m->AttachFile(a);
}
}
else
{
d->SetError("Allocation failed.");
DecryptStatus(1);
}
}
}
// Re-write the MIME hierarchy to have the message and attachments encrypted
// 1) Get the password
d->GetPassword(d->Ui, FromEmail.Get(),
[this, callback, InputRoot=Root, uSign, uEncrypt, m, PrivKeyId](auto Psw)
{
LDataI *LocalRoot = InputRoot;
if (!Psw)
{
d->SetStatus(LLoadString(IDS_GNUPG_ERR_SIGN_ENC_CANCEL));
DecryptStatus(1);
}
// 1) Export the message to a file:
const char *BaseName = "encrypted.asc";
LFile::Path p = ScribeTempPath();
p += BaseName;
LFile f;
if (!f.Open(p, O_READWRITE))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_TEMP_WRITE));
DecryptStatus(1);
}
f.SetSize(0);
f.SetPos(0);
LMime Mime(ScribeTempPath());
Store3ToLMime(&Mime, LocalRoot);
if (!Mime.GetBoundary())
{
// No boundary... so set it and propagate the change back
char b[64];
CreateMimeBoundary(b, sizeof(b));
Mime.SetBoundary(b);
LocalRoot->SetStr(FIELD_INTERNET_HEADER, Mime.GetHeaders());
// If we don't do this then LMime will create it again later
// when we actually go to send the message, but it will be
// different then and the signing will fail.
}
if (!Mime.Text.Encode.Push(&f))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_EXPORT_TEMP));
DecryptStatus(1);
}
#if 1
if (uSign)
{
// Remove any white space from the end of the file... this is
// to make sure the signing process is standardized. See
// https://www.ietf.org/rfc/rfc3156.txt
// Part 5: OpenPGP signed data
// This is probably not very efficient but it's usually only the
// 2 bytes: "\r\n"
int64 Size, Pos;
while ( (Size = f.GetSize()) > 0)
{
Pos = f.SetPos(Size-1);
if (Pos != Size - 1)
break;
char c;
ssize_t Rd = f.Read(&c, 1);
if (Rd == 1 &&
strchr(WhiteSpace, c))
{
f.SetSize(Size - 1);
}
else break;
}
}
#endif
f.Close();
if (!uEncrypt)
{
// Just signing... move MIME tree into child node
LDataI *NewRoot = LocalRoot->GetStore()->Create(MAGIC_ATTACHMENT);
if (!NewRoot)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NEW_ATTACH_FAIL));
DecryptStatus(1);
}
// Copy over the root node headers
NewRoot->SetStr(FIELD_INTERNET_HEADER, LocalRoot->GetStr(FIELD_INTERNET_HEADER));
// Reparent the old root to the new root, and then attach that to the message...
if (!LocalRoot->Save(NewRoot) ||
!m->GetObject()->SetObj(FIELD_MIME_SEG, NewRoot))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_REPARENT));
DecryptStatus(1);
}
LocalRoot = NewRoot;
}
// 2) Encrypt/sign the file:
LString InFile(p);
p--;
p += "encrypted.gpg";
LString OutFile(p);
if (LFileExists(OutFile))
{
FileDev->Delete(OutFile, false);
}
LString Args, s;
Args.Printf("--batch --passphrase-fd 0 -u 0x%s",
PrivKeyId.Get());
if (uEncrypt)
{
LDataIt To = m->GetTo();
for (LDataPropI *Recip = To->First(); Recip; Recip = To->Next())
{
auto Email = Recip->GetStr(FIELD_EMAIL);
LAssert(Email != NULL);
if (Email)
{
s.Printf(" --recipient %s", Email);
Args += s;
}
else
{
d->SetError(LLoadString(IDS_GNUPG_ERR_RECIP_NO_EMAIL));
DecryptStatus(1);
}
}
}
s.Printf(" --armor -o \"%s\" %s \"%s\"",
OutFile.Get(),
uSign && uEncrypt ? "-se" : (uSign ? "--detach-sign" : "-e"),
InFile.Get());
Args += s;
LSubProcess Proc(GpgBinPath, Args);
char Buf[256];
if (!Proc.Start(true, true))
{
d->SetError(LLoadString(IDS_GNUPG_ERR_CANT_START));
DecryptStatus(1);
}
LString PswStr;
PswStr.Printf("%s\n", Psw.Get());
ssize_t w = Proc.Write(PswStr.Get(), PswStr.Length());
if (w < 0)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_WRITE));
DecryptStatus(1);
}
ssize_t r = Proc.Read(Buf, sizeof(Buf)-1);
Buf[MAX(r, 0)] = 0;
int Result = Proc.Wait();
if (Result)
{
d->SetError(Buf[0] ? Buf : LLoadString(IDS_GNUPG_ERR_ENCRYPT_FAIL));
DecryptStatus(1);
}
// 3) Import the encrypted message and replace contents of MIME tree.
LArray InData, OutData;
if (!ReadFile(OutData, OutFile))
DecryptStatus(1);
#ifndef _DEBUG
// Clean up temporary files...
FileDev->Delete(InFile, false);
FileDev->Delete(OutFile, false);
#endif
if (uEncrypt)
{
// Clear out all existing attachments...
DeleteChildSegments(LocalRoot);
}
// Setup the root MIME node to have the right type and fields...
LAutoStreamI Data;
{
// By using a LMime object we preserve the existing headers in the MIME
// segment while still being able to change the MIME type and charset.
LMime Tmp;
Tmp.SetHeaders(LocalRoot->GetStr(FIELD_INTERNET_HEADER));
Tmp.SetMimeType(uSign ? sMultipartSigned : sMultipartEncrypted);
Tmp.SetCharset("utf-8");
Tmp.SetSub( "Content-Type",
"protocol",
uEncrypt ? "application/pgp-encrypted" : "application/pgp-signature");
LocalRoot->SetStr(FIELD_INTERNET_HEADER, Tmp.GetHeaders());
// Set a body message
const char *BodyMsg = "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\n";
Data.Reset(new LMemStream(BodyMsg, strlen(BodyMsg)));
LocalRoot->SetStream(Data);
LocalRoot->Save();
}
if (uEncrypt)
{
// Attach some app info...
LDataI *AppInfo = LocalRoot->GetStore()->Create(MAGIC_ATTACHMENT);
if (!AppInfo)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NEW_ATTACH_FAIL));
DecryptStatus(1);
}
AppInfo->SetStr(FIELD_MIME_TYPE, "application/pgp-encrypted");
const char *AppInfoMsg = "Version: 1\n";
Data.Reset(new LMemStream(AppInfoMsg, strlen(AppInfoMsg)));
AppInfo->SetStream(Data);
AppInfo->Save(LocalRoot);
}
{
// Attach the new data to the email...
LDataI *File = LocalRoot->GetStore()->Create(MAGIC_ATTACHMENT);
if (!File)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NEW_ATTACH_FAIL));
DecryptStatus(1);
}
if (uEncrypt)
{
// Attach the encrypted data here...
LMime Tmp;
Tmp.SetMimeType(sAppOctetStream);
Tmp.SetFileName(BaseName);
Tmp.Set("Content-Disposition", "inline");
Tmp.SetSub("Content-Disposition", "filename", BaseName);
File->SetStr(FIELD_INTERNET_HEADER, Tmp.GetHeaders());
}
else // uSign
{
// Set up the signature attachment
LMime Tmp;
Tmp.SetMimeType("application/pgp-signature");
Tmp.SetFileName(BaseName);
Tmp.Set("Content-Description", "OpenPGP digital signature");
Tmp.Set("Content-Disposition", "attachment");
Tmp.SetSub("Content-Disposition", "filename", BaseName);
File->SetStr(FIELD_INTERNET_HEADER, Tmp.GetHeaders());
}
Data.Reset(new LMemStream(&OutData[0], OutData.Length()));
File->SetStream(Data);
File->Save(LocalRoot);
}
// Tell the UI that the object has changed...
LArray ChangeArr;
ChangeArr.Add(m->GetObject());
d->App->SetContext(_FL);
d->App->OnChange(ChangeArr, 0);
DecryptStatus(0);
});
}
-void MailUiGpg::DoCommand(int Cmd, std::function callback)
+void MailUiGpg::DoCommand(
+ int Cmd,
+ /// This callback will do the normal command processing...
+ /// In the case where we encrypt / sign we replace the normal
+ /// processing and don't do it at all... thus NOT calling the
+ /// callback.
+ std::function callback)
{
switch (Cmd)
{
case IDM_SEND_MSG:
{
if (!d ||
!d->Enc ||
!d->Sign ||
!d->Attach ||
!d->Ui)
{
// Resending an old mail.
// There is no Enc/Sign UI.
DecryptStatus(0);
}
if (!d->Enc->Value() && !d->Sign->Value())
{
- DecryptStatus(0); // No need to sign &| encrypt
+ DecryptStatus(0); // No need to sign &| encrypt, but do the normal processing
}
- Mail *m = d->Ui->GetItem();
+ auto m = d->Ui->GetItem();
if (!m)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NOMSG));
- DecryptStatus(1);
+ DecryptStatus(1); // Don't do normal processing of the cmd
}
SignEncrypt(d->Sign->Value() != 0,
d->Enc->Value() != 0,
d->Attach->Value() != 0,
- [this, m, callback](auto status)
- {
- if (!status)
- {
- // Send the email..
- m->Send(true);
+ [this, m](auto status)
+ {
+ if (!status)
+ {
+ // Send the email..
+ m->Send(true);
- // Close the window...
- d->Ui->Quit();
- }
-
- // Bypass the normal code
- DecryptStatus(1);
- });
+ // Close the window... but after we clean up.
+ d->Ui->PostEvent(M_CLOSE);
+ }
+ });
+
+ // Don't call the callback as we're not doing the normal processing of this command.
+ return;
}
}
- DecryptStatus(0);
+ DecryptStatus(0); // Do the default processing
}
LMessage::Result MailUiGpg::OnEvent(LMessage *Msg)
{
switch (Msg->Msg())
{
case M_GNUPG_KEY_INFO:
{
d->Inf.Reset( (KeyArr*) Msg->B() );
AddressList *AddrLst = d->GetAddrLst();
if (d->Inf && AddrLst)
{
int NoKey = 0;
List a;
if (AddrLst->GetAll(a))
{
LHashTbl, GpgConnector::KeyInfo*> Map;
for (unsigned i=0; iInf->Length(); i++)
{
GpgConnector::KeyInfo *ki = &(*d->Inf)[i];
Map.Add(ki->Email, ki);
}
for (auto i: a)
{
// Check if this recipient has a key...
GpgConnector::KeyInfo *ki = (i->sAddr) ? Map.Find(i->sAddr) : NULL;
i->SetInt(FIELD_COLOUR, ki ? cDefaultListItemColour : cError.c32());
i->Update();
if (!ki)
NoKey++;
}
}
if (NoKey > 0)
d->SetError(LLoadString(IDS_GNUPG_ERR_ONE_OR_MORE));
else if (d->Enc && d->Sign)
{
if (d->Enc->Value() && d->Sign->Value())
d->SetSuccess(LLoadString(IDS_GNUPG_ENCRYPTED_AND_SIGNED));
else if (d->Enc->Value())
d->SetSuccess(LLoadString(IDS_GNUPG_ENCRYPTED));
else if (d->Sign->Value())
d->SetSuccess(LLoadString(IDS_GNUPG_SIGNED));
else
LAssert(0);
}
else LAssert(0);
}
else d->SetError("Parameter error.");
break;
}
case M_GNUPG_SIG_CHECK:
{
Mail *m = d->Ui ? d->Ui->GetItem() : NULL;
LAutoPtr Resp((GpgSigCheckResponse*)Msg->A());
if (!Resp || m != (Mail*)Resp->UserValue)
{
d->SetError("M_GNUPG_SIG_CHECK: bad response");
break;
}
if (Resp->Error)
{
d->SetError(Resp->Error);
break;
}
LString Dt = Resp->TimeStamp.Get();
const char *Type = Resp->SignatureMatch ? LLoadString(IDS_GNUPG_GOOD_SIG) : LLoadString(IDS_GNUPG_BAD_SIG);
LString Msg;
if (Resp->Identity)
Msg.Printf(LLoadString(IDS_GNUPG_SIG_MSG), Type, Resp->Identity.Get(), Dt.Get());
else
Msg.Printf(LLoadString(IDS_GNUPG_SIG_NO_ID), Type, Dt.Get());
if (Resp->SignatureMatch)
d->SetSuccess(Msg);
else
d->SetError(Msg);
break;
}
case M_GNUPG_DECRYPT:
{
LAutoPtr Resp((GpgDecryptResponse*)Msg->A());
if (!Resp)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_INVALID_DECRYPTION));
break;
}
if (!Resp->Data)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NO_OUTPUT));
break;
}
if (Resp->Error)
{
d->SetError(Resp->Error);
break;
}
Mail *m = d->Ui->GetItem();
if (!m || m != (Mail*)Resp->UserValue)
{
d->SetError("Incorrect mail object after decryption.");
break;
}
d->SetSuccess(LLoadString(IDS_GNUPG_DECRYPT_OK));
LDataI *Obj = m->GetObject();
if (!Obj)
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NOMSG));
break;
}
// This turns of notification processing, the message is changing
// but we don't want it to get dirty.
d->Ui->_Running = false;
// Unload the attachments, because they will point to stale back-end
// objects, that "SetStream" will delete.
m->UnloadAttachments();
// Set the stream of the object (which will MIME parse the data,
// replacing the MIME Seg tree)
Obj->SetStream(Resp->Data);
// Re-enable the notifcation processing.
d->Ui->_Running = true;
// Get the UI to reload and display the object...
LArray Items;
Items.Add(Obj);
m->App->SetContext(_FL);
m->App->OnChange(Items, 0);
// Change the button to disabled... no need for it anymore.
SetCtrlEnabled(IDC_DECRYPT, false);
break;
}
}
return LView::OnEvent(Msg);
}
bool MailUiGpg::Pour(LRegion &r)
{
LRect lrg = FindLargest(r);
if (!lrg.Valid())
return false;
int y = LSysFont->GetHeight() + 12;
lrg.y2 = lrg.y1 + MIN(y, lrg.Y()) - 1;
SetPos(lrg);
if (d->Table)
{
LRect c = GetClient();
c.Inset(PANEL_BORDER_PX, PANEL_BORDER_PX);
d->Table->SetPos(c);
}
return true;
}
void MailUiGpg::OnCreate()
{
AttachChildren();
LResources::StyleElement(this);
// This is in OnCreate because the CheckSignature needs a valid
// view handle to post the message back to us.
if (d->Signed)
{
GpgConnector *Conn = d->App->GetGpgConnector();
if (Conn)
{
Mail *m = d->Ui->GetItem();
LDataI *obj = m ? m->GetObject() : NULL;
LAutoStreamI Msg;
if (obj)
Msg = obj->GetStream(_FL);
if (Msg)
{
Conn->CheckSignature(this, Msg, (LMessage::Param)m);
d->SetWarning(LLoadString(IDS_GNUPG_CHECK_MSG));
}
else
{
d->SetError(LLoadString(IDS_GNUPG_ERR_NOMSG));
}
}
else
{
d->NotInstalled();
}
}
}
void MailUiGpg::OnPaint(LSurface *pDC)
{
LCssTools Tools(this);
LRect c = GetClient();
Tools.PaintBorder(pDC, c);
Tools.PaintPadding(pDC, c);
Tools.PaintContent(pDC, c);
}
diff --git a/win/Scribe_vs2019.sln b/win/Scribe_vs2019.sln
--- a/win/Scribe_vs2019.sln
+++ b/win/Scribe_vs2019.sln
@@ -1,83 +1,83 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32413.511
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Scribe", "Scribe_vs2019.vcxproj", "{540DDE64-0927-4E67-B927-2E3BE7ECB029}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Lgi", "..\..\..\lgi\trunk\win\Lgi_vs2019.vcxproj", "{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mapi", "..\mapi\ScribeMapi_vs2019.vcxproj", "{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScribeMapi", "..\mapi\ScribeMapi_vs2019.vcxproj", "{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LgiStatic", "..\..\..\Lgi\trunk\win\LgiStatic_vs2019.vcxproj", "{DE6D97B9-A194-401B-A8A4-83110C56D572}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Updater", "..\..\..\Lgi\trunk\utils\Updater\Updater_vs2019.vcxproj", "{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aspell-dist-0.60", "..\..\libs\aspell-0.60.6.1\win32\dist\aspell-dist-0.60-vs2019.vcxproj", "{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzip2-1.0.6", "..\..\libs\bzip2-1.0.6\windows\bzip2-1.0.6-vs2019.vcxproj", "{E5575141-C1A2-4997-BADB-7584CF546E1F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libntlm", "..\..\..\Lgi\trunk\src\common\Net\libntlm-0.4.2\build-win32\libntlm_vs2019.vcxproj", "{F97AAACA-BF41-46B8-B534-A1639589A1A3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
ReleaseNoOptimize|x64 = ReleaseNoOptimize|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{540DDE64-0927-4E67-B927-2E3BE7ECB029}.Debug|x64.ActiveCfg = Debug|x64
{540DDE64-0927-4E67-B927-2E3BE7ECB029}.Debug|x64.Build.0 = Debug|x64
{540DDE64-0927-4E67-B927-2E3BE7ECB029}.Release|x64.ActiveCfg = Release|x64
{540DDE64-0927-4E67-B927-2E3BE7ECB029}.Release|x64.Build.0 = Release|x64
{540DDE64-0927-4E67-B927-2E3BE7ECB029}.ReleaseNoOptimize|x64.ActiveCfg = ReleaseNoOptimize|x64
{540DDE64-0927-4E67-B927-2E3BE7ECB029}.ReleaseNoOptimize|x64.Build.0 = ReleaseNoOptimize|x64
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Debug|x64.ActiveCfg = Debug|x64
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Debug|x64.Build.0 = Debug|x64
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Release|x64.ActiveCfg = Release|x64
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.Release|x64.Build.0 = Release|x64
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.ReleaseNoOptimize|x64.ActiveCfg = ReleaseNoOptimize|x64
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}.ReleaseNoOptimize|x64.Build.0 = ReleaseNoOptimize|x64
{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}.Debug|x64.ActiveCfg = Debug|x64
{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}.Debug|x64.Build.0 = Debug|x64
{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}.Release|x64.ActiveCfg = Release|x64
{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}.Release|x64.Build.0 = Release|x64
{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}.ReleaseNoOptimize|x64.ActiveCfg = Release|x64
{148574CE-4DA6-4B8A-9EC6-FF68ADC2A046}.ReleaseNoOptimize|x64.Build.0 = Release|x64
{DE6D97B9-A194-401B-A8A4-83110C56D572}.Debug|x64.ActiveCfg = Debug|x64
{DE6D97B9-A194-401B-A8A4-83110C56D572}.Debug|x64.Build.0 = Debug|x64
{DE6D97B9-A194-401B-A8A4-83110C56D572}.Release|x64.ActiveCfg = Release|x64
{DE6D97B9-A194-401B-A8A4-83110C56D572}.Release|x64.Build.0 = Release|x64
{DE6D97B9-A194-401B-A8A4-83110C56D572}.ReleaseNoOptimize|x64.ActiveCfg = Release|x64
{DE6D97B9-A194-401B-A8A4-83110C56D572}.ReleaseNoOptimize|x64.Build.0 = Release|x64
{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}.Debug|x64.ActiveCfg = Debug|x64
{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}.Debug|x64.Build.0 = Debug|x64
{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}.Release|x64.ActiveCfg = Release|x64
{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}.Release|x64.Build.0 = Release|x64
{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}.ReleaseNoOptimize|x64.ActiveCfg = Release|x64
{11E9238B-921E-44E5-A3D2-61C5BFA7CC6C}.ReleaseNoOptimize|x64.Build.0 = Release|x64
{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}.Debug|x64.ActiveCfg = Debug|x64
{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}.Debug|x64.Build.0 = Debug|x64
{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}.Release|x64.ActiveCfg = Release|x64
{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}.Release|x64.Build.0 = Release|x64
{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}.ReleaseNoOptimize|x64.ActiveCfg = Release|x64
{1509FE6D-0B3A-422A-99D1-E26A26CE8DA5}.ReleaseNoOptimize|x64.Build.0 = Release|x64
{E5575141-C1A2-4997-BADB-7584CF546E1F}.Debug|x64.ActiveCfg = Debug|x64
{E5575141-C1A2-4997-BADB-7584CF546E1F}.Debug|x64.Build.0 = Debug|x64
{E5575141-C1A2-4997-BADB-7584CF546E1F}.Release|x64.ActiveCfg = Release|x64
{E5575141-C1A2-4997-BADB-7584CF546E1F}.Release|x64.Build.0 = Release|x64
{E5575141-C1A2-4997-BADB-7584CF546E1F}.ReleaseNoOptimize|x64.ActiveCfg = Release|x64
{E5575141-C1A2-4997-BADB-7584CF546E1F}.ReleaseNoOptimize|x64.Build.0 = Release|x64
{F97AAACA-BF41-46B8-B534-A1639589A1A3}.Debug|x64.ActiveCfg = Debug|x64
{F97AAACA-BF41-46B8-B534-A1639589A1A3}.Debug|x64.Build.0 = Debug|x64
{F97AAACA-BF41-46B8-B534-A1639589A1A3}.Release|x64.ActiveCfg = Release|x64
{F97AAACA-BF41-46B8-B534-A1639589A1A3}.Release|x64.Build.0 = Release|x64
{F97AAACA-BF41-46B8-B534-A1639589A1A3}.ReleaseNoOptimize|x64.ActiveCfg = ReleaseNoOptimize|x64
{F97AAACA-BF41-46B8-B534-A1639589A1A3}.ReleaseNoOptimize|x64.Build.0 = ReleaseNoOptimize|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {269D6B2E-2EA7-4141-8BC5-FA6B1F7DED92}
EndGlobalSection
EndGlobal