github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }