github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/utils/kbfs.go (about) 1 // Copyright 2016 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 // This is all stuff copied from libkbfs. 5 6 package utils 7 8 import ( 9 "fmt" 10 "sort" 11 "strings" 12 13 "github.com/keybase/client/go/externals" 14 "github.com/keybase/client/go/libkb" 15 "github.com/keybase/client/go/protocol/keybase1" 16 ) 17 18 const ( 19 // ReaderSep is the string that separates readers from writers in a 20 // TLF name. 21 ReaderSep = "#" 22 23 // TlfHandleExtensionSep is the string that separates the folder 24 // participants from an extension suffix in the TLF name. 25 TlfHandleExtensionSep = " " 26 27 // PublicUIDName is the name given to keybase1.PublicUID. This string 28 // should correspond to an illegal or reserved Keybase user name. 29 PublicUIDName = "_public" 30 ) 31 32 func SplitAndNormalizeTLFName(mctx libkb.MetaContext, name string, public bool) ( 33 writerNames, readerNames []string, 34 extensionSuffix string, err error) { 35 36 names := strings.SplitN(name, TlfHandleExtensionSep, 2) 37 if len(names) > 2 { 38 return nil, nil, "", BadTLFNameError{name} 39 } 40 if len(names) > 1 { 41 extensionSuffix = names[1] 42 } 43 44 splitNames := strings.SplitN(names[0], ReaderSep, 3) 45 if len(splitNames) > 2 { 46 return nil, nil, "", BadTLFNameError{name} 47 } 48 writerNames = strings.Split(splitNames[0], ",") 49 if len(splitNames) > 1 { 50 readerNames = strings.Split(splitNames[1], ",") 51 } 52 53 hasPublic := len(readerNames) == 0 54 55 if public && !hasPublic { 56 // No public folder exists for this folder. 57 return nil, nil, "", NoSuchNameError{Name: name} 58 } 59 60 normalizedName, err := NormalizeNamesInTLF(mctx, 61 writerNames, readerNames, extensionSuffix) 62 if err != nil { 63 return nil, nil, "", err 64 } 65 if normalizedName != name { 66 return nil, nil, "", TlfNameNotCanonical{name, normalizedName} 67 } 68 69 return writerNames, readerNames, strings.ToLower(extensionSuffix), nil 70 } 71 72 // normalizeNamesInTLF takes a split TLF name and, without doing any 73 // resolutions or identify calls, normalizes all elements of the 74 // name. It then returns the normalized name. 75 func NormalizeNamesInTLF(mctx libkb.MetaContext, writerNames, readerNames []string, 76 extensionSuffix string) (string, error) { 77 sortedWriterNames := make([]string, len(writerNames)) 78 var err error 79 for i, w := range writerNames { 80 sortedWriterNames[i], err = NormalizeAssertionOrName(mctx, w) 81 if err != nil { 82 return "", err 83 } 84 } 85 sort.Strings(sortedWriterNames) 86 normalizedName := strings.Join(sortedWriterNames, ",") 87 if len(readerNames) > 0 { 88 sortedReaderNames := make([]string, len(readerNames)) 89 for i, r := range readerNames { 90 sortedReaderNames[i], err = NormalizeAssertionOrName(mctx, r) 91 if err != nil { 92 return "", err 93 } 94 } 95 sort.Strings(sortedReaderNames) 96 normalizedName += ReaderSep + strings.Join(sortedReaderNames, ",") 97 } 98 if len(extensionSuffix) != 0 { 99 // This *should* be normalized already but make sure. I can see not 100 // doing so might surprise a caller. 101 normalizedName += TlfHandleExtensionSep + strings.ToLower(extensionSuffix) 102 } 103 104 return normalizedName, nil 105 } 106 107 // TODO: this function can likely be replaced with a call to 108 // AssertionParseAndOnly when CORE-2967 and CORE-2968 are fixed. 109 func NormalizeAssertionOrName(mctx libkb.MetaContext, s string) (string, error) { 110 if libkb.CheckUsername.F(s) { 111 return libkb.NewNormalizedUsername(s).String(), nil 112 } 113 114 // TODO: this fails for http and https right now (see CORE-2968). 115 socialAssertion, isSocialAssertion := externals.NormalizeSocialAssertion(mctx, s) 116 if isSocialAssertion { 117 return socialAssertion.String(), nil 118 } 119 120 if expr, err := externals.AssertionParseAndOnly(mctx, s); err == nil { 121 // If the expression only contains a single url, make sure 122 // it's not a just considered a single keybase username. If 123 // it is, then some non-username slipped into the default 124 // "keybase" case and should be considered an error. 125 urls := expr.CollectUrls(nil) 126 if len(urls) == 1 && urls[0].IsKeybase() { 127 return "", NoSuchUserError{s} 128 } 129 130 // Normalize and return. Ideally `AssertionParseAndOnly` 131 // would normalize for us, but that doesn't work yet, so for 132 // now we'll just lower-case. This will incorrectly lower 133 // case http/https/web assertions, as well as case-sensitive 134 // social assertions in AND expressions. TODO: see CORE-2967. 135 return strings.ToLower(s), nil 136 } 137 138 return "", BadTLFNameError{s} 139 } 140 141 // NoSuchNameError indicates that the user tried to access a 142 // subdirectory entry that doesn't exist. 143 type NoSuchNameError struct { 144 Name string 145 } 146 147 // Error implements the error interface for NoSuchNameError 148 func (e NoSuchNameError) Error() string { 149 return fmt.Sprintf("%s doesn't exist", e.Name) 150 } 151 152 // BadTLFNameError indicates a top-level folder name that has an 153 // incorrect format. 154 type BadTLFNameError struct { 155 Name string 156 } 157 158 // Error implements the error interface for BadTLFNameError. 159 func (e BadTLFNameError) Error() string { 160 return fmt.Sprintf("TLF name %s is in an incorrect format", e.Name) 161 } 162 163 // TlfNameNotCanonical indicates that a name isn't a canonical, and 164 // that another (not necessarily canonical) name should be tried. 165 type TlfNameNotCanonical struct { 166 Name, NameToTry string 167 } 168 169 func (e TlfNameNotCanonical) Error() string { 170 return fmt.Sprintf("TLF name %s isn't canonical: try %s instead", 171 e.Name, e.NameToTry) 172 } 173 174 // NoSuchUserError indicates that the given user couldn't be resolved. 175 type NoSuchUserError struct { 176 Input string 177 } 178 179 // Error implements the error interface for NoSuchUserError 180 func (e NoSuchUserError) Error() string { 181 return fmt.Sprintf("%s is not a Keybase user", e.Input) 182 } 183 184 // ToStatus implements the keybase1.ToStatusAble interface for NoSuchUserError 185 func (e NoSuchUserError) ToStatus() keybase1.Status { 186 return keybase1.Status{ 187 Name: "NotFound", 188 Code: int(keybase1.StatusCode_SCNotFound), 189 Desc: e.Error(), 190 } 191 }