<commit_before>package podsecuritypolicy

import (
	"fmt"

	v13 "github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/extensions/v1beta1"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	v12 "github.com/rancher/types/apis/rbac.authorization.k8s.io/v1"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	rbac "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/tools/cache"
)

const psptpbByTargetProjectNameAnnotationIndex = "podsecuritypolicy.rbac.user.cattle.io/psptpb-by-project-id"
const roleBindingByServiceAccountIndex = "podsecuritypolicy.rbac.user.cattle.io/role-binding-by-service-account"
const psptpbRoleBindingAnnotation = "podsecuritypolicy.rbac.user.cattle.io/psptpb-role-binding"

// RegisterServiceAccount ensures that:
// 	1. Each namespace has a pod security policy assigned to a role if:
//		a. its project has a PSPT assigned to it
//		OR
//		b. its cluster has a default PSPT assigned to it
//  2. PSPs are bound to their associated service accounts via a cluster role binding
func RegisterServiceAccount(context *config.UserContext) {
	logrus.Infof("registering podsecuritypolicy serviceaccount handler for cluster %v", context.ClusterName)

	psptpbInformer := context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").Controller().Informer()
	psptpbIndexers := map[string]cache.IndexFunc{
		psptpbByTargetProjectNameAnnotationIndex: psptpbByTargetProjectName,
	}
	psptpbInformer.AddIndexers(psptpbIndexers)

	roleBindingInformer := context.RBAC.RoleBindings("").Controller().Informer()
	roleBindingIndexers := map[string]cache.IndexFunc{
		roleBindingByServiceAccountIndex: roleBindingByServiceAccount,
	}
	roleBindingInformer.AddIndexers(roleBindingIndexers)

	m := &serviceAccountManager{
		clusterName:        context.ClusterName,
		clusters:           context.Management.Management.Clusters(""),
		pspts:              context.Management.Management.PodSecurityPolicyTemplates(""),
		roleBindings:       context.RBAC.RoleBindings(""),
		roleBindingIndexer: roleBindingInformer.GetIndexer(),

		policies:      context.Extensions.PodSecurityPolicies(""),
		psptpbIndexer: psptpbInformer.GetIndexer(),

		clusterLister:     context.Management.Management.Clusters("").Controller().Lister(),
		psptLister:        context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		templateLister:    context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		policyLister:      context.Extensions.PodSecurityPolicies("").Controller().Lister(),
		roleBindingLister: context.RBAC.RoleBindings("").Controller().Lister(),
		roleLister:        context.RBAC.ClusterRoles("").Controller().Lister(),
		namespaceLister:   context.Core.Namespaces("").Controller().Lister(),
		projectLister:     context.Management.Management.Projects("").Controller().Lister(),
		psptpbLister: context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").
			Controller().Lister(),
	}

	context.Core.ServiceAccounts("").AddHandler("ServiceAccountLifecycleHandler", m.sync)
}

func psptpbByTargetProjectName(obj interface{}) ([]string, error) {
	psptpb, ok := obj.(*v3.PodSecurityPolicyTemplateProjectBinding)
	if !ok || psptpb.TargetProjectName == "" {
		return []string{}, nil
	}

	return []string{psptpb.TargetProjectName}, nil
}

func roleBindingByServiceAccount(obj interface{}) ([]string, error) {
	roleBinding, ok := obj.(*rbac.RoleBinding)
	if !ok || len(roleBinding.Subjects) != 1 ||
		roleBinding.Subjects[0].Name == "" ||
		roleBinding.Subjects[0].Namespace == "" {
		return []string{}, nil
	}

	subject := roleBinding.Subjects[0]
	return []string{subject.Namespace + "-" + subject.Name}, nil
}

type serviceAccountManager struct {
	clusterName        string
	clusterLister      v3.ClusterLister
	clusters           v3.ClusterInterface
	pspts              v3.PodSecurityPolicyTemplateInterface
	psptLister         v3.PodSecurityPolicyTemplateLister
	psptpbIndexer      cache.Indexer
	templateLister     v3.PodSecurityPolicyTemplateLister
	policyLister       v1beta1.PodSecurityPolicyLister
	roleBindingLister  v12.RoleBindingLister
	roleBindings       v12.RoleBindingInterface
	roleBindingIndexer cache.Indexer
	policies           v1beta1.PodSecurityPolicyInterface
	roleLister         v12.ClusterRoleLister
	namespaceLister    v13.NamespaceLister
	projectLister      v3.ProjectLister
	psptpbLister       v3.PodSecurityPolicyTemplateProjectBindingLister
}

func (m *serviceAccountManager) sync(key string, obj *v1.ServiceAccount) error {
	if obj == nil {
		// do nothing
		return nil
	}

	namespace, err := m.namespaceLister.Get("", obj.Namespace)
	if err != nil {
		return fmt.Errorf("error getting projects: %v", err)
	}

	var psptpbs []interface{}

	if namespace.Annotations[projectIDAnnotation] != "" {
		psptpbs, err = m.psptpbIndexer.ByIndex(psptpbByTargetProjectNameAnnotationIndex, namespace.Annotations[projectIDAnnotation])
		if err != nil {
			return fmt.Errorf("error getting psptpbs: %v", err)
		}
	}

	onePSPTPBExists := false
	desiredBindings := map[string]*v3.PodSecurityPolicyTemplateProjectBinding{}

	for _, rawPSPTPB := range psptpbs {
		psptpb, ok := rawPSPTPB.(*v3.PodSecurityPolicyTemplateProjectBinding)
		if !ok {
			return fmt.Errorf("could not convert to *v3.PodSecurityPolicyTemplateProjectBinding: %v", rawPSPTPB)
		}

		if psptpb.DeletionTimestamp != nil {
			continue
		}

		onePSPTPBExists = true

		key := getClusterRoleName(psptpb.PodSecurityPolicyTemplateName)
		desiredBindings[key] = psptpb
	}

	originalDesiredBindingsLen := len(desiredBindings)

	roleBindings, err := m.roleBindingIndexer.ByIndex(roleBindingByServiceAccountIndex, obj.Namespace+"-"+obj.Name)
	if err != nil {
		return fmt.Errorf("error getting role bindings: %v", err)
	}

	cluster, err := m.clusterLister.Get("", m.clusterName)
	if err != nil {
		return fmt.Errorf("error getting cluster: %v", err)
	}

	for _, rawRoleBinding := range roleBindings {
		roleBinding, ok := rawRoleBinding.(*rbac.RoleBinding)
		if !ok {
			return fmt.Errorf("could not convert to *rbac2.RoleBinding: %v", rawRoleBinding)
		}

		key := roleBinding.RoleRef.Name

		if desiredBindings[key] == nil && okToDelete(obj, roleBinding, cluster, originalDesiredBindingsLen) {
			err = m.roleBindings.DeleteNamespaced(roleBinding.Namespace, roleBinding.Name, &metav1.DeleteOptions{})
			if err != nil {
				return fmt.Errorf("error deleting role binding: %v", err)
			}
		} else {
			delete(desiredBindings, key)
		}
	}

	for clusterRoleName, desiredBinding := range desiredBindings {
		roleBindingName := getRoleBindingName(obj, clusterRoleName)
		_, err = m.roleBindings.Create(&rbac.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				Name:      roleBindingName,
				Namespace: obj.Namespace,
				Annotations: map[string]string{
					podSecurityPolicyTemplateParentAnnotation: desiredBinding.PodSecurityPolicyTemplateName,
					psptpbRoleBindingAnnotation:               "true",
				},
				OwnerReferences: []metav1.OwnerReference{
					{
						APIVersion: "v1",
						Name:       obj.Name,
						Kind:       "ServiceAccount",
						UID:        obj.UID,
					},
				},
			},
			TypeMeta: metav1.TypeMeta{
				Kind: "RoleBinding",
			},
			RoleRef: rbac.RoleRef{
				APIGroup: apiGroup,
				Name:     clusterRoleName,
				Kind:     "ClusterRole",
			},
			Subjects: []rbac.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      obj.Name,
					Namespace: obj.Namespace,
				},
			},
		})
		if err != nil {
			return fmt.Errorf("error creating binding: %v", err)
		}
	}

	if !onePSPTPBExists && namespace.Annotations[projectIDAnnotation] != "" {
		// create default pspt role binding if it is set
		clusterRoleName := getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)
		roleBindingName := getDefaultRoleBindingName(obj, clusterRoleName)

		if cluster.Spec.DefaultPodSecurityPolicyTemplateName != "" {
			_, err := m.roleBindingLister.Get(obj.Namespace, roleBindingName)
			if err != nil {
				if errors.IsNotFound(err) {
					_, err = m.roleBindings.Create(&rbac.RoleBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:      roleBindingName,
							Namespace: obj.Namespace,
							Annotations: map[string]string{
								podSecurityPolicyTemplateParentAnnotation: cluster.Spec.DefaultPodSecurityPolicyTemplateName,
								psptpbRoleBindingAnnotation:               "true",
							},
							OwnerReferences: []metav1.OwnerReference{
								{
									APIVersion: "v1",
									Name:       obj.Name,
									Kind:       "ServiceAccount",
									UID:        obj.UID,
								},
							},
						},
						TypeMeta: metav1.TypeMeta{
							Kind: "RoleBinding",
						},
						RoleRef: rbac.RoleRef{
							APIGroup: apiGroup,
							Name:     clusterRoleName,
							Kind:     "ClusterRole",
						},
						Subjects: []rbac.Subject{
							{
								Kind:      "ServiceAccount",
								Name:      obj.Name,
								Namespace: obj.Namespace,
							},
						},
					})
					if err != nil {
						return fmt.Errorf("error creating role binding: %v", err)
					}
				} else {
					return fmt.Errorf("error getting role binding %v: %v", roleBindingName, err)
				}
			}
		}
	}

	return nil
}

func okToDelete(svcAct *v1.ServiceAccount, rb *rbac.RoleBinding, cluster *v3.Cluster,
	originalDesiredBindingsLen int) bool {
	// This is not a role binding this logic should manage so exit immediately
	if rb.Annotations[psptpbRoleBindingAnnotation] == "" {
		return false
	}

	// Namespace isn't in a project so it should have no role bindings
	if rb.Annotations[projectIDAnnotation] == "" {
		return true
	}

	// No default PSPT is set so its ok to delete this if its a normal rolebinding or a leftover default PSPT binding
	if cluster.Spec.DefaultPodSecurityPolicyTemplateName == "" {
		return true
	}

	// at least one PSPTPB exists so we need to delete all default PSPT bindings
	if originalDesiredBindingsLen > 0 {
		return true
	}

	// the default PSPT has changed so we need to clean it up before creating the new one
	if getDefaultRoleBindingName(svcAct,
		getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)) != rb.Name {
		return true
	}

	return false
}

func getRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getDefaultRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("default-%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getClusterRoleName(podSecurityPolicyTemplateName string) string {
	return fmt.Sprintf("%v-clusterrole", podSecurityPolicyTemplateName)
}

func resyncServiceAccounts(serviceAccountLister v13.ServiceAccountLister,
	serviceAccountController v13.ServiceAccountController, namespace string) error {
	serviceAccounts, err := serviceAccountLister.List(namespace, labels.Everything())
	if err != nil {
		return fmt.Errorf("error getting service accounts: %v", err)
	}

	for _, account := range serviceAccounts {
		serviceAccountController.Enqueue(account.Namespace, account.Name)
	}

	return nil
}
<commit_msg>Fixing issue where service account did not get PSP bound<commit_after>package podsecuritypolicy

import (
	"fmt"

	v13 "github.com/rancher/types/apis/core/v1"
	"github.com/rancher/types/apis/extensions/v1beta1"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	v12 "github.com/rancher/types/apis/rbac.authorization.k8s.io/v1"
	"github.com/rancher/types/config"
	"github.com/sirupsen/logrus"
	"k8s.io/api/core/v1"
	rbac "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/tools/cache"
)

const psptpbByTargetProjectNameAnnotationIndex = "podsecuritypolicy.rbac.user.cattle.io/psptpb-by-project-id"
const roleBindingByServiceAccountIndex = "podsecuritypolicy.rbac.user.cattle.io/role-binding-by-service-account"
const psptpbRoleBindingAnnotation = "podsecuritypolicy.rbac.user.cattle.io/psptpb-role-binding"

// RegisterServiceAccount ensures that:
// 	1. Each namespace has a pod security policy assigned to a role if:
//		a. its project has a PSPT assigned to it
//		OR
//		b. its cluster has a default PSPT assigned to it
//  2. PSPs are bound to their associated service accounts via a cluster role binding
func RegisterServiceAccount(context *config.UserContext) {
	logrus.Infof("registering podsecuritypolicy serviceaccount handler for cluster %v", context.ClusterName)

	psptpbInformer := context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").Controller().Informer()
	psptpbIndexers := map[string]cache.IndexFunc{
		psptpbByTargetProjectNameAnnotationIndex: psptpbByTargetProjectName,
	}
	psptpbInformer.AddIndexers(psptpbIndexers)

	roleBindingInformer := context.RBAC.RoleBindings("").Controller().Informer()
	roleBindingIndexers := map[string]cache.IndexFunc{
		roleBindingByServiceAccountIndex: roleBindingByServiceAccount,
	}
	roleBindingInformer.AddIndexers(roleBindingIndexers)

	m := &serviceAccountManager{
		clusterName:        context.ClusterName,
		clusters:           context.Management.Management.Clusters(""),
		pspts:              context.Management.Management.PodSecurityPolicyTemplates(""),
		roleBindings:       context.RBAC.RoleBindings(""),
		roleBindingIndexer: roleBindingInformer.GetIndexer(),

		policies:      context.Extensions.PodSecurityPolicies(""),
		psptpbIndexer: psptpbInformer.GetIndexer(),

		clusterLister:     context.Management.Management.Clusters("").Controller().Lister(),
		psptLister:        context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		templateLister:    context.Management.Management.PodSecurityPolicyTemplates("").Controller().Lister(),
		policyLister:      context.Extensions.PodSecurityPolicies("").Controller().Lister(),
		roleBindingLister: context.RBAC.RoleBindings("").Controller().Lister(),
		roleLister:        context.RBAC.ClusterRoles("").Controller().Lister(),
		namespaceLister:   context.Core.Namespaces("").Controller().Lister(),
		projectLister:     context.Management.Management.Projects("").Controller().Lister(),
		psptpbLister: context.Management.Management.PodSecurityPolicyTemplateProjectBindings("").
			Controller().Lister(),
	}

	context.Core.ServiceAccounts("").AddHandler("ServiceAccountLifecycleHandler", m.sync)
}

func psptpbByTargetProjectName(obj interface{}) ([]string, error) {
	psptpb, ok := obj.(*v3.PodSecurityPolicyTemplateProjectBinding)
	if !ok || psptpb.TargetProjectName == "" {
		return []string{}, nil
	}

	return []string{psptpb.TargetProjectName}, nil
}

func roleBindingByServiceAccount(obj interface{}) ([]string, error) {
	roleBinding, ok := obj.(*rbac.RoleBinding)
	if !ok || len(roleBinding.Subjects) != 1 ||
		roleBinding.Subjects[0].Name == "" ||
		roleBinding.Subjects[0].Namespace == "" {
		return []string{}, nil
	}

	subject := roleBinding.Subjects[0]
	return []string{subject.Namespace + "-" + subject.Name}, nil
}

type serviceAccountManager struct {
	clusterName        string
	clusterLister      v3.ClusterLister
	clusters           v3.ClusterInterface
	pspts              v3.PodSecurityPolicyTemplateInterface
	psptLister         v3.PodSecurityPolicyTemplateLister
	psptpbIndexer      cache.Indexer
	templateLister     v3.PodSecurityPolicyTemplateLister
	policyLister       v1beta1.PodSecurityPolicyLister
	roleBindingLister  v12.RoleBindingLister
	roleBindings       v12.RoleBindingInterface
	roleBindingIndexer cache.Indexer
	policies           v1beta1.PodSecurityPolicyInterface
	roleLister         v12.ClusterRoleLister
	namespaceLister    v13.NamespaceLister
	projectLister      v3.ProjectLister
	psptpbLister       v3.PodSecurityPolicyTemplateProjectBindingLister
}

func (m *serviceAccountManager) sync(key string, obj *v1.ServiceAccount) error {
	if obj == nil {
		// do nothing
		return nil
	}

	namespace, err := m.namespaceLister.Get("", obj.Namespace)
	if err != nil {
		return fmt.Errorf("error getting projects: %v", err)
	}

	var psptpbs []interface{}

	if namespace.Annotations[projectIDAnnotation] != "" {
		psptpbs, err = m.psptpbIndexer.ByIndex(psptpbByTargetProjectNameAnnotationIndex, namespace.Annotations[projectIDAnnotation])
		if err != nil {
			return fmt.Errorf("error getting psptpbs: %v", err)
		}
	}

	onePSPTPBExists := false
	desiredBindings := map[string]*v3.PodSecurityPolicyTemplateProjectBinding{}

	for _, rawPSPTPB := range psptpbs {
		psptpb, ok := rawPSPTPB.(*v3.PodSecurityPolicyTemplateProjectBinding)
		if !ok {
			return fmt.Errorf("could not convert to *v3.PodSecurityPolicyTemplateProjectBinding: %v", rawPSPTPB)
		}

		if psptpb.DeletionTimestamp != nil {
			continue
		}

		onePSPTPBExists = true

		key := getClusterRoleName(psptpb.PodSecurityPolicyTemplateName)
		desiredBindings[key] = psptpb
	}

	originalDesiredBindingsLen := len(desiredBindings)

	roleBindings, err := m.roleBindingIndexer.ByIndex(roleBindingByServiceAccountIndex, obj.Namespace+"-"+obj.Name)
	if err != nil {
		return fmt.Errorf("error getting role bindings: %v", err)
	}

	cluster, err := m.clusterLister.Get("", m.clusterName)
	if err != nil {
		return fmt.Errorf("error getting cluster: %v", err)
	}

	for _, rawRoleBinding := range roleBindings {
		roleBinding, ok := rawRoleBinding.(*rbac.RoleBinding)
		if !ok {
			return fmt.Errorf("could not convert to *rbac2.RoleBinding: %v", rawRoleBinding)
		}

		key := roleBinding.RoleRef.Name

		if desiredBindings[key] == nil && okToDelete(obj, roleBinding, namespace, cluster, originalDesiredBindingsLen) {
			err = m.roleBindings.DeleteNamespaced(roleBinding.Namespace, roleBinding.Name, &metav1.DeleteOptions{})
			if err != nil {
				return fmt.Errorf("error deleting role binding: %v", err)
			}
		} else {
			delete(desiredBindings, key)
		}
	}

	for clusterRoleName, desiredBinding := range desiredBindings {
		roleBindingName := getRoleBindingName(obj, clusterRoleName)
		_, err = m.roleBindings.Create(&rbac.RoleBinding{
			ObjectMeta: metav1.ObjectMeta{
				Name:      roleBindingName,
				Namespace: obj.Namespace,
				Annotations: map[string]string{
					podSecurityPolicyTemplateParentAnnotation: desiredBinding.PodSecurityPolicyTemplateName,
					psptpbRoleBindingAnnotation:               "true",
				},
				OwnerReferences: []metav1.OwnerReference{
					{
						APIVersion: "v1",
						Name:       obj.Name,
						Kind:       "ServiceAccount",
						UID:        obj.UID,
					},
				},
			},
			TypeMeta: metav1.TypeMeta{
				Kind: "RoleBinding",
			},
			RoleRef: rbac.RoleRef{
				APIGroup: apiGroup,
				Name:     clusterRoleName,
				Kind:     "ClusterRole",
			},
			Subjects: []rbac.Subject{
				{
					Kind:      "ServiceAccount",
					Name:      obj.Name,
					Namespace: obj.Namespace,
				},
			},
		})
		if err != nil {
			return fmt.Errorf("error creating binding: %v", err)
		}
	}

	if !onePSPTPBExists && namespace.Annotations[projectIDAnnotation] != "" {
		// create default pspt role binding if it is set
		clusterRoleName := getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)
		roleBindingName := getDefaultRoleBindingName(obj, clusterRoleName)

		if cluster.Spec.DefaultPodSecurityPolicyTemplateName != "" {
			_, err := m.roleBindingLister.Get(obj.Namespace, roleBindingName)
			if err != nil {
				if errors.IsNotFound(err) {
					_, err = m.roleBindings.Create(&rbac.RoleBinding{
						ObjectMeta: metav1.ObjectMeta{
							Name:      roleBindingName,
							Namespace: obj.Namespace,
							Annotations: map[string]string{
								podSecurityPolicyTemplateParentAnnotation: cluster.Spec.DefaultPodSecurityPolicyTemplateName,
								psptpbRoleBindingAnnotation:               "true",
							},
							OwnerReferences: []metav1.OwnerReference{
								{
									APIVersion: "v1",
									Name:       obj.Name,
									Kind:       "ServiceAccount",
									UID:        obj.UID,
								},
							},
						},
						TypeMeta: metav1.TypeMeta{
							Kind: "RoleBinding",
						},
						RoleRef: rbac.RoleRef{
							APIGroup: apiGroup,
							Name:     clusterRoleName,
							Kind:     "ClusterRole",
						},
						Subjects: []rbac.Subject{
							{
								Kind:      "ServiceAccount",
								Name:      obj.Name,
								Namespace: obj.Namespace,
							},
						},
					})
					if err != nil {
						return fmt.Errorf("error creating role binding: %v", err)
					}
				} else {
					return fmt.Errorf("error getting role binding %v: %v", roleBindingName, err)
				}
			}
		}
	}

	return nil
}

func okToDelete(svcAct *v1.ServiceAccount, rb *rbac.RoleBinding, namespace *v1.Namespace, cluster *v3.Cluster,
	originalDesiredBindingsLen int) bool {
	// This is not a role binding this logic should manage so exit immediately
	if rb.Annotations[psptpbRoleBindingAnnotation] == "" {
		return false
	}

	// Namespace isn't in a project so it should have no role bindings
	if namespace.Annotations[projectIDAnnotation] == "" {
		return true
	}

	// No default PSPT is set so its ok to delete this if its a normal rolebinding or a leftover default PSPT binding
	if cluster.Spec.DefaultPodSecurityPolicyTemplateName == "" {
		return true
	}

	// at least one PSPTPB exists so we need to delete all default PSPT bindings
	if originalDesiredBindingsLen > 0 {
		return true
	}

	// the default PSPT has changed so we need to clean it up before creating the new one
	if getDefaultRoleBindingName(svcAct,
		getClusterRoleName(cluster.Spec.DefaultPodSecurityPolicyTemplateName)) != rb.Name {
		return true
	}

	return false
}

func getRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getDefaultRoleBindingName(obj *v1.ServiceAccount, clusterRoleName string) string {
	return fmt.Sprintf("default-%v-%v-%v-binding", obj.Name, obj.Namespace, clusterRoleName)
}

func getClusterRoleName(podSecurityPolicyTemplateName string) string {
	return fmt.Sprintf("%v-clusterrole", podSecurityPolicyTemplateName)
}

func resyncServiceAccounts(serviceAccountLister v13.ServiceAccountLister,
	serviceAccountController v13.ServiceAccountController, namespace string) error {
	serviceAccounts, err := serviceAccountLister.List(namespace, labels.Everything())
	if err != nil {
		return fmt.Errorf("error getting service accounts: %v", err)
	}

	for _, account := range serviceAccounts {
		serviceAccountController.Enqueue(account.Namespace, account.Name)
	}

	return nil
}
<|endoftext|>


<commit_before>package quic

type FrameType uint8

const (
	PaddingFrameType FrameType = iota
	RstStreamFrameType
	ConnectionCloseFrameType
	GoawayFrameType
	WindowUpdateFrameType
	BlockedFrameType
	StopWaitingFrameType
	PingFrameType
	StreamFrameType             = 0x80
	AckFrameType                = 0x40
	CongestionFeedbackFrameType = 0x20
)
<commit_msg>add frame header<commit_after>package quic

type FrameType uint8

const (
	PaddingFrameType FrameType = iota
	RstStreamFrameType
	ConnectionCloseFrameType
	GoawayFrameType
	WindowUpdateFrameType
	BlockedFrameType
	StopWaitingFrameType
	PingFrameType
	StreamFrameType             = 0x80
	AckFrameType                = 0x40
	CongestionFeedbackFrameType = 0x20
)

// Frame Header
/*
+--------+--------+--------+--------+--------+---    ---+
 Public |    Connection ID (0, 8, 32, or 64)    ...    | ->
|Flags(8)|      (variable length)                       |
+--------+--------+--------+--------+--------+---    ---+

    9       10       11        12
+--------+--------+--------+--------+
|      Quic Version (32)            | ->
|         (optional)                |
+--------+--------+--------+--------+

    13      14       15        16        17       18       19       20
+--------+--------+--------+--------+--------+--------+--------+--------+
|         Sequence Number (8, 16, 32, or 48)          |Private | FEC (8)|
|                         (variable length)           |Flags(8)|  (opt) |
+--------+--------+--------+--------+--------+--------+--------+--------+
*/

type FrameHeader struct {
	PublicFlags    byte
	ConnectionID   uint64
	Version        uint32
	SequenceNumber uint64
	PrivateFlags   byte
	FEC            byte
}

func NewFrameHeader(publicFlags byte, connectionID uint64, version uint32, sequenceNumber uint64, privateFlags, fec byte) *FrameHeader {
	fh := &FrameHeader{
		publicFlags,
		connectionID,
		version,
		sequenceNumber,
		privateFlags,
		fec,
	}
	return fh
}
<|endoftext|>


<commit_before>package index

import (
	"math/rand"
	"restic"
	"restic/repository"
	"testing"
	"time"
)

var (
	snapshotTime = time.Unix(1470492820, 207401672)
	depth        = 3
)

func createFilledRepo(t testing.TB, snapshots int, dup float32) (restic.Repository, func()) {
	repo, cleanup := repository.TestRepository(t)

	for i := 0; i < 3; i++ {
		restic.TestCreateSnapshot(t, repo, snapshotTime.Add(time.Duration(i)*time.Second), depth, dup)
	}

	return repo, cleanup
}

func validateIndex(t testing.TB, repo restic.Repository, idx *Index) {
	for id := range repo.List(restic.DataFile, nil) {
		if _, ok := idx.Packs[id]; !ok {
			t.Errorf("pack %v missing from index", id.Str())
		}
	}
}

func TestIndexNew(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	idx, err := New(repo, nil)
	if err != nil {
		t.Fatalf("New() returned error %v", err)
	}

	if idx == nil {
		t.Fatalf("New() returned nil index")
	}

	validateIndex(t, repo, idx)
}

func TestIndexLoad(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	loadIdx, err := Load(repo, nil)
	if err != nil {
		t.Fatalf("Load() returned error %v", err)
	}

	if loadIdx == nil {
		t.Fatalf("Load() returned nil index")
	}

	validateIndex(t, repo, loadIdx)

	newIdx, err := New(repo, nil)
	if err != nil {
		t.Fatalf("New() returned error %v", err)
	}

	if len(loadIdx.Packs) != len(newIdx.Packs) {
		t.Errorf("number of packs does not match: want %v, got %v",
			len(loadIdx.Packs), len(newIdx.Packs))
	}

	validateIndex(t, repo, newIdx)

	for packID, packNew := range newIdx.Packs {
		packLoad, ok := loadIdx.Packs[packID]

		if !ok {
			t.Errorf("loaded index does not list pack %v", packID.Str())
			continue
		}

		if len(packNew.Entries) != len(packLoad.Entries) {
			t.Errorf("  number of entries in pack %v does not match: %d != %d\n  %v\n  %v",
				packID.Str(), len(packNew.Entries), len(packLoad.Entries),
				packNew.Entries, packLoad.Entries)
			continue
		}

		for _, entryNew := range packNew.Entries {
			found := false
			for _, entryLoad := range packLoad.Entries {
				if !entryLoad.ID.Equal(entryNew.ID) {
					continue
				}

				if entryLoad.Type != entryNew.Type {
					continue
				}

				if entryLoad.Offset != entryNew.Offset {
					continue
				}

				if entryLoad.Length != entryNew.Length {
					continue
				}

				found = true
				break
			}

			if !found {
				t.Errorf("blob not found in loaded index: %v", entryNew)
			}
		}
	}
}

func BenchmarkIndexNew(b *testing.B) {
	repo, cleanup := createFilledRepo(b, 3, 0)
	defer cleanup()

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		idx, err := New(repo, nil)

		if err != nil {
			b.Fatalf("New() returned error %v", err)
		}

		if idx == nil {
			b.Fatalf("New() returned nil index")
		}
	}
}

func TestIndexDuplicateBlobs(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0.01)
	defer cleanup()

	idx, err := New(repo, nil)
	if err != nil {
		t.Fatal(err)
	}

	dups := idx.DuplicateBlobs()
	if len(dups) == 0 {
		t.Errorf("no duplicate blobs found")
	}
	t.Logf("%d packs, %d duplicate blobs", len(idx.Packs), len(dups))

	packs := idx.PacksForBlobs(dups)
	if len(packs) == 0 {
		t.Errorf("no packs with duplicate blobs found")
	}
	t.Logf("%d packs with duplicate blobs", len(packs))
}

func loadIndex(t testing.TB, repo restic.Repository) *Index {
	idx, err := Load(repo, nil)
	if err != nil {
		t.Fatalf("Load() returned error %v", err)
	}

	return idx
}

func TestIndexSave(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	idx := loadIndex(t, repo)

	packs := make(map[restic.ID][]restic.Blob)
	for id := range idx.Packs {
		if rand.Float32() < 0.5 {
			packs[id] = idx.Packs[id].Entries
		}
	}

	t.Logf("save %d/%d packs in a new index\n", len(packs), len(idx.Packs))

	id, err := Save(repo, packs, idx.IndexIDs.List())
	if err != nil {
		t.Fatalf("unable to save new index: %v", err)
	}

	t.Logf("new index saved as %v", id.Str())

	for id := range idx.IndexIDs {
		t.Logf("remove index %v", id.Str())
		err = repo.Backend().Remove(restic.IndexFile, id.String())
		if err != nil {
			t.Errorf("error removing index %v: %v", id, err)
		}
	}

	idx2 := loadIndex(t, repo)
	t.Logf("load new index with %d packs", len(idx2.Packs))

	if len(idx2.Packs) != len(packs) {
		t.Errorf("wrong number of packs in new index, want %d, got %d", len(packs), len(idx2.Packs))
	}

	for id := range packs {
		if _, ok := idx2.Packs[id]; !ok {
			t.Errorf("pack %v is not contained in new index", id.Str())
		}
	}

	for id := range idx2.Packs {
		if _, ok := packs[id]; !ok {
			t.Errorf("pack %v is not contained in new index", id.Str())
		}
	}
}

func TestIndexAddRemovePack(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	idx, err := Load(repo, nil)
	if err != nil {
		t.Fatalf("Load() returned error %v", err)
	}

	done := make(chan struct{})
	defer close(done)

	packID := <-repo.List(restic.DataFile, done)

	t.Logf("selected pack %v", packID.Str())

	blobs := idx.Packs[packID].Entries

	idx.RemovePack(packID)

	if _, ok := idx.Packs[packID]; ok {
		t.Errorf("removed pack %v found in index.Packs", packID.Str())
	}

	for _, blob := range blobs {
		h := restic.BlobHandle{ID: blob.ID, Type: blob.Type}
		_, err := idx.FindBlob(h)
		if err == nil {
			t.Errorf("removed blob %v found in index", h)
		}
	}

}

// example index serialization from doc/Design.md
var docExample = []byte(`
{
  "supersedes": [
	"ed54ae36197f4745ebc4b54d10e0f623eaaaedd03013eb7ae90df881b7781452"
  ],
  "packs": [
	{
	  "id": "73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c",
	  "blobs": [
		{
		  "id": "3ec79977ef0cf5de7b08cd12b874cd0f62bbaf7f07f3497a5b1bbcc8cb39b1ce",
		  "type": "data",
		  "offset": 0,
		  "length": 25
		},{
		  "id": "9ccb846e60d90d4eb915848add7aa7ea1e4bbabfc60e573db9f7bfb2789afbae",
		  "type": "tree",
		  "offset": 38,
		  "length": 100
		},
		{
		  "id": "d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66",
		  "type": "data",
		  "offset": 150,
		  "length": 123
		}
	  ]
	}
  ]
}
`)

func TestIndexLoadDocReference(t *testing.T) {
	repo, cleanup := repository.TestRepository(t)
	defer cleanup()

	id, err := repo.SaveUnpacked(restic.IndexFile, docExample)
	if err != nil {
		t.Fatalf("SaveUnpacked() returned error %v", err)
	}

	t.Logf("index saved as %v", id.Str())

	idx := loadIndex(t, repo)

	blobID := restic.TestParseID("d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66")
	locs, err := idx.FindBlob(restic.BlobHandle{ID: blobID, Type: restic.DataBlob})
	if err != nil {
		t.Errorf("FindBlob() returned error %v", err)
	}

	if len(locs) != 1 {
		t.Errorf("blob found %d times, expected just one", len(locs))
	}

	l := locs[0]
	if !l.ID.Equal(blobID) {
		t.Errorf("blob IDs are not equal: %v != %v", l.ID, blobID)
	}

	if l.Type != restic.DataBlob {
		t.Errorf("want type %v, got %v", restic.DataBlob, l.Type)
	}

	if l.Offset != 150 {
		t.Errorf("wrong offset, want %d, got %v", 150, l.Offset)
	}

	if l.Length != 123 {
		t.Errorf("wrong length, want %d, got %v", 123, l.Length)
	}
}
<commit_msg>Add Benchmark for IndexSave<commit_after>package index

import (
	"math/rand"
	"restic"
	"restic/repository"
	"restic/test"
	"testing"
	"time"
)

var (
	snapshotTime = time.Unix(1470492820, 207401672)
	depth        = 3
)

func createFilledRepo(t testing.TB, snapshots int, dup float32) (restic.Repository, func()) {
	repo, cleanup := repository.TestRepository(t)

	for i := 0; i < 3; i++ {
		restic.TestCreateSnapshot(t, repo, snapshotTime.Add(time.Duration(i)*time.Second), depth, dup)
	}

	return repo, cleanup
}

func validateIndex(t testing.TB, repo restic.Repository, idx *Index) {
	for id := range repo.List(restic.DataFile, nil) {
		if _, ok := idx.Packs[id]; !ok {
			t.Errorf("pack %v missing from index", id.Str())
		}
	}
}

func TestIndexNew(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	idx, err := New(repo, nil)
	if err != nil {
		t.Fatalf("New() returned error %v", err)
	}

	if idx == nil {
		t.Fatalf("New() returned nil index")
	}

	validateIndex(t, repo, idx)
}

func TestIndexLoad(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	loadIdx, err := Load(repo, nil)
	if err != nil {
		t.Fatalf("Load() returned error %v", err)
	}

	if loadIdx == nil {
		t.Fatalf("Load() returned nil index")
	}

	validateIndex(t, repo, loadIdx)

	newIdx, err := New(repo, nil)
	if err != nil {
		t.Fatalf("New() returned error %v", err)
	}

	if len(loadIdx.Packs) != len(newIdx.Packs) {
		t.Errorf("number of packs does not match: want %v, got %v",
			len(loadIdx.Packs), len(newIdx.Packs))
	}

	validateIndex(t, repo, newIdx)

	for packID, packNew := range newIdx.Packs {
		packLoad, ok := loadIdx.Packs[packID]

		if !ok {
			t.Errorf("loaded index does not list pack %v", packID.Str())
			continue
		}

		if len(packNew.Entries) != len(packLoad.Entries) {
			t.Errorf("  number of entries in pack %v does not match: %d != %d\n  %v\n  %v",
				packID.Str(), len(packNew.Entries), len(packLoad.Entries),
				packNew.Entries, packLoad.Entries)
			continue
		}

		for _, entryNew := range packNew.Entries {
			found := false
			for _, entryLoad := range packLoad.Entries {
				if !entryLoad.ID.Equal(entryNew.ID) {
					continue
				}

				if entryLoad.Type != entryNew.Type {
					continue
				}

				if entryLoad.Offset != entryNew.Offset {
					continue
				}

				if entryLoad.Length != entryNew.Length {
					continue
				}

				found = true
				break
			}

			if !found {
				t.Errorf("blob not found in loaded index: %v", entryNew)
			}
		}
	}
}

func BenchmarkIndexNew(b *testing.B) {
	repo, cleanup := createFilledRepo(b, 3, 0)
	defer cleanup()

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		idx, err := New(repo, nil)

		if err != nil {
			b.Fatalf("New() returned error %v", err)
		}

		if idx == nil {
			b.Fatalf("New() returned nil index")
		}
		b.Logf("idx %v packs", len(idx.Packs))
	}
}

func BenchmarkIndexSave(b *testing.B) {
	repo, cleanup := createFilledRepo(b, 3, 0)
	defer cleanup()

	idx, err := New(repo, nil)
	test.OK(b, err)

	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		id, err := idx.Save(repo, nil)
		if err != nil {
			b.Fatalf("New() returned error %v", err)
		}

		b.Logf("saved as %v", id.Str())
	}
}

func TestIndexDuplicateBlobs(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0.01)
	defer cleanup()

	idx, err := New(repo, nil)
	if err != nil {
		t.Fatal(err)
	}

	dups := idx.DuplicateBlobs()
	if len(dups) == 0 {
		t.Errorf("no duplicate blobs found")
	}
	t.Logf("%d packs, %d duplicate blobs", len(idx.Packs), len(dups))

	packs := idx.PacksForBlobs(dups)
	if len(packs) == 0 {
		t.Errorf("no packs with duplicate blobs found")
	}
	t.Logf("%d packs with duplicate blobs", len(packs))
}

func loadIndex(t testing.TB, repo restic.Repository) *Index {
	idx, err := Load(repo, nil)
	if err != nil {
		t.Fatalf("Load() returned error %v", err)
	}

	return idx
}

func TestIndexSave(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	idx := loadIndex(t, repo)

	packs := make(map[restic.ID][]restic.Blob)
	for id := range idx.Packs {
		if rand.Float32() < 0.5 {
			packs[id] = idx.Packs[id].Entries
		}
	}

	t.Logf("save %d/%d packs in a new index\n", len(packs), len(idx.Packs))

	id, err := Save(repo, packs, idx.IndexIDs.List())
	if err != nil {
		t.Fatalf("unable to save new index: %v", err)
	}

	t.Logf("new index saved as %v", id.Str())

	for id := range idx.IndexIDs {
		t.Logf("remove index %v", id.Str())
		err = repo.Backend().Remove(restic.IndexFile, id.String())
		if err != nil {
			t.Errorf("error removing index %v: %v", id, err)
		}
	}

	idx2 := loadIndex(t, repo)
	t.Logf("load new index with %d packs", len(idx2.Packs))

	if len(idx2.Packs) != len(packs) {
		t.Errorf("wrong number of packs in new index, want %d, got %d", len(packs), len(idx2.Packs))
	}

	for id := range packs {
		if _, ok := idx2.Packs[id]; !ok {
			t.Errorf("pack %v is not contained in new index", id.Str())
		}
	}

	for id := range idx2.Packs {
		if _, ok := packs[id]; !ok {
			t.Errorf("pack %v is not contained in new index", id.Str())
		}
	}
}

func TestIndexAddRemovePack(t *testing.T) {
	repo, cleanup := createFilledRepo(t, 3, 0)
	defer cleanup()

	idx, err := Load(repo, nil)
	if err != nil {
		t.Fatalf("Load() returned error %v", err)
	}

	done := make(chan struct{})
	defer close(done)

	packID := <-repo.List(restic.DataFile, done)

	t.Logf("selected pack %v", packID.Str())

	blobs := idx.Packs[packID].Entries

	idx.RemovePack(packID)

	if _, ok := idx.Packs[packID]; ok {
		t.Errorf("removed pack %v found in index.Packs", packID.Str())
	}

	for _, blob := range blobs {
		h := restic.BlobHandle{ID: blob.ID, Type: blob.Type}
		_, err := idx.FindBlob(h)
		if err == nil {
			t.Errorf("removed blob %v found in index", h)
		}
	}
}

// example index serialization from doc/Design.md
var docExample = []byte(`
{
  "supersedes": [
	"ed54ae36197f4745ebc4b54d10e0f623eaaaedd03013eb7ae90df881b7781452"
  ],
  "packs": [
	{
	  "id": "73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c",
	  "blobs": [
		{
		  "id": "3ec79977ef0cf5de7b08cd12b874cd0f62bbaf7f07f3497a5b1bbcc8cb39b1ce",
		  "type": "data",
		  "offset": 0,
		  "length": 25
		},{
		  "id": "9ccb846e60d90d4eb915848add7aa7ea1e4bbabfc60e573db9f7bfb2789afbae",
		  "type": "tree",
		  "offset": 38,
		  "length": 100
		},
		{
		  "id": "d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66",
		  "type": "data",
		  "offset": 150,
		  "length": 123
		}
	  ]
	}
  ]
}
`)

func TestIndexLoadDocReference(t *testing.T) {
	repo, cleanup := repository.TestRepository(t)
	defer cleanup()

	id, err := repo.SaveUnpacked(restic.IndexFile, docExample)
	if err != nil {
		t.Fatalf("SaveUnpacked() returned error %v", err)
	}

	t.Logf("index saved as %v", id.Str())

	idx := loadIndex(t, repo)

	blobID := restic.TestParseID("d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66")
	locs, err := idx.FindBlob(restic.BlobHandle{ID: blobID, Type: restic.DataBlob})
	if err != nil {
		t.Errorf("FindBlob() returned error %v", err)
	}

	if len(locs) != 1 {
		t.Errorf("blob found %d times, expected just one", len(locs))
	}

	l := locs[0]
	if !l.ID.Equal(blobID) {
		t.Errorf("blob IDs are not equal: %v != %v", l.ID, blobID)
	}

	if l.Type != restic.DataBlob {
		t.Errorf("want type %v, got %v", restic.DataBlob, l.Type)
	}

	if l.Offset != 150 {
		t.Errorf("wrong offset, want %d, got %v", 150, l.Offset)
	}

	if l.Length != 123 {
		t.Errorf("wrong length, want %d, got %v", 123, l.Length)
	}
}
<|endoftext|>


<commit_before>package mock

import (
	"fmt"

	"github.com/DMarby/picsum-photos/cache"
)

// Provider is a mock cache
type Provider struct{}

// Get returns an object from the cache if it exists
func (p *Provider) Get(key string) (data []byte, err error) {
	if key == "notfound" || key == "notfounderr" || key == "seterror" {
		return nil, cache.ErrNotFound
	}

	if key == "error" || key == "healthcheck" {
		return nil, fmt.Errorf("error")
	}

	return []byte("foo"), nil
}

// Set returns an object from the cache if it exists
func (p *Provider) Set(key string, data []byte) (err error) {
	if key == "seterror" {
		return fmt.Errorf("seterror")
	}

	return nil
}
<commit_msg>Remove healthcheck key check from mock cache<commit_after>package mock

import (
	"fmt"

	"github.com/DMarby/picsum-photos/cache"
)

// Provider is a mock cache
type Provider struct{}

// Get returns an object from the cache if it exists
func (p *Provider) Get(key string) (data []byte, err error) {
	if key == "notfound" || key == "notfounderr" || key == "seterror" {
		return nil, cache.ErrNotFound
	}

	if key == "error" {
		return nil, fmt.Errorf("error")
	}

	return []byte("foo"), nil
}

// Set returns an object from the cache if it exists
func (p *Provider) Set(key string, data []byte) (err error) {
	if key == "seterror" {
		return fmt.Errorf("seterror")
	}

	return nil
}
<|endoftext|>


<commit_before>package xmltree
/*
#cgo pkg-config: libxml-2.0
#include <libxml/tree.h>

static inline void free_string(char* s) { free(s); }
static inline xmlChar *to_xmlcharptr(const char *s) { return (xmlChar *)s; }
static inline char *to_charptr(const xmlChar *s) { return (char *)s; }

*/
import "C"
//import "unsafe"

type NodePtr struct {
	Ptr C.xmlNodePtr
}

type DocPtr struct {
	Ptr C.xmlDocPtr
}

type NsPtr struct {
	Ptr C.xmlNsPtr
}

type Buffer struct {
	Ptr C.xmlBufferPtr
}

// xmlAddChild
func (parent NodePtr) AddChild(cur NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddChild(parent.Ptr, cur.Ptr)}
}

// xmlAddChildList
func (parent NodePtr) AddChildList(cur NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddNextSibling(parent.Ptr, cur.Ptr)}
}

// xmlAddNextSibling
func (cur NodePtr) AddNextSibling(elem NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddNextSibling(cur.Ptr, elem.Ptr)}
}

// xmlAddPrevSibling
func (cur NodePtr) AddPrevSibling(elem NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddPrevSibling(cur.Ptr, elem.Ptr)}
}

// xmlAddSibling
func (cur NodePtr) AddSibling(elem NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddSibling(cur.Ptr, elem.Ptr)}
}

// xmlBufferCreate
func BufferCreate() Buffer {
	return Buffer{C.xmlBufferCreate()}
}

// xmlNewComment
func NewComment(content string) (NodePtr) {
	ptr := C.CString(content)
	defer C.free_string(ptr)
	return NodePtr{C.xmlNewComment(C.to_xmlcharptr(ptr))}
}

// xmlNewDoc
func NewDoc(version string) (DocPtr) {
	ptr := C.CString(version)
	defer C.free_string(ptr)
	return DocPtr{C.xmlNewDoc(C.to_xmlcharptr(ptr))}
}

// xmlNewDocComment
func (doc DocPtr) NewDocComment(content string) (NodePtr) {
	ptr := C.CString(content)
	defer C.free_string(ptr)
	return NodePtr{C.xmlNewDocComment(doc.Ptr, C.to_xmlcharptr(ptr))}
}

// xmlNewDocFragment
func (doc DocPtr) NewDocFragment() (NodePtr) {
	return NodePtr{C.xmlNewDocFragment(doc.Ptr)}
}

// xmlNewNode
func NewNode(ns NsPtr, name string) (NodePtr) {
	ptr := C.CString(name)
	defer C.free_string(ptr)
	return NodePtr{C.xmlNewNode(ns.Ptr, C.to_xmlcharptr(ptr))}
}

// xmlNewNs
func (node NodePtr) NewNs(href string, prefix string) NsPtr {
	ptrh := C.CString(href)
	defer C.free_string(ptrh)
	ptrp := C.CString(prefix)
	defer C.free_string(ptrp)
	return NsPtr{C.xmlNewNs(node.Ptr, C.to_xmlcharptr(ptrh), C.to_xmlcharptr(ptrp))}
}

func (node NodePtr) NodeGetContent() string {
	return C.GoString(C.to_charptr(C.xmlNodeGetContent(node.Ptr)))
}

<commit_msg>Docs are nodes too<commit_after>package xmltree
/*
#cgo pkg-config: libxml-2.0
#include <libxml/tree.h>

static inline void free_string(char* s) { free(s); }
static inline xmlChar *to_xmlcharptr(const char *s) { return (xmlChar *)s; }
static inline char *to_charptr(const xmlChar *s) { return (char *)s; }

*/
import "C"
import "unsafe"

type NodePtr struct {
	Ptr C.xmlNodePtr
}

type DocPtr struct {
	NodePtr
	Ptr C.xmlDocPtr
}

type NsPtr struct {
	Ptr C.xmlNsPtr
}

type Buffer struct {
	Ptr C.xmlBufferPtr
}

// xmlAddChild
func (parent NodePtr) AddChild(cur NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddChild(parent.Ptr, cur.Ptr)}
}

// xmlAddChildList
func (parent NodePtr) AddChildList(cur NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddNextSibling(parent.Ptr, cur.Ptr)}
}

// xmlAddNextSibling
func (cur NodePtr) AddNextSibling(elem NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddNextSibling(cur.Ptr, elem.Ptr)}
}

// xmlAddPrevSibling
func (cur NodePtr) AddPrevSibling(elem NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddPrevSibling(cur.Ptr, elem.Ptr)}
}

// xmlAddSibling
func (cur NodePtr) AddSibling(elem NodePtr) (NodePtr) {
	return NodePtr{C.xmlAddSibling(cur.Ptr, elem.Ptr)}
}

// xmlBufferCreate
func BufferCreate() Buffer {
	return Buffer{C.xmlBufferCreate()}
}

// xmlNewComment
func NewComment(content string) (NodePtr) {
	ptr := C.CString(content)
	defer C.free_string(ptr)
	return NodePtr{C.xmlNewComment(C.to_xmlcharptr(ptr))}
}

// xmlNewDoc
func NewDoc(version string) (DocPtr) {
	ptr := C.CString(version)
	defer C.free_string(ptr)
	doc := C.xmlNewDoc(C.to_xmlcharptr(ptr))
	return DocPtr{
		Ptr: doc,
		NodePtr: NodePtr{C.xmlNodePtr(unsafe.Pointer(doc))},
	}
}

// xmlNewDocComment
func (doc DocPtr) NewDocComment(content string) (NodePtr) {
	ptr := C.CString(content)
	defer C.free_string(ptr)
	return NodePtr{C.xmlNewDocComment(doc.Ptr, C.to_xmlcharptr(ptr))}
}

// xmlNewDocFragment
func (doc DocPtr) NewDocFragment() (NodePtr) {
	return NodePtr{C.xmlNewDocFragment(doc.Ptr)}
}

// xmlNewNode
func NewNode(ns *NsPtr, name string) (NodePtr) {
	ptr := C.CString(name)
	defer C.free_string(ptr)
	if ns != nil {
		return NodePtr{C.xmlNewNode(ns.Ptr, C.to_xmlcharptr(ptr))}
	}
	return NodePtr{C.xmlNewNode(nil, C.to_xmlcharptr(ptr))}
}

// xmlNewNs
func (node NodePtr) NewNs(href string, prefix string) NsPtr {
	ptrh := C.CString(href)
	defer C.free_string(ptrh)
	ptrp := C.CString(prefix)
	defer C.free_string(ptrp)
	return NsPtr{C.xmlNewNs(node.Ptr, C.to_xmlcharptr(ptrh), C.to_xmlcharptr(ptrp))}
}

func (node NodePtr) NodeGetContent() string {
	return C.GoString(C.to_charptr(C.xmlNodeGetContent(node.Ptr)))
}

<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/api"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/env"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/execution/rerun"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/formatter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/refactor"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/validation"
	"github.com/getgauge/gauge/version"

	"github.com/getgauge/gauge/plugin/install"
	"github.com/getgauge/gauge/projectInit"
	"github.com/getgauge/gauge/util"
	flag "github.com/getgauge/mflag"
)

// Command line flags
var daemonize = flag.Bool([]string{"-daemonize"}, false, "Run as a daemon")
var gaugeVersion = flag.Bool([]string{"v", "-version", "version"}, false, "Print the current version and exit. Eg: gauge --version")
var verbosity = flag.Bool([]string{"-verbose"}, false, "Enable step level reporting on console, default being scenario level. Eg: gauge --verbose specs")
var logLevel = flag.String([]string{"-log-level"}, "", "Set level of logging to debug, info, warning, error or critical")
var simpleConsoleOutput = flag.Bool([]string{"-simple-console"}, false, "Removes colouring and simplifies from the console output")
var initialize = flag.String([]string{"-init"}, "", "Initializes project structure in the current directory. Eg: gauge --init java")
var installPlugin = flag.String([]string{"-install"}, "", "Downloads and installs a plugin. Eg: gauge --install java")
var uninstallPlugin = flag.String([]string{"-uninstall"}, "", "Uninstalls a plugin. Eg: gauge --uninstall java")
var installAll = flag.Bool([]string{"-install-all"}, false, "Installs all the plugins specified in project manifest, if not installed. Eg: gauge --install-all")
var update = flag.String([]string{"-update"}, "", "Updates a plugin. Eg: gauge --update java")
var pluginVersion = flag.String([]string{"-plugin-version"}, "", "Version of plugin to be installed. This is used with --install or --uninstall flag.")
var installZip = flag.String([]string{"-file", "f"}, "", "Installs the plugin from zip file. This is used with --install. Eg: gauge --install java -f ZIP_FILE")
var currentEnv = flag.String([]string{"-env"}, "default", "Specifies the environment. If not specified, default will be used")
var addPlugin = flag.String([]string{"-add-plugin"}, "", "Adds the specified non-language plugin to the current project")
var pluginArgs = flag.String([]string{"-plugin-args"}, "", "Specified additional arguments to the plugin. This is used together with --add-plugin")
var specFilesToFormat = flag.String([]string{"-format"}, "", "Formats the specified spec files")
var executeTags = flag.String([]string{"-tags"}, "", "Executes the specs and scenarios tagged with given tags. Eg: gauge --tags tag1,tag2 specs")
var tableRows = flag.String([]string{"-table-rows"}, "", "Executes the specs and scenarios only for the selected rows. Eg: gauge --table-rows \"1-3\" specs/hello.spec")
var apiPort = flag.String([]string{"-api-port"}, "", "Specifies the api port to be used. Eg: gauge --daemonize --api-port 7777")
var refactorSteps = flag.String([]string{"-refactor"}, "", "Refactor steps. Eg: gauge --refactor <old step> <new step> [[spec directories]]")
var parallel = flag.Bool([]string{"-parallel", "p"}, false, "Execute specs in parallel")
var numberOfExecutionStreams = flag.Int([]string{"n"}, util.NumberOfCores(), "Specify number of parallel execution streams")
var distribute = flag.Int([]string{"g", "-group"}, -1, "Specify which group of specification to execute based on -n flag")
var workingDir = flag.String([]string{"-dir"}, ".", "Set the working directory for the current command, accepts a path relative to current directory.")
var strategy = flag.String([]string{"-strategy"}, "lazy", "Set the parallelization strategy for execution. Possible options are: `eager`, `lazy`. Ex: gauge -p --strategy=\"eager\"")
var doNotRandomize = flag.Bool([]string{"-sort", "s"}, false, "Run specs in Alphabetical Order. Eg: gauge -s specs")
var validate = flag.Bool([]string{"-validate", "#-check"}, false, "Check for validation and parse errors. Eg: gauge --validate specs")
var updateAll = flag.Bool([]string{"-update-all"}, false, "Updates all the installed Gauge plugins. Eg: gauge --update-all")
var checkUpdates = flag.Bool([]string{"-check-updates"}, false, "Checks for Gauge and plugins updates. Eg: gauge --check-updates")
var listTemplates = flag.Bool([]string{"-list-templates"}, false, "Lists all the Gauge templates available. Eg: gauge --list-templates")
var machineReadable = flag.Bool([]string{"-machine-readable"}, false, "Used with `--version` to produce JSON output of currently installed Gauge and plugin versions. e.g: gauge --version --machine-readable")
var runFailed = flag.Bool([]string{"-failed"}, false, "Run only the scenarios failed in previous run. Eg: gauge --failed")
var docs = flag.String([]string{"-docs"}, "", "Generate documenation using specified plugin. Eg: gauge --docs <plugin name> specs/")

func main() {
	flag.Parse()
	util.SetWorkingDir(*workingDir)
	initPackageFlags()
	validGaugeProject := true
	err := config.SetProjectRoot(flag.Args())
	if err != nil {
		validGaugeProject = false
	}
	err = rerun.Initialize()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	env.LoadEnv(*currentEnv)
	logger.Initialize(*logLevel)
	if *gaugeVersion && *machineReadable {
		printJSONVersion()
	} else if *machineReadable {
		fmt.Printf("flag '--machine-readable' can only be used with '--version' or '-v'\n\n")
		fmt.Printf("Usage:\n\n")
		flag.PrintDefaults()
		os.Exit(1)
	} else if *gaugeVersion {
		printVersion()
	} else if *initialize != "" {
		projectInit.InitializeProject(*initialize)
	} else if *installZip != "" && *installPlugin != "" {
		install.HandleInstallResult(install.InstallPluginFromZipFile(*installZip, *installPlugin), *installPlugin, true)
	} else if *installPlugin != "" {
		install.HandleInstallResult(install.InstallPlugin(*installPlugin, *pluginVersion), *installPlugin, true)
	} else if *uninstallPlugin != "" {
		install.UninstallPlugin(*uninstallPlugin, *pluginVersion)
	} else if *installAll {
		install.InstallAllPlugins()
	} else if *update != "" {
		install.HandleUpdateResult(install.InstallPlugin(*update, *pluginVersion), *update, true)
	} else if *updateAll {
		install.UpdatePlugins()
	} else if *checkUpdates {
		install.PrintUpdateInfoWithDetails()
	} else if *addPlugin != "" {
		install.AddPluginToProject(*addPlugin, *pluginArgs)
	} else if *listTemplates {
		projectInit.ListTemplates()
	} else if flag.NFlag() == 0 && len(flag.Args()) == 0 {
		printUsage()
		os.Exit(0)
	} else if validGaugeProject {
		var specDirs = []string{common.SpecsDirectoryName}
		if len(flag.Args()) > 0 {
			specDirs = flag.Args()
		}
		if *refactorSteps != "" {
			refactorInit(flag.Args())
		} else if *daemonize {
			api.RunInBackground(*apiPort, specDirs)
		} else if *specFilesToFormat != "" {
			formatter.FormatSpecFilesIn(*specFilesToFormat)
		} else if *validate {
			validation.Validate(flag.Args())
		} else if *docs != "" {
			gaugeConnectionHandler := api.Start(specDirs)
			plugin.GenerateDoc(*docs, specDirs, gaugeConnectionHandler.ConnectionPortNumber())
		} else {
			exitCode := execution.ExecuteSpecs(specDirs)
			os.Exit(exitCode)
		}
	} else {
		logger.Fatalf(err.Error())
	}
}

func refactorInit(args []string) {
	if len(args) < 1 {
		logger.Fatalf("Flag needs at least two arguments: --refactor\nUsage : gauge --refactor <old step> <new step> [[spec directories]]")
	}
	var specDirs = []string{common.SpecsDirectoryName}
	if len(args) > 1 {
		specDirs = args[1:]
	}
	startChan := api.StartAPI()
	refactor.RefactorSteps(*refactorSteps, args[0], startChan, specDirs)
}

func printJSONVersion() {
	type pluginJSON struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	}
	type versionJSON struct {
		Version string        `json:"version"`
		Plugins []*pluginJSON `json:"plugins"`
	}
	gaugeVersion := versionJSON{version.FullVersion(), make([]*pluginJSON, 0)}
	allPluginsWithVersion, err := plugin.GetAllInstalledPluginsWithVersion()
	for _, pluginInfo := range allPluginsWithVersion {
		gaugeVersion.Plugins = append(gaugeVersion.Plugins, &pluginJSON{pluginInfo.Name, filepath.Base(pluginInfo.Path)})
	}
	b, err := json.MarshalIndent(gaugeVersion, "", "    ")
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Println(fmt.Sprintf("%s\n", string(b)))
}

func printVersion() {
	fmt.Printf("Gauge version: %s\n\n", version.FullVersion())
	fmt.Println("Plugins\n-------")
	allPluginsWithVersion, err := plugin.GetAllInstalledPluginsWithVersion()
	if err != nil {
		fmt.Println("No plugins found")
		fmt.Println("Plugins can be installed with `gauge --install {plugin-name}`")
		os.Exit(0)
	}
	for _, pluginInfo := range allPluginsWithVersion {
		fmt.Printf("%s (%s)\n", pluginInfo.Name, filepath.Base(pluginInfo.Path))
	}
}

func printUsage() {
	fmt.Printf("gauge version %s\n", version.FullVersion())
	fmt.Printf("Copyright %d ThoughtWorks, Inc.\n\n", time.Now().Year())
	fmt.Println("Usage:")
	fmt.Println("\tgauge specs/")
	fmt.Println("\tgauge specs/spec_name.spec")
	fmt.Println("\nOptions:")
	flag.PrintDefaults()
}

func initPackageFlags() {
	if *parallel {
		*simpleConsoleOutput = true
		reporter.IsParallel = true
	}
	reporter.SimpleConsoleOutput = *simpleConsoleOutput
	reporter.Verbose = *verbosity
	execution.ExecuteTags = *executeTags
	execution.TableRows = *tableRows
	execution.NumberOfExecutionStreams = *numberOfExecutionStreams
	execution.InParallel = *parallel
	execution.Strategy = *strategy
	filter.ExecuteTags = *executeTags
	filter.DoNotRandomize = *doNotRandomize
	filter.Distribute = *distribute
	filter.NumberOfExecutionStreams = *numberOfExecutionStreams
	reporter.NumberOfExecutionStreams = *numberOfExecutionStreams
	rerun.RunFailed = *runFailed
	if *distribute != -1 {
		execution.Strategy = execution.Eager
	}
}
<commit_msg>Showing usages if gauge invoked in invalid gauge project #464<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/api"
	"github.com/getgauge/gauge/config"
	"github.com/getgauge/gauge/env"
	"github.com/getgauge/gauge/execution"
	"github.com/getgauge/gauge/execution/rerun"
	"github.com/getgauge/gauge/filter"
	"github.com/getgauge/gauge/formatter"
	"github.com/getgauge/gauge/logger"
	"github.com/getgauge/gauge/plugin"
	"github.com/getgauge/gauge/refactor"
	"github.com/getgauge/gauge/reporter"
	"github.com/getgauge/gauge/validation"
	"github.com/getgauge/gauge/version"

	"github.com/getgauge/gauge/plugin/install"
	"github.com/getgauge/gauge/projectInit"
	"github.com/getgauge/gauge/util"
	flag "github.com/getgauge/mflag"
)

// Command line flags
var daemonize = flag.Bool([]string{"-daemonize"}, false, "Run as a daemon")
var gaugeVersion = flag.Bool([]string{"v", "-version", "version"}, false, "Print the current version and exit. Eg: gauge --version")
var verbosity = flag.Bool([]string{"-verbose"}, false, "Enable step level reporting on console, default being scenario level. Eg: gauge --verbose specs")
var logLevel = flag.String([]string{"-log-level"}, "", "Set level of logging to debug, info, warning, error or critical")
var simpleConsoleOutput = flag.Bool([]string{"-simple-console"}, false, "Removes colouring and simplifies from the console output")
var initialize = flag.String([]string{"-init"}, "", "Initializes project structure in the current directory. Eg: gauge --init java")
var installPlugin = flag.String([]string{"-install"}, "", "Downloads and installs a plugin. Eg: gauge --install java")
var uninstallPlugin = flag.String([]string{"-uninstall"}, "", "Uninstalls a plugin. Eg: gauge --uninstall java")
var installAll = flag.Bool([]string{"-install-all"}, false, "Installs all the plugins specified in project manifest, if not installed. Eg: gauge --install-all")
var update = flag.String([]string{"-update"}, "", "Updates a plugin. Eg: gauge --update java")
var pluginVersion = flag.String([]string{"-plugin-version"}, "", "Version of plugin to be installed. This is used with --install or --uninstall flag.")
var installZip = flag.String([]string{"-file", "f"}, "", "Installs the plugin from zip file. This is used with --install. Eg: gauge --install java -f ZIP_FILE")
var currentEnv = flag.String([]string{"-env"}, "default", "Specifies the environment. If not specified, default will be used")
var addPlugin = flag.String([]string{"-add-plugin"}, "", "Adds the specified non-language plugin to the current project")
var pluginArgs = flag.String([]string{"-plugin-args"}, "", "Specified additional arguments to the plugin. This is used together with --add-plugin")
var specFilesToFormat = flag.String([]string{"-format"}, "", "Formats the specified spec files")
var executeTags = flag.String([]string{"-tags"}, "", "Executes the specs and scenarios tagged with given tags. Eg: gauge --tags tag1,tag2 specs")
var tableRows = flag.String([]string{"-table-rows"}, "", "Executes the specs and scenarios only for the selected rows. Eg: gauge --table-rows \"1-3\" specs/hello.spec")
var apiPort = flag.String([]string{"-api-port"}, "", "Specifies the api port to be used. Eg: gauge --daemonize --api-port 7777")
var refactorSteps = flag.String([]string{"-refactor"}, "", "Refactor steps. Eg: gauge --refactor <old step> <new step> [[spec directories]]")
var parallel = flag.Bool([]string{"-parallel", "p"}, false, "Execute specs in parallel")
var numberOfExecutionStreams = flag.Int([]string{"n"}, util.NumberOfCores(), "Specify number of parallel execution streams")
var distribute = flag.Int([]string{"g", "-group"}, -1, "Specify which group of specification to execute based on -n flag")
var workingDir = flag.String([]string{"-dir"}, ".", "Set the working directory for the current command, accepts a path relative to current directory.")
var strategy = flag.String([]string{"-strategy"}, "lazy", "Set the parallelization strategy for execution. Possible options are: `eager`, `lazy`. Ex: gauge -p --strategy=\"eager\"")
var doNotRandomize = flag.Bool([]string{"-sort", "s"}, false, "Run specs in Alphabetical Order. Eg: gauge -s specs")
var validate = flag.Bool([]string{"-validate", "#-check"}, false, "Check for validation and parse errors. Eg: gauge --validate specs")
var updateAll = flag.Bool([]string{"-update-all"}, false, "Updates all the installed Gauge plugins. Eg: gauge --update-all")
var checkUpdates = flag.Bool([]string{"-check-updates"}, false, "Checks for Gauge and plugins updates. Eg: gauge --check-updates")
var listTemplates = flag.Bool([]string{"-list-templates"}, false, "Lists all the Gauge templates available. Eg: gauge --list-templates")
var machineReadable = flag.Bool([]string{"-machine-readable"}, false, "Used with `--version` to produce JSON output of currently installed Gauge and plugin versions. e.g: gauge --version --machine-readable")
var runFailed = flag.Bool([]string{"-failed"}, false, "Run only the scenarios failed in previous run. Eg: gauge --failed")
var docs = flag.String([]string{"-docs"}, "", "Generate documenation using specified plugin. Eg: gauge --docs <plugin name> specs/")

func main() {
	flag.Parse()
	util.SetWorkingDir(*workingDir)
	initPackageFlags()
	validGaugeProject := true
	err := config.SetProjectRoot(flag.Args())
	if err != nil {
		validGaugeProject = false
	}
	err = rerun.Initialize()
	if err != nil {
		fmt.Println(err)
		os.Exit(0)
	}
	env.LoadEnv(*currentEnv)
	logger.Initialize(*logLevel)
	if *gaugeVersion && *machineReadable {
		printJSONVersion()
	} else if *machineReadable {
		fmt.Printf("flag '--machine-readable' can only be used with '--version' or '-v'\n\n")
		fmt.Printf("Usage:\n\n")
		flag.PrintDefaults()
		os.Exit(1)
	} else if *gaugeVersion {
		printVersion()
	} else if *initialize != "" {
		projectInit.InitializeProject(*initialize)
	} else if *installZip != "" && *installPlugin != "" {
		install.HandleInstallResult(install.InstallPluginFromZipFile(*installZip, *installPlugin), *installPlugin, true)
	} else if *installPlugin != "" {
		install.HandleInstallResult(install.InstallPlugin(*installPlugin, *pluginVersion), *installPlugin, true)
	} else if *uninstallPlugin != "" {
		install.UninstallPlugin(*uninstallPlugin, *pluginVersion)
	} else if *installAll {
		install.InstallAllPlugins()
	} else if *update != "" {
		install.HandleUpdateResult(install.InstallPlugin(*update, *pluginVersion), *update, true)
	} else if *updateAll {
		install.UpdatePlugins()
	} else if *checkUpdates {
		install.PrintUpdateInfoWithDetails()
	} else if *addPlugin != "" {
		install.AddPluginToProject(*addPlugin, *pluginArgs)
	} else if *listTemplates {
		projectInit.ListTemplates()
	} else if flag.NFlag() == 0 && len(flag.Args()) == 0 {
		printUsage()
		os.Exit(0)
	} else if validGaugeProject {
		var specDirs = []string{common.SpecsDirectoryName}
		if len(flag.Args()) > 0 {
			specDirs = flag.Args()
		}
		if *refactorSteps != "" {
			refactorInit(flag.Args())
		} else if *daemonize {
			api.RunInBackground(*apiPort, specDirs)
		} else if *specFilesToFormat != "" {
			formatter.FormatSpecFilesIn(*specFilesToFormat)
		} else if *validate {
			validation.Validate(flag.Args())
		} else if *docs != "" {
			gaugeConnectionHandler := api.Start(specDirs)
			plugin.GenerateDoc(*docs, specDirs, gaugeConnectionHandler.ConnectionPortNumber())
		} else {
			exitCode := execution.ExecuteSpecs(specDirs)
			os.Exit(exitCode)
		}
	} else {
		if err != nil {
			logger.Fatalf(err.Error())
		}
		logger.Info("Not a valid Gauge project.")
		printUsage()
	}
}

func refactorInit(args []string) {
	if len(args) < 1 {
		logger.Fatalf("Flag needs at least two arguments: --refactor\nUsage : gauge --refactor <old step> <new step> [[spec directories]]")
	}
	var specDirs = []string{common.SpecsDirectoryName}
	if len(args) > 1 {
		specDirs = args[1:]
	}
	startChan := api.StartAPI()
	refactor.RefactorSteps(*refactorSteps, args[0], startChan, specDirs)
}

func printJSONVersion() {
	type pluginJSON struct {
		Name    string `json:"name"`
		Version string `json:"version"`
	}
	type versionJSON struct {
		Version string        `json:"version"`
		Plugins []*pluginJSON `json:"plugins"`
	}
	gaugeVersion := versionJSON{version.FullVersion(), make([]*pluginJSON, 0)}
	allPluginsWithVersion, err := plugin.GetAllInstalledPluginsWithVersion()
	for _, pluginInfo := range allPluginsWithVersion {
		gaugeVersion.Plugins = append(gaugeVersion.Plugins, &pluginJSON{pluginInfo.Name, filepath.Base(pluginInfo.Path)})
	}
	b, err := json.MarshalIndent(gaugeVersion, "", "    ")
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Println(fmt.Sprintf("%s\n", string(b)))
}

func printVersion() {
	fmt.Printf("Gauge version: %s\n\n", version.FullVersion())
	fmt.Println("Plugins\n-------")
	allPluginsWithVersion, err := plugin.GetAllInstalledPluginsWithVersion()
	if err != nil {
		fmt.Println("No plugins found")
		fmt.Println("Plugins can be installed with `gauge --install {plugin-name}`")
		os.Exit(0)
	}
	for _, pluginInfo := range allPluginsWithVersion {
		fmt.Printf("%s (%s)\n", pluginInfo.Name, filepath.Base(pluginInfo.Path))
	}
}

func printUsage() {
	fmt.Printf("Gauge version %s\n", version.FullVersion())
	fmt.Printf("Copyright %d ThoughtWorks, Inc.\n\n", time.Now().Year())
	fmt.Println("Usage:")
	fmt.Println("\tgauge specs/")
	fmt.Println("\tgauge specs/spec_name.spec")
	fmt.Println("\nOptions:")
	flag.PrintDefaults()
}

func initPackageFlags() {
	if *parallel {
		*simpleConsoleOutput = true
		reporter.IsParallel = true
	}
	reporter.SimpleConsoleOutput = *simpleConsoleOutput
	reporter.Verbose = *verbosity
	execution.ExecuteTags = *executeTags
	execution.TableRows = *tableRows
	execution.NumberOfExecutionStreams = *numberOfExecutionStreams
	execution.InParallel = *parallel
	execution.Strategy = *strategy
	filter.ExecuteTags = *executeTags
	filter.DoNotRandomize = *doNotRandomize
	filter.Distribute = *distribute
	filter.NumberOfExecutionStreams = *numberOfExecutionStreams
	reporter.NumberOfExecutionStreams = *numberOfExecutionStreams
	rerun.RunFailed = *runFailed
	if *distribute != -1 {
		execution.Strategy = execution.Eager
	}
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/opts-go"
	"encoding/base64"
	"fmt"
	"io"
	"os"
)

const (
	version = `base64 (go coretuils) 0.1
Packaged by Prabir Shrestha
Copyright (c) 2014 Prabir Shrestha
License MIT: This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Prabir Shrestha`

	usage = `Usage: base64 [-di --wrap=<COLS>] [FILE]
Base64 encode or decode FILE, or standard input, to standard output.

  -d, --decode          decode data
  -i, --ignore-garbage  when decoding, ignore non-alphabet characters
  -w, --wrap=COLS       wrap encoded lines after COLS character (default 76).

      --help     display this help and exit
      --version  output version information and exit

With no FILE, or when FILE is -, read standard input.

The data are encoded as describe for base64 alpahbet in RFC 4648.
When decoding, the input may contain new lines in addition to the bytes of
the format base64 alphabet. Use --ignore-garbage to attempt to recover
from any other non-alphabet bytes in the encoded stream.`
)

func main() {
	opts.Usage = usage
	showHelp := opts.Flag("", "--help", "Help")
	showVersion := opts.Flag("", "--version", "Version")
	decode := opts.Flag("-d", "--decode", "Decode")

	opts.Parse()

	if *showHelp {
		fmt.Print(usage)
		os.Exit(0)
	}

	if *showVersion {
		fmt.Print(version)
		os.Exit(0)
	}

	if *decode {
		d := base64.NewDecoder(base64.StdEncoding, os.Stdin)
		defer os.Stdin.Close()
		io.Copy(os.Stdout, d)
	} else {
		e := base64.NewEncoder(base64.StdEncoding, os.Stdout)
		defer e.Close()
		io.Copy(e, os.Stdin)
	}
}
<commit_msg>added support for file for base64<commit_after>package main

import (
	"code.google.com/p/opts-go"
	"encoding/base64"
	"fmt"
	"io"
	"os"
)

const (
	version = `base64 (go coretuils) 0.1
Packaged by Prabir Shrestha
Copyright (c) 2014 Prabir Shrestha
License MIT: This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Prabir Shrestha`

	usage = `Usage: base64 [-di --wrap=<COLS>] [FILE]
Base64 encode or decode FILE, or standard input, to standard output.

  -d, --decode          decode data
  -i, --ignore-garbage  when decoding, ignore non-alphabet characters
  -w, --wrap=COLS       wrap encoded lines after COLS character (default 76).

      --help     display this help and exit
      --version  output version information and exit

With no FILE, or when FILE is -, read standard input.

The data are encoded as describe for base64 alpahbet in RFC 4648.
When decoding, the input may contain new lines in addition to the bytes of
the format base64 alphabet. Use --ignore-garbage to attempt to recover
from any other non-alphabet bytes in the encoded stream.`
)

func main() {
	opts.Usage = usage
	showHelp := opts.Flag("", "--help", "Help")
	showVersion := opts.Flag("", "--version", "Version")
	decode := opts.Flag("-d", "--decode", "Decode")

	opts.Parse()

	if *showHelp {
		fmt.Print(usage)
		os.Exit(0)
	}

	if *showVersion {
		fmt.Print(version)
		os.Exit(0)
	}

	argsLen := len(opts.Args)

	var (
		reader io.ReadCloser
		writer io.WriteCloser
	)

	if argsLen == 0 {
		reader = os.Stdin
		writer = os.Stdout
	} else if argsLen == 1 {
		file, err := os.Open(opts.Args[0])
		if err != nil {
			fmt.Print(err)
			os.Exit(1)
		}
		reader = file
		writer = os.Stdout
	} else {
		fmt.Printf(`base64: extra operand '%s'
Try 'base64 --help' for more information.`, opts.Args[1])
		os.Exit(1)
	}

	if *decode {
		d := base64.NewDecoder(base64.StdEncoding, reader)
		defer reader.Close()
		defer writer.Close()
		io.Copy(writer, d)
	} else {
		e := base64.NewEncoder(base64.StdEncoding, writer)
		defer e.Close()
		defer reader.Close()
		defer writer.Close()
		io.Copy(e, reader)
	}
}
<|endoftext|>


<commit_before>/**
  Steam Library For Go
  Copyright (C) 2016 Ahmed Samy <f.fallen45@gmail.com>
  Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
package steam

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"math/big"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"time"
)

type LoginResponse struct {
	Success      bool   `json:"success"`
	PublicKeyMod string `json:"publickey_mod"`
	PublicKeyExp string `json:"publickey_exp"`
	Timestamp    string
	TokenGID     string
}

type OAuth struct {
	SteamID       SteamID `json:"steamid,string"`
	Token         string  `json:"oauth_token"`
	WGToken       string  `json:"wgtoken"`
	WGTokenSecure string  `json:"wgtoken_secure"`
	WebCookie     string  `json:"webcookie"`
}

type LoginSession struct {
	Success           bool   `json:"success"`
	LoginComplete     bool   `json:"login_complete"`
	RequiresTwoFactor bool   `json:"requires_twofactor"`
	Message           string `json:"message"`
	RedirectURI       string `json:"redirect_uri"`
	OAuthInfo         string `json:"oauth"`
}

type Community struct {
	client    *http.Client
	oauth     OAuth
	sessionID string
	apiKey    string
	deviceID  string
}

const (
	deviceIDCookieName = "steamMachineAuth"

	httpXRequestedWithValue = "com.valvesoftware.android.steam.community"
	httpUserAgentValue      = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
	httpAcceptValue         = "text/javascript, text/html, application/xml, text/xml, */*"
)

var (
	ErrUnableToLogin             = errors.New("unable to login")
	ErrInvalidUsername           = errors.New("invalid username")
	ErrNeedTwoFactor             = errors.New("invalid twofactor code")
	ErrMachineAuthCookieNotFound = errors.New("machine auth cookie not found")
)

func (community *Community) proceedDirectLogin(response *LoginResponse, accountName, password, sharedSecret string) error {
	n := &big.Int{}
	n.SetString(response.PublicKeyMod, 16)

	exp, err := strconv.ParseInt(response.PublicKeyExp, 16, 32)
	if err != nil {
		return err
	}

	pub := &rsa.PublicKey{N: n, E: int(exp)}
	rsaOut, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(password))
	if err != nil {
		return err
	}

	var twoFactorCode string
	if sharedSecret != "" {
		if twoFactorCode, err = GenerateTwoFactorCode(sharedSecret); err != nil {
			return err
		}
	}

	params := url.Values{
		"captcha_text":      {""},
		"captchagid":        {"-1"},
		"emailauth":         {""},
		"emailsteamid":      {""},
		"password":          {base64.StdEncoding.EncodeToString(rsaOut)},
		"remember_login":    {"true"},
		"rsatimestamp":      {response.Timestamp},
		"twofactorcode":     {twoFactorCode},
		"username":          {accountName},
		"oauth_client_id":   {"DE45CD61"},
		"oauth_scope":       {"read_profile write_profile read_client write_client"},
		"loginfriendlyname": {"#login_emailauth_friendlyname_mobile"},
		"donotcache":        {strconv.FormatInt(time.Now().Unix()*1000, 10)},
	}

	req, err := http.NewRequest(http.MethodPost, "https://steamcommunity.com/login/dologin/?"+params.Encode(), nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var session LoginSession
	if err := json.NewDecoder(resp.Body).Decode(&session); err != nil {
		return err
	}

	if !session.Success {
		if session.RequiresTwoFactor {
			return ErrNeedTwoFactor
		}

		return ErrUnableToLogin
	}

	randomBytes := make([]byte, 6)
	if _, err := rand.Read(randomBytes); err != nil {
		return err
	}

	sessionID := make([]byte, hex.EncodedLen(len(randomBytes)))
	hex.Encode(sessionID, randomBytes)
	community.sessionID = string(sessionID)

	url, _ := url.Parse("https://steamcommunity.com")
	cookies := community.client.Jar.Cookies(url)
	for _, cookie := range cookies {
		if cookie.Name == "mobileClient" || cookie.Name == "mobileClientVersion" {
			// remove by setting max age -1
			cookie.MaxAge = -1
		}
	}

	if sharedSecret != "" {
		for _, cookie := range cookies {
			name := cookie.Name
			if len(name) <= len(deviceIDCookieName) || name[:len(deviceIDCookieName)] != deviceIDCookieName {
				continue
			}

			shaSum := sha1.Sum([]byte(name[len(deviceIDCookieName):]))
			halfHash := shaSum[:sha1.Size/2]
			sum := make([]byte, hex.EncodedLen(len(halfHash)))
			hex.Encode(sum, halfHash)

			var deviceID bytes.Buffer
			deviceID.Grow(8 + 4 + 20)
			deviceID.WriteString("android:")
			deviceID.Write(sum[:4])
			deviceID.WriteByte('-')
			deviceID.Write(sum[4:8])
			deviceID.WriteByte('-')
			deviceID.Write(sum[8:12])
			deviceID.WriteByte('-')
			deviceID.Write(sum[12:16])
			deviceID.WriteByte('-')
			deviceID.Write(sum[16:20])
			community.deviceID = deviceID.String()
			break
		}

		if community.deviceID == "" {
			return ErrMachineAuthCookieNotFound
		}
	}

	community.client.Jar.SetCookies(
		url,
		append(cookies, &http.Cookie{
			Name:  "sessionid",
			Value: community.sessionID,
		}),
	)

	if err := json.Unmarshal([]byte(session.OAuthInfo), &community.oauth); err != nil {
		return err
	}
	return nil
}

func (community *Community) Login(accountName, password, sharedSecret string) error {
	req, err := http.NewRequest(http.MethodPost, "https://steamcommunity.com/login/getrsakey?username="+accountName, nil)
	if err != nil {
		return err
	}

	jar, err := cookiejar.New(nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	cookies := []*http.Cookie{
		&http.Cookie{Name: "mobileClientVersion", Value: "0 (2.1.3)"},
		&http.Cookie{Name: "mobileClient", Value: "android"},
		&http.Cookie{Name: "Steam_Language", Value: "english"},
		&http.Cookie{Name: "timezoneOffset", Value: "0,0"},
	}
	url, _ := url.Parse("https://steamcommunity.com")
	jar.SetCookies(url, cookies)

	// Construct the client
	community.client = &http.Client{Jar: jar}

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var response LoginResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return err
	}

	if !response.Success {
		return ErrInvalidUsername
	}

	return community.proceedDirectLogin(&response, accountName, password, sharedSecret)
}

func (community *Community) GetSteamID() SteamID {
	return community.oauth.SteamID
}
<commit_msg>Strengthen device ID<commit_after>/**
  Steam Library For Go
  Copyright (C) 2016 Ahmed Samy <f.fallen45@gmail.com>
  Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
package steam

import (
	"crypto/md5"
	"crypto/rand"
	"crypto/rsa"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/big"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"time"
)

type LoginResponse struct {
	Success      bool   `json:"success"`
	PublicKeyMod string `json:"publickey_mod"`
	PublicKeyExp string `json:"publickey_exp"`
	Timestamp    string
	TokenGID     string
}

type OAuth struct {
	SteamID       SteamID `json:"steamid,string"`
	Token         string  `json:"oauth_token"`
	WGToken       string  `json:"wgtoken"`
	WGTokenSecure string  `json:"wgtoken_secure"`
	WebCookie     string  `json:"webcookie"`
}

type LoginSession struct {
	Success           bool   `json:"success"`
	LoginComplete     bool   `json:"login_complete"`
	RequiresTwoFactor bool   `json:"requires_twofactor"`
	Message           string `json:"message"`
	RedirectURI       string `json:"redirect_uri"`
	OAuthInfo         string `json:"oauth"`
}

type Community struct {
	client    *http.Client
	oauth     OAuth
	sessionID string
	apiKey    string
	deviceID  string
}

const (
	deviceIDCookieName = "steamMachineAuth"

	httpXRequestedWithValue = "com.valvesoftware.android.steam.community"
	httpUserAgentValue      = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
	httpAcceptValue         = "text/javascript, text/html, application/xml, text/xml, */*"
)

var (
	ErrUnableToLogin             = errors.New("unable to login")
	ErrInvalidUsername           = errors.New("invalid username")
	ErrNeedTwoFactor             = errors.New("invalid twofactor code")
	ErrMachineAuthCookieNotFound = errors.New("machine auth cookie not found")
)

func (community *Community) proceedDirectLogin(response *LoginResponse, accountName, password, sharedSecret string) error {
	n := &big.Int{}
	n.SetString(response.PublicKeyMod, 16)

	exp, err := strconv.ParseInt(response.PublicKeyExp, 16, 32)
	if err != nil {
		return err
	}

	pub := &rsa.PublicKey{N: n, E: int(exp)}
	rsaOut, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(password))
	if err != nil {
		return err
	}

	var twoFactorCode string
	if sharedSecret != "" {
		if twoFactorCode, err = GenerateTwoFactorCode(sharedSecret); err != nil {
			return err
		}
	}

	params := url.Values{
		"captcha_text":      {""},
		"captchagid":        {"-1"},
		"emailauth":         {""},
		"emailsteamid":      {""},
		"password":          {base64.StdEncoding.EncodeToString(rsaOut)},
		"remember_login":    {"true"},
		"rsatimestamp":      {response.Timestamp},
		"twofactorcode":     {twoFactorCode},
		"username":          {accountName},
		"oauth_client_id":   {"DE45CD61"},
		"oauth_scope":       {"read_profile write_profile read_client write_client"},
		"loginfriendlyname": {"#login_emailauth_friendlyname_mobile"},
		"donotcache":        {strconv.FormatInt(time.Now().Unix()*1000, 10)},
	}

	req, err := http.NewRequest(http.MethodPost, "https://steamcommunity.com/login/dologin/?"+params.Encode(), nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var session LoginSession
	if err := json.NewDecoder(resp.Body).Decode(&session); err != nil {
		return err
	}

	if !session.Success {
		if session.RequiresTwoFactor {
			return ErrNeedTwoFactor
		}

		return ErrUnableToLogin
	}

	randomBytes := make([]byte, 6)
	if _, err := rand.Read(randomBytes); err != nil {
		return err
	}

	sessionID := make([]byte, hex.EncodedLen(len(randomBytes)))
	hex.Encode(sessionID, randomBytes)
	community.sessionID = string(sessionID)

	url, _ := url.Parse("https://steamcommunity.com")
	cookies := community.client.Jar.Cookies(url)
	for _, cookie := range cookies {
		if cookie.Name == "mobileClient" || cookie.Name == "mobileClientVersion" {
			// remove by setting max age -1
			cookie.MaxAge = -1
		}
	}

	if sharedSecret != "" {
		sum := md5.Sum([]byte(sharedSecret))
		community.deviceID = fmt.Sprintf(
			"android:%x-%x-%x-%x-%x",
			sum[:2], sum[2:4], sum[4:6], sum[6:8], sum[8:10],
		)
	}

	community.client.Jar.SetCookies(
		url,
		append(cookies, &http.Cookie{
			Name:  "sessionid",
			Value: community.sessionID,
		}),
	)

	return json.Unmarshal([]byte(session.OAuthInfo), &community.oauth)
}

func (community *Community) Login(accountName, password, sharedSecret string) error {
	req, err := http.NewRequest(http.MethodPost, "https://steamcommunity.com/login/getrsakey?username="+accountName, nil)
	if err != nil {
		return err
	}

	jar, err := cookiejar.New(nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	cookies := []*http.Cookie{
		&http.Cookie{Name: "mobileClientVersion", Value: "0 (2.1.3)"},
		&http.Cookie{Name: "mobileClient", Value: "android"},
		&http.Cookie{Name: "Steam_Language", Value: "english"},
		&http.Cookie{Name: "timezoneOffset", Value: "0,0"},
	}
	url, _ := url.Parse("https://steamcommunity.com")
	jar.SetCookies(url, cookies)

	// Construct the client
	community.client = &http.Client{Jar: jar}

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var response LoginResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return err
	}

	if !response.Success {
		return ErrInvalidUsername
	}

	return community.proceedDirectLogin(&response, accountName, password, sharedSecret)
}

func (community *Community) GetSteamID() SteamID {
	return community.oauth.SteamID
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/big"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"time"
)

/* Uppercase because of JSON.  */
type LoginResponse struct {
	Success      bool   `json:"success"`
	PublicKeyMod string `json:"publickey_mod"`
	PublicKeyExp string `json:"publickey_exp"`
	Timestamp    string
	TokenGID     string
}

type OAuth struct {
	SteamID       string `json:"steamid"`
	Token         string `json:"oauth_token"`
	WGToken       string `json:"wgtoken"`
	WGTokenSecure string `json:"wgtoken_secure"`
	WebCookie     string `json:"webcookie"`
}

type LoginSession struct {
	Success           bool   `json:"success"`
	LoginComplete     bool   `json:"login_complete"`
	RequiresTwoFactor bool   `json:"requires_twofactor"`
	Message           string `json:"message"`
	RedirectURI       string `json:"redirect_uri"`
	OAuthInfo         string `json:"oauth"`
}

type Community struct {
	client    *http.Client
	session   LoginSession
	sessionID string
}

const (
	httpXRequestedWithValue = "com.valvesoftware.android.steam.community"
	httpUserAgentValue      = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
	httpAcceptValue         = "text/javascript, text/html, application/xml, text/xml, */*"
)

var (
	ErrUnableToLogin       = errors.New("unable to login")
	ErrInvalidUsername     = errors.New("invalid username")
	ErrInsufficientEntropy = errors.New("insufficient entropy")
)

func (community *Community) proceedDirectLogin(response *LoginResponse, accountName, password, twoFactor string) error {
	n := &big.Int{}
	n.SetString(response.PublicKeyMod, 16)

	exp, err := strconv.ParseInt(response.PublicKeyExp, 16, 32)
	if err != nil {
		return err
	}

	itimestamp, err := strconv.ParseInt(response.Timestamp, 10, 64)
	if err != nil {
		return err
	}

	pub := &rsa.PublicKey{N: n, E: int(exp)}
	rsaOut, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(password))
	if err != nil {
		return err
	}

	params := fmt.Sprintf(`https://steamcommunity.com/login/dologin/?captcha_text=''&captchagid=-1&emailauth=''&emailsteamid=''&password=%s&remember_login=true&rsatimestamp=%d&twofactorcode=%s&username=%s&oauth_client_id=DE45CD61&oauth_scope=read_profile write_profile read_client write_client&loginfriendlyname=#login_emailauth_friendlyname_mobile&donotcache=%d`,
		url.QueryEscape(base64.StdEncoding.EncodeToString(rsaOut)),
		itimestamp,
		twoFactor,
		accountName,
		time.Now().Unix()*1000)
	req, err := http.NewRequest(http.MethodPost, params, nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var session LoginSession
	if err := json.NewDecoder(resp.Body).Decode(&session); err != nil {
		return err
	}

	if !session.Success {
		return ErrUnableToLogin
	}

	randomBytes := make([]byte, 6)
	if count, err := rand.Read(randomBytes); err != nil {
		return err
	} else if count != 6 {
		return ErrInsufficientEntropy
	}

	sessionID := make([]byte, hex.EncodedLen(len(randomBytes)))
	hex.Encode(sessionID, randomBytes)

	community.session = session
	community.sessionID = string(sessionID)
	fmt.Println(session)
	fmt.Println(community.sessionID)

	url, _ := url.Parse("https://steamcommunity.com")
	cookies := community.client.Jar.Cookies(url)
	community.client.Jar.SetCookies(
		url,
		append(cookies, &http.Cookie{
			Name:  "sessionid",
			Value: community.sessionID,
		}),
	)

	var oauth OAuth
	if err := json.Unmarshal([]byte(session.OAuthInfo), &oauth); err != nil {
		return err
	}
	return nil
}

func (community *Community) login(accountName, password, twoFactor string) error {
	req, err := http.NewRequest(http.MethodPost, "https://steamcommunity.com/login/getrsakey?username="+accountName, nil)
	if err != nil {
		return err
	}

	jar, err := cookiejar.New(nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	cookies := []*http.Cookie{
		&http.Cookie{Name: "mobileClientVersion", Value: "0 (2.1.3)"},
		&http.Cookie{Name: "mobileClient", Value: "android"},
		&http.Cookie{Name: "Steam_Language", Value: "english"},
		&http.Cookie{Name: "timezoneOffset", Value: "0,0"},
	}
	url, _ := url.Parse("https://steamcommunity.com")
	jar.SetCookies(url, cookies)

	// Construct the client
	community.client = &http.Client{Jar: jar}

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var response LoginResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return err
	}

	if !response.Success {
		return ErrInvalidUsername
	}

	return community.proceedDirectLogin(&response, accountName, password, twoFactor)
}
<commit_msg>login: store oauth info only, no need for entire session<commit_after>package main

import (
	"crypto/rand"
	"crypto/rsa"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/big"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"time"
)

type LoginResponse struct {
	Success      bool   `json:"success"`
	PublicKeyMod string `json:"publickey_mod"`
	PublicKeyExp string `json:"publickey_exp"`
	Timestamp    string
	TokenGID     string
}

type OAuth struct {
	SteamID       string `json:"steamid"`
	Token         string `json:"oauth_token"`
	WGToken       string `json:"wgtoken"`
	WGTokenSecure string `json:"wgtoken_secure"`
	WebCookie     string `json:"webcookie"`
}

type LoginSession struct {
	Success           bool   `json:"success"`
	LoginComplete     bool   `json:"login_complete"`
	RequiresTwoFactor bool   `json:"requires_twofactor"`
	Message           string `json:"message"`
	RedirectURI       string `json:"redirect_uri"`
	OAuthInfo         string `json:"oauth"`
}

type Community struct {
	client    *http.Client
	oauth     OAuth
	sessionID string
}

const (
	httpXRequestedWithValue = "com.valvesoftware.android.steam.community"
	httpUserAgentValue      = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
	httpAcceptValue         = "text/javascript, text/html, application/xml, text/xml, */*"
)

var (
	ErrUnableToLogin       = errors.New("unable to login")
	ErrInvalidUsername     = errors.New("invalid username")
	ErrInsufficientEntropy = errors.New("insufficient entropy")
	ErrNeedTwoFactor       = errors.New("invalid twofactor code")
)

func (community *Community) proceedDirectLogin(response *LoginResponse, accountName, password, twoFactor string) error {
	n := &big.Int{}
	n.SetString(response.PublicKeyMod, 16)

	exp, err := strconv.ParseInt(response.PublicKeyExp, 16, 32)
	if err != nil {
		return err
	}

	itimestamp, err := strconv.ParseInt(response.Timestamp, 10, 64)
	if err != nil {
		return err
	}

	pub := &rsa.PublicKey{N: n, E: int(exp)}
	rsaOut, err := rsa.EncryptPKCS1v15(rand.Reader, pub, []byte(password))
	if err != nil {
		return err
	}

	params := fmt.Sprintf(`https://steamcommunity.com/login/dologin/?captcha_text=''&captchagid=-1&emailauth=''&emailsteamid=''&password=%s&remember_login=true&rsatimestamp=%d&twofactorcode=%s&username=%s&oauth_client_id=DE45CD61&oauth_scope=read_profile write_profile read_client write_client&loginfriendlyname=#login_emailauth_friendlyname_mobile&donotcache=%d`,
		url.QueryEscape(base64.StdEncoding.EncodeToString(rsaOut)),
		itimestamp,
		twoFactor,
		accountName,
		time.Now().Unix()*1000)
	req, err := http.NewRequest(http.MethodPost, params, nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var session LoginSession
	if err := json.NewDecoder(resp.Body).Decode(&session); err != nil {
		return err
	}

	if !session.Success {
		if session.RequiresTwoFactor {
			return ErrNeedTwoFactor
		}

		return ErrUnableToLogin
	}

	randomBytes := make([]byte, 6)
	if count, err := rand.Read(randomBytes); err != nil {
		return err
	} else if count != 6 {
		return ErrInsufficientEntropy
	}

	sessionID := make([]byte, hex.EncodedLen(len(randomBytes)))
	hex.Encode(sessionID, randomBytes)
	community.sessionID = string(sessionID)

	url, _ := url.Parse("https://steamcommunity.com")
	cookies := community.client.Jar.Cookies(url)
	community.client.Jar.SetCookies(
		url,
		append(cookies, &http.Cookie{
			Name:  "sessionid",
			Value: community.sessionID,
		}),
	)

	if err := json.Unmarshal([]byte(session.OAuthInfo), &community.oauth); err != nil {
		return err
	}

	return nil
}

func (community *Community) login(accountName, password, twoFactor string) error {
	req, err := http.NewRequest(http.MethodPost, "https://steamcommunity.com/login/getrsakey?username="+accountName, nil)
	if err != nil {
		return err
	}

	jar, err := cookiejar.New(nil)
	if err != nil {
		return err
	}

	req.Header.Add("X-Requested-With", httpXRequestedWithValue)
	req.Header.Add("Referer", "https://steamcommunity.com/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
	req.Header.Add("User-Agent", httpUserAgentValue)
	req.Header.Add("Accept", httpAcceptValue)

	cookies := []*http.Cookie{
		&http.Cookie{Name: "mobileClientVersion", Value: "0 (2.1.3)"},
		&http.Cookie{Name: "mobileClient", Value: "android"},
		&http.Cookie{Name: "Steam_Language", Value: "english"},
		&http.Cookie{Name: "timezoneOffset", Value: "0,0"},
	}
	url, _ := url.Parse("https://steamcommunity.com")
	jar.SetCookies(url, cookies)

	// Construct the client
	community.client = &http.Client{Jar: jar}

	resp, err := community.client.Do(req)
	if resp != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return err
	}

	var response LoginResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		return err
	}

	if !response.Success {
		return ErrInvalidUsername
	}

	return community.proceedDirectLogin(&response, accountName, password, twoFactor)
}
<|endoftext|>