keys.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. package pgp
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "os/exec"
  7. "strings"
  8. gosrc "github.com/Morganamilo/go-srcinfo"
  9. "github.com/leonelquinteros/gotext"
  10. "github.com/Jguer/yay/v12/pkg/settings/exe"
  11. "github.com/Jguer/yay/v12/pkg/text"
  12. )
  13. // pgpKeySet maps a PGP key with a list of PKGBUILDs that require it.
  14. // This is similar to stringSet, used throughout the code.
  15. type pgpKeySet map[string][]string
  16. func (set pgpKeySet) toSlice() []string {
  17. slice := make([]string, 0, len(set))
  18. for v := range set {
  19. slice = append(slice, v)
  20. }
  21. return slice
  22. }
  23. func (set pgpKeySet) set(key, p string) {
  24. // Using ToUpper to make sure keys with a different case will be
  25. // considered the same.
  26. upperKey := strings.ToUpper(key)
  27. set[upperKey] = append(set[upperKey], p)
  28. }
  29. func (set pgpKeySet) get(key string) bool {
  30. upperKey := strings.ToUpper(key)
  31. _, exists := set[upperKey]
  32. return exists
  33. }
  34. type GPGCmdBuilder interface {
  35. exe.Runner
  36. BuildGPGCmd(ctx context.Context, extraArgs ...string) *exec.Cmd
  37. }
  38. // CheckPgpKeys iterates through the keys listed in the PKGBUILDs and if needed,
  39. // asks the user whether yay should try to import them.
  40. func CheckPgpKeys(ctx context.Context, logger *text.Logger, pkgbuildDirsByBase map[string]string, srcinfos map[string]*gosrc.Srcinfo,
  41. cmdBuilder GPGCmdBuilder, noConfirm bool,
  42. ) ([]string, error) {
  43. // Let's check the keys individually, and then we can offer to import
  44. // the problematic ones.
  45. problematic := make(pgpKeySet)
  46. // Mapping all the keys.
  47. for pkg := range pkgbuildDirsByBase {
  48. srcinfo := srcinfos[pkg]
  49. for _, key := range srcinfo.ValidPGPKeys {
  50. // If key already marked as problematic, indicate the current
  51. // PKGBUILD requires it.
  52. if problematic.get(key) {
  53. problematic.set(key, pkg)
  54. continue
  55. }
  56. if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, "--list-keys", key)); err != nil {
  57. problematic.set(key, pkg)
  58. }
  59. }
  60. }
  61. // No key issues!
  62. if len(problematic) == 0 {
  63. return []string{}, nil
  64. }
  65. str, err := formatKeysToImport(logger, problematic)
  66. if err != nil {
  67. return nil, err
  68. }
  69. logger.Println("\n", str)
  70. if logger.ContinueTask(gotext.Get("Import?"), true, noConfirm) {
  71. return problematic.toSlice(), importKeys(ctx, logger, cmdBuilder, problematic.toSlice())
  72. }
  73. return problematic.toSlice(), nil
  74. }
  75. // importKeys tries to import the list of keys specified in its argument.
  76. func importKeys(ctx context.Context, logger *text.Logger, cmdBuilder GPGCmdBuilder, keys []string) error {
  77. logger.OperationInfoln(gotext.Get("Importing keys with gpg..."))
  78. if err := cmdBuilder.Show(cmdBuilder.BuildGPGCmd(ctx, append([]string{"--recv-keys"}, keys...)...)); err != nil {
  79. return errors.New(gotext.Get("problem importing keys"))
  80. }
  81. return nil
  82. }
  83. // formatKeysToImport receives a set of keys and returns a string containing the
  84. // question asking the user wants to import the problematic keys.
  85. func formatKeysToImport(logger *text.Logger, keys pgpKeySet) (string, error) {
  86. if len(keys) == 0 {
  87. return "", errors.New(gotext.Get("no keys to import"))
  88. }
  89. var buffer bytes.Buffer
  90. buffer.WriteString(logger.SprintOperationInfo(gotext.Get("PGP keys need importing:")))
  91. for key, bases := range keys {
  92. pkglist := ""
  93. for _, base := range bases {
  94. pkglist += base + " "
  95. }
  96. pkglist = strings.TrimRight(pkglist, " ")
  97. buffer.WriteString("\n" + logger.SprintWarn(gotext.Get("%s, required by: %s", text.Cyan(key), text.Cyan(pkglist))))
  98. }
  99. return buffer.String(), nil
  100. }