@@ -3,9 +3,11 @@ package main
3
3
4
4
import (
5
5
"bufio"
6
+ "encoding/csv"
6
7
"errors"
7
8
"fmt"
8
9
"os"
10
+ "os/exec"
9
11
"path/filepath"
10
12
"strconv"
11
13
"strings"
@@ -627,3 +629,351 @@ func decryptDatabase(dbPath string) (error, string) {
627
629
628
630
return err , passwd
629
631
}
632
+
633
+ // Export data to a varity of file types
634
+ func exportToFile (fileName string ) error {
635
+
636
+ var err error
637
+ ext := filepath .Ext (fileName )
638
+
639
+ switch strings .ToLower (ext ) {
640
+ case ".csv" :
641
+ err = exportToCsv (fileName )
642
+ case ".md" :
643
+ err = exportToMarkdown (fileName )
644
+ case ".html" :
645
+ err = exportToHTML (fileName )
646
+ case ".pdf" :
647
+ err = exportToPDF (fileName )
648
+ default :
649
+ return fmt .Errorf ("format %s not supported" , ext )
650
+ }
651
+
652
+ if err != nil {
653
+ fmt .Printf ("Error exporting to \" %s\" - \" %s\" \n " , fileName , err .Error ())
654
+ return err
655
+ } else {
656
+ if _ , err = os .Stat (fileName ); err == nil {
657
+ fmt .Printf ("Exported to %s.\n " , fileName )
658
+ // Chmod 600
659
+ os .Chmod (fileName , 0600 )
660
+ return nil
661
+ }
662
+ }
663
+
664
+ return err
665
+ }
666
+
667
+ // Export current database to markdown
668
+ func exportToMarkdown (fileName string ) error {
669
+
670
+ var err error
671
+ var dataArray [][]string
672
+ var fh * os.File
673
+ var maxLengths [7 ]int
674
+ var headers []string = []string {" ID " , " Title " , " User " , " URL " , " Password " , " Notes " , " Modified " }
675
+
676
+ err , dataArray = entriesToStringArray (false )
677
+
678
+ if err != nil {
679
+ fmt .Printf ("Error exporting entries to string array - \" %s\" \n " , err .Error ())
680
+ return err
681
+ }
682
+
683
+ for _ , record := range dataArray {
684
+ for idx , field := range record {
685
+
686
+ if len (field ) > maxLengths [idx ] {
687
+ maxLengths [idx ] = len (field )
688
+ }
689
+ }
690
+ }
691
+
692
+ // fmt.Printf("%+v\n", maxLengths)
693
+ fh , err = os .Create (fileName )
694
+ if err != nil {
695
+ fmt .Printf ("Cannt open \" %s\" for writing - \" %s\" \n " , fileName , err .Error ())
696
+ return err
697
+ }
698
+
699
+ defer fh .Close ()
700
+
701
+ writer := bufio .NewWriter (fh )
702
+
703
+ // Write markdown header
704
+ for idx , length := range maxLengths {
705
+ delta := length - len (headers [idx ])
706
+ // fmt.Printf("%d\n", delta)
707
+ if delta > 0 {
708
+ for i := 0 ; i < delta + 2 ; i ++ {
709
+ headers [idx ] += " "
710
+ }
711
+ }
712
+ }
713
+
714
+ writer .WriteString (" |" + strings .Join (headers , "|" ) + "|\n " )
715
+
716
+ // Write line separator
717
+ writer .WriteString (" | " )
718
+ for _ , length := range maxLengths {
719
+
720
+ for i := 0 ; i < length ; i ++ {
721
+ writer .WriteString ("-" )
722
+ }
723
+ writer .WriteString (" | " )
724
+ }
725
+ writer .WriteString ("\n " )
726
+
727
+ // Write records
728
+ for _ , record := range dataArray {
729
+ writer .WriteString (" | " )
730
+ for _ , field := range record {
731
+ writer .WriteString (field + " | " )
732
+ }
733
+ writer .WriteString ("\n " )
734
+ }
735
+
736
+ writer .Flush ()
737
+
738
+ return nil
739
+
740
+ }
741
+
742
+ // This needs pandoc and pdflatex support
743
+ func exportToPDF (fileName string ) error {
744
+
745
+ var err error
746
+ var tmpFile string
747
+ var passwd string
748
+ var pdfTkFound bool
749
+
750
+ cmd := exec .Command ("which" , "pandoc" )
751
+ if _ , err = cmd .Output (); err != nil {
752
+ return errors .New ("pandoc not found" )
753
+ }
754
+
755
+ cmd = exec .Command ("which" , "pdftk" )
756
+ if _ , err = cmd .Output (); err != nil {
757
+ fmt .Printf ("pdftk not found, PDF won't be secure!\n " )
758
+ } else {
759
+ pdfTkFound = true
760
+ }
761
+
762
+ if pdfTkFound {
763
+ fmt .Printf ("Password: " )
764
+ err , passwd = readPassword ()
765
+ }
766
+
767
+ tmpFile = randomFileName (os .TempDir (), ".tmp" )
768
+ // fmt.Printf("Temp file => %s\n", tmpFile)
769
+ err = exportToMarkdownLimited (tmpFile )
770
+
771
+ if err == nil {
772
+ var args []string = []string {"-o" , fileName , "-f" , "markdown" , "-V" , "geometry:landscape" , "--columns=600" , "--pdf-engine" , "xelatex" , "--dpi=150" , tmpFile }
773
+
774
+ cmd = exec .Command ("pandoc" , args ... )
775
+ _ , err = cmd .Output ()
776
+ // Remove tmpfile
777
+ os .Remove (tmpFile )
778
+
779
+ // If the file is generated, encrypt it if pdfTkFound
780
+ if _ , err = os .Stat (fileName ); err == nil {
781
+ fmt .Printf ("\n File %s created without password.\n " , fileName )
782
+
783
+ if pdfTkFound {
784
+ tmpFile = randomFileName ("." , ".pdf" )
785
+ // fmt.Printf("pdf file => %s\n", tmpFile)
786
+ args = []string {fileName , "output" , tmpFile , "user_pw" , passwd }
787
+ cmd = exec .Command ("pdftk" , args ... )
788
+ _ , err = cmd .Output ()
789
+
790
+ if err == nil {
791
+ // Copy over
792
+ fmt .Printf ("Added password to %s.\n " , fileName )
793
+ os .Remove (fileName )
794
+ err = os .Rename (tmpFile , fileName )
795
+ } else {
796
+ fmt .Printf ("Error adding password to pdf - \" %s\" \n " , err .Error ())
797
+ }
798
+ }
799
+ }
800
+ }
801
+
802
+ return err
803
+
804
+ }
805
+
806
+ // Export current database to markdown minus the long fields
807
+ func exportToMarkdownLimited (fileName string ) error {
808
+
809
+ var err error
810
+ var dataArray [][]string
811
+ var fh * os.File
812
+ var maxLengths [6 ]int
813
+ var headers []string = []string {" ID " , " Title " , " User " , " URL " , " Password " , " Modified " }
814
+
815
+ err , dataArray = entriesToStringArray (true )
816
+
817
+ if err != nil {
818
+ fmt .Printf ("Error exporting entries to string array - \" %s\" \n " , err .Error ())
819
+ return err
820
+ }
821
+
822
+ for _ , record := range dataArray {
823
+ for idx , field := range record {
824
+
825
+ if len (field ) > maxLengths [idx ] {
826
+ maxLengths [idx ] = len (field )
827
+ }
828
+ }
829
+ }
830
+
831
+ // fmt.Printf("%+v\n", maxLengths)
832
+ fh , err = os .Create (fileName )
833
+ if err != nil {
834
+ fmt .Printf ("Cannt open \" %s\" for writing - \" %s\" \n " , fileName , err .Error ())
835
+ return err
836
+ }
837
+
838
+ defer fh .Close ()
839
+
840
+ writer := bufio .NewWriter (fh )
841
+
842
+ // Write markdown header
843
+ for idx , length := range maxLengths {
844
+ delta := length - len (headers [idx ])
845
+ // fmt.Printf("%d\n", delta)
846
+ if delta > 0 {
847
+ for i := 0 ; i < delta + 2 ; i ++ {
848
+ headers [idx ] += " "
849
+ }
850
+ }
851
+ }
852
+
853
+ writer .WriteString (" |" + strings .Join (headers , "|" ) + "|\n " )
854
+
855
+ // Write line separator
856
+ writer .WriteString (" | " )
857
+ for _ , length := range maxLengths {
858
+
859
+ for i := 0 ; i < length ; i ++ {
860
+ writer .WriteString ("-" )
861
+ }
862
+ writer .WriteString (" | " )
863
+ }
864
+ writer .WriteString ("\n " )
865
+
866
+ // Write records
867
+ for _ , record := range dataArray {
868
+ writer .WriteString (" | " )
869
+ for _ , field := range record {
870
+ writer .WriteString (field + " | " )
871
+ }
872
+ writer .WriteString ("\n " )
873
+ }
874
+
875
+ writer .Flush ()
876
+
877
+ return nil
878
+
879
+ }
880
+
881
+ // Export current database to html
882
+ func exportToHTML (fileName string ) error {
883
+
884
+ var err error
885
+ var dataArray [][]string
886
+ var fh * os.File
887
+ var headers []string = []string {" ID " , " Title " , " User " , " URL " , " Password " , " Notes " , " Modified " }
888
+
889
+ err , dataArray = entriesToStringArray (false )
890
+
891
+ if err != nil {
892
+ fmt .Printf ("Error exporting entries to string array - \" %s\" \n " , err .Error ())
893
+ return err
894
+ }
895
+
896
+ // fmt.Printf("%+v\n", maxLengths)
897
+ fh , err = os .Create (fileName )
898
+ if err != nil {
899
+ fmt .Printf ("Cannt open \" %s\" for writing - \" %s\" \n " , fileName , err .Error ())
900
+ return err
901
+ }
902
+
903
+ defer fh .Close ()
904
+
905
+ writer := bufio .NewWriter (fh )
906
+
907
+ writer .WriteString ("<html><body>\n " )
908
+ writer .WriteString ("<table cellPadding=\" 2\" cellSpacing=\" 2\" border=\" 1\" >\n " )
909
+ writer .WriteString ("<theader>\n " )
910
+
911
+ for _ , h := range headers {
912
+ writer .WriteString (fmt .Sprintf ("<th>%s</th>" , h ))
913
+ }
914
+ writer .WriteString ("</theader>\n " )
915
+ writer .WriteString ("<tbody>\n " )
916
+
917
+ // Write records
918
+ for _ , record := range dataArray {
919
+ writer .WriteString ("<tr>" )
920
+ for _ , field := range record {
921
+ writer .WriteString (fmt .Sprintf ("<td>%s</td>" , field ))
922
+ }
923
+ writer .WriteString ("</tr>\n " )
924
+ }
925
+ writer .WriteString ("</tbody>\n " )
926
+ writer .WriteString ("</table>\n " )
927
+
928
+ writer .WriteString ("</body></html>\n " )
929
+
930
+ writer .Flush ()
931
+
932
+ return nil
933
+
934
+ }
935
+
936
+ // Export current database to CSV
937
+ func exportToCsv (fileName string ) error {
938
+
939
+ var err error
940
+ var dataArray [][]string
941
+ var fh * os.File
942
+
943
+ err , dataArray = entriesToStringArray (false )
944
+
945
+ if err != nil {
946
+ fmt .Printf ("Error exporting entries to string array - \" %s\" \n " , err .Error ())
947
+ return err
948
+ }
949
+
950
+ fh , err = os .Create (fileName )
951
+ if err != nil {
952
+ fmt .Printf ("Cannt open \" %s\" for writing - \" %s\" \n " , fileName , err .Error ())
953
+ return err
954
+ }
955
+
956
+ writer := csv .NewWriter (fh )
957
+
958
+ // Write header
959
+ writer .Write ([]string {"ID" , "Title" , "User" , "URL" , "Password" , "Notes" , "Modified" })
960
+
961
+ for idx , record := range dataArray {
962
+ if err = writer .Write (record ); err != nil {
963
+ fmt .Printf ("Error writing record #%d to %s - \" %s\" \n " , idx + 1 , fileName , err .Error ())
964
+ break
965
+ }
966
+ }
967
+
968
+ writer .Flush ()
969
+
970
+ if err != nil {
971
+ return err
972
+ }
973
+
974
+ os .Chmod (fileName , 0600 )
975
+ fmt .Printf ("!WARNING: Passwords are stored in plain-text!\n " )
976
+ fmt .Printf ("Exported %d records to %s .\n " , len (dataArray ), fileName )
977
+
978
+ return nil
979
+ }
0 commit comments