github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/externals/proof_service_reddit.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package externals 5 6 import ( 7 "net/url" 8 "regexp" 9 "strings" 10 11 libkb "github.com/keybase/client/go/libkb" 12 keybase1 "github.com/keybase/client/go/protocol/keybase1" 13 jsonw "github.com/keybase/go-jsonw" 14 ) 15 16 // ============================================================================= 17 // Reddit 18 // 19 20 type RedditChecker struct { 21 proof libkb.RemoteProofChainLink 22 } 23 24 var _ libkb.ProofChecker = (*RedditChecker)(nil) 25 26 const ( 27 RedditPrefix = "https://www.reddit.com" 28 RedditSub = RedditPrefix + "/r/keybaseproofs" 29 ) 30 31 func NewRedditChecker(p libkb.RemoteProofChainLink) (*RedditChecker, libkb.ProofError) { 32 return &RedditChecker{p}, nil 33 } 34 35 func (rc *RedditChecker) GetTorError() libkb.ProofError { return nil } 36 37 func (rc *RedditChecker) CheckStatus(mctx libkb.MetaContext, h libkb.SigHint, _ libkb.ProofCheckerMode, 38 pvlU keybase1.MerkleStoreEntry) (*libkb.SigHint, libkb.ProofError) { 39 // TODO CORE-8951 see if we can populate verifiedHint with anything useful. 40 return nil, CheckProofPvl(mctx, keybase1.ProofType_REDDIT, rc.proof, h, pvlU) 41 } 42 43 // 44 // ============================================================================= 45 46 func urlReencode(s string) string { 47 // Reddit interprets plusses in the query string differently depending 48 // on whether the user is using the old or new (2018) design, and 49 // on whether it's the title or body of the post. 50 // old, *, '+' -> ' ' 51 // old, *, '%2B' -> '+' 52 // new, title, '+' -> ' ' 53 // new, title, '%2B' -> ' ' 54 // new, body, '+' -> '+' 55 // new, body, '%2B' -> '+' 56 57 // Examples: 58 // https://www.reddit.com/r/test/submit?text=content+fmt%0Aplus+x%2By%20z&title=title+fmt%0Aplus+x%2By%20z 59 // old: https://www.reddit.com/r/test/comments/8eee0e/title_fmt_plus_xy_z/ 60 // new: https://www.reddit.com/r/test/comments/8eelwf/title_fmt_plus_x_y_z/ 61 62 // Replace '(', ")" and "'" so that URL-detection works in Linux 63 // Padding is not needed now, but might be in the future depending on 64 // changes we make 65 rxx := regexp.MustCompile(`[()'+]`) 66 s = rxx.ReplaceAllStringFunc(s, func(r string) string { 67 switch r { 68 case `(`: 69 return `%28` 70 case `)`: 71 return `%29` 72 case `'`: 73 return `%27` 74 case `+`: 75 // HTTPArgs.EncodeToString has encoded ' ' -> '+' 76 // we recode '+' -> '%20'. 77 return `%20` 78 default: 79 return r 80 } 81 }) 82 return s 83 } 84 85 type RedditServiceType struct{ libkb.BaseServiceType } 86 87 func (t *RedditServiceType) Key() string { return t.GetTypeName() } 88 89 var redditUsernameRegexp = regexp.MustCompile(`^(?i:[a-z0-9_-]{3,20})$`) 90 91 func (t *RedditServiceType) NormalizeUsername(s string) (string, error) { 92 if !redditUsernameRegexp.MatchString(s) { 93 return "", libkb.NewBadUsernameError(s) 94 } 95 return strings.ToLower(s), nil 96 } 97 98 func (t *RedditServiceType) NormalizeRemoteName(mctx libkb.MetaContext, s string) (ret string, err error) { 99 return t.NormalizeUsername(s) 100 } 101 102 func (t *RedditServiceType) GetTypeName() string { return "reddit" } 103 func (t *RedditServiceType) PickerSubtext() string { return "reddit.com" } 104 105 func (t *RedditServiceType) GetPrompt() string { return "Your username on Reddit" } 106 107 func (t *RedditServiceType) ToServiceJSON(un string) *jsonw.Wrapper { 108 return t.BaseToServiceJSON(t, un) 109 } 110 111 func (t *RedditServiceType) PostInstructions(un string) *libkb.Markup { 112 return libkb.FmtMarkup(`Please click on the following link to post to Reddit:`) 113 } 114 115 func (t *RedditServiceType) FormatProofText(mctx libkb.MetaContext, ppr *libkb.PostProofRes, 116 kbUsername, remoteUsername string, sigID keybase1.SigID) (res string, err error) { 117 var title string 118 if title, err = ppr.Metadata.AtKey("title").GetString(); err != nil { 119 return 120 } 121 122 urlPre := libkb.HTTPArgs{"title": libkb.S{Val: title}, "text": libkb.S{Val: ppr.Text}}.EncodeToString() 123 q := urlReencode(urlPre) 124 125 chooseHost := func(untrustedHint, trustedDefault string) string { 126 allowedHosts := []string{ 127 "reddit.com", 128 "www.reddit.com", 129 // 2017-04-18: The new reddit mobile site doesn't respect the post-pre-populate query parameters. 130 // The i.reddit.com site may be better. 131 "i.reddit.com", 132 "old.reddit.com", 133 } 134 if untrustedHint == "" { 135 return trustedDefault 136 } 137 for _, h := range allowedHosts { 138 if untrustedHint == h { 139 return h 140 } 141 } 142 return trustedDefault 143 } 144 var host string 145 if mctx.G().IsMobileAppType() { 146 hostHint, err := ppr.Metadata.AtKey("mobile_host").GetString() 147 if err != nil { 148 hostHint = "" 149 } 150 host = chooseHost(hostHint, "old.reddit.com") 151 } else { 152 // Note that GetAppType() often returns libkb.NoAppType. Don't assume that we get 153 // libkb.DesktopAppType in the non-mobile case. 154 // Use the old reddit design until this bug is fixed: 155 // https://www.reddit.com/r/redesign/comments/8evfap/bug_post_contents_gets_dropped_when_using/ 156 hostHint, err := ppr.Metadata.AtKey("other_host").GetString() 157 if err != nil { 158 hostHint = "" 159 } 160 host = chooseHost(hostHint, "old.reddit.com") 161 } 162 163 u := url.URL{ 164 Scheme: "https", 165 Host: host, 166 Path: "/r/KeybaseProofs/submit", 167 RawQuery: q, 168 } 169 170 res = u.String() 171 return 172 } 173 174 func (t *RedditServiceType) DisplayName() string { return "Reddit" } 175 176 func (t *RedditServiceType) RecheckProofPosting(tryNumber int, status keybase1.ProofStatus, _ string) (warning *libkb.Markup, err error) { 177 warning, err = t.BaseRecheckProofPosting(tryNumber, status) 178 return 179 } 180 181 func (t *RedditServiceType) GetProofType() string { return t.BaseGetProofType(t) } 182 183 func (t *RedditServiceType) CheckProofText(text string, id keybase1.SigID, sig string) (err error) { 184 // Anything is fine. We might get rid of the body later. 185 return nil 186 } 187 188 func (t *RedditServiceType) MakeProofChecker(l libkb.RemoteProofChainLink) libkb.ProofChecker { 189 return &RedditChecker{l} 190 }