Example #1
0
func checkfile(w http.ResponseWriter, r *http.Request) {
	req := api.CheckFileRequest{}
	err := readRequest(r, &req)
	if err != nil {
		sendError(w, "Could not parse request")
		return
	}

	accessKey, _ := mailbox.FindKeyByName(req.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Access key invalid")
		return
	}

	path := filepath.Join(filesPath(), req.MD5)

	resp := api.SimpleResponse{}
	if _, err := os.Stat(path); os.IsNotExist(err) {
		resp.Success = false
	} else {
		resp.Success = true
	}
	resp.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, resp)
}
Example #2
0
// deleteMessage is used by clients to mark messages as processed. The message
// remains in the database, but is marked deleted and will no longer be
// presented to polling clients.
func deleteMessage(w http.ResponseWriter, r *http.Request) {
	var request api.DeleteMessageRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, "Could not parse json request")
		return
	}

	accessKey, _ := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Access key invalid")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Signature is invalid")
		return
	}

	err = mailbox.DeleteMessage(request.Message)
	if err != nil {
		sendError(w, "Could not delete message")
		return
	}
	resp := api.DeleteMessageResponse{Message: request.Message}
	resp.Sign(accessKey.Name, accessKey.Secret)
	log.Infof("Message %s deleted", request.Message)
	writeResponse(&w, resp)
}
Example #3
0
func getAsset(w http.ResponseWriter, r *http.Request) {
	var (
		request = api.GetAssetRequest{}
	)

	err := readRequest(r, &request)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Could not validate signature")
		return
	}

	fp := filepath.Join(filesPath(), request.MD5)
	if _, err := os.Stat(fp); os.IsNotExist(err) {
		sendError(w, "Asset not found on server")
		return
	}

	log.Infof("Serving asset to %s", accessKey.Name)
	http.ServeFile(w, r, fp)

}
Example #4
0
// deregister is used by administrative clients to remove mailboxes. It deletes
// the mailbox and all messages from the database.
func deregister(w http.ResponseWriter, r *http.Request) {
	var request api.RegisterRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, err.Error())
		return
	}
	accessKey, _ := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}
	if !request.Validate(accessKey.Secret) {
		sendError(w, "Could not validate signature")
		return
	}
	if !accessKey.CanAdmin() {
		sendError(w, "Not allowed to deregister mailboxes")
		return
	}
	err = mailbox.Deregister(request.Mailbox)
	if err != nil {
		sendError(w, err.Error())
		return
	}
	resp := api.SimpleResponse{Success: true}
	resp.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, resp)
}
Example #5
0
func TestAcessCmd(t *testing.T) {
	accessCmd.Run(accessCmd, []string{"NAME"})
	key, err := mailbox.FindKeyByName("NAME")
	if err != nil {
		t.Fatal(err)
	}
	if key == nil {
		t.Fatal("Key not created")
	}
}
Example #6
0
// register is used by administrative clients to reigster new mailboxes.
func register(w http.ResponseWriter, r *http.Request) {
	var request api.RegisterRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Could not validate signature")
		return
	}

	if !accessKey.CanAdmin() {
		sendError(w, "Not allowed to register mailboxes.")
		return
	}

	if mailbox.KeyExists(request.Mailbox) {
		sendError(w, "An access key already exists with that mailbox name")
		return
	}

	mb, err := mailbox.Create(request.Mailbox)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	mbKey := &mailbox.AccessKey{
		MailboxId: mb.Id,
	}

	err = mbKey.Create()
	if err != nil {
		sendError(w, err.Error())
		return
	}

	resp := api.RegisterResponse{
		Mailbox:         mb.Id,
		AccessKeyName:   mbKey.Name,
		AccessKeySecret: mbKey.Secret,
	}
	resp.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, resp)
	log.Infof("Mailbox %s registered.", mb.Id)
}
Example #7
0
// clientStats reports all mailboxes and their current connection status.
func clientStats(w http.ResponseWriter, r *http.Request) {
	var request api.SimpleRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, "Bad request")
	}

	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if err != nil || accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}

	if !accessKey.CanAdmin() {
		sendError(w, "Not allowed to get statistics")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Could not validate signature")
		return
	}

	clients := []api.ClientStatus{}
	mbxs, err := mailbox.All()
	if err != nil {
		sendError(w, err.Error())
		return
	}
	for _, mb := range mbxs {
		st := api.ClientStatus{
			Mailbox:  mb.Id,
			Version:  mb.Version,
			Host:     mb.Host,
			LastSeen: mb.LastSeen,
		}
		if _, ok := pollingChannels[mb.Id]; ok {
			st.Online = true
		} else {
			st.Online = false
		}
		clients = append(clients, st)
	}
	response := api.ClientStatusResponse{Clients: clients}
	response.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, response)
}
Example #8
0
// getMessage is used by clients to poll for mailbox messages. This method will
// search the database for messages for the given mailbox. If the mailbox is
// empty and long_polling is enabled it will create a channel and add it to
// pollingChannels. It will then wait for a message to be pushed to that
// channel. It will then continue to output that message. Messages are pushed by
// the putMessage method.
func getMessage(w http.ResponseWriter, r *http.Request) {
	if !EnableLongPolling {
		time.Sleep(ThrottleDelay)
	}
	var request api.GetMessageRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	mb, err := mailbox.Find(request.Mailbox)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	if mb == nil {
		log.Warnf("Could not find a mailbox named '%s'", request.Mailbox)
		sendError(w, fmt.Sprintf("Mailbox %s not found.", request.Mailbox))
		return
	}

	log.Debugf("Message request from %s", mb.Id)

	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		log.Warnf("Could not find an access key named '%s'", request.AccessKeyName)
		sendError(w, "Access key is invalid")
		return
	}

	if !request.Validate(accessKey.Secret) {
		log.Warnf(fmt.Sprintf("Signature for %s invalid", mb.Id))
		sendError(w, "Signature is invalid")
		return
	}

	if !accessKey.CanGet(mb) {
		sendError(w, "Not allowed to get messages from this mailbox.")
		return
	}

	if err := mb.Checkin(r.RemoteAddr, request.Version); err != nil {
		sendError(w, err.Error())
		return
	}

	msg, err := mb.GetMessage()
	if err != nil {
		sendError(w, err.Error())
		log.Errorf("Error retrieving messages: %s", err.Error())
		return
	}

	if EnableLongPolling == true && msg == nil {
		if _, ok := pollingChannels[mb.Id]; ok {
			delete(pollingChannels, mb.Id)
		}
		// Create a channel for the client. This channel has a message pushed to it
		// from the putMessage function. When a message gets delivered.
		pollingChannels[mb.Id] = make(chan *mailbox.Message)
		timeout := make(chan bool, 1)
		// This goroutine will create a timeout to close the long polling connection
		// and force the client to reconnect.
		go func() {
			// Randomize the polling timeout in order to stagger client reconnects.
			sleepTime := rand.Intn(500) + 200
			time.Sleep(time.Duration(sleepTime) * time.Second)
			timeout <- true
		}()
		// Wait for either a timeout or a message to be sent to a channel.
		select {
		case m := <-pollingChannels[mb.Id]:
			msg = m
		case <-timeout:
			response := api.GetMessageResponse{}
			response.Sign(accessKey.Name, accessKey.Secret)
			writeResponse(&w, response)
			delete(pollingChannels, mb.Id)
			return
		}
		delete(pollingChannels, mb.Id)
	}

	if msg == nil {
		writeResponse(&w, nil)
		return
	}

	dep, err := msg.GetDeployment()
	if err != nil {
		sendError(w, err.Error())
		return
	}

	response := api.GetMessageResponse{
		Message:      msg.Id,
		Body:         msg.Body,
		CreatedAt:    msg.CreatedAt,
		ReceiveCount: msg.ReceiveCount,
		Deployment:   msg.Deployment,
		Asset:        dep.Asset,
	}

	response.Sign(accessKey.Name, accessKey.Secret)
	log.Infof("Delivering message %s to %s", response.Message, mb.Id)

	writeResponse(&w, response)
}
Example #9
0
// deployRespond is used by scripts to "respond" with information from the
// remote system. These responses can then be reported to the admin client that
// deployed the original script.
func deployRespond(w http.ResponseWriter, r *http.Request) {
	var request api.ResponseRequest
	err := readRequest(r, &request)

	if err != nil {
		sendError(w, "Could not parse request")
		return
	}
	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Access key is not valid")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Could not validate signature")
		return
	}

	msg, err := mailbox.FindMessage(request.Message)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	if msg == nil {
		sendError(w, "Could not find message "+request.Message)
		return
	}

	mb, err := mailbox.Find(msg.Mailbox)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	if mb == nil {
		sendError(w, "Mailbox not found")
		return
	}

	dep, err := mailbox.FindDeployment(msg.Deployment)
	if err != nil {
		sendError(w, err.Error())
		return
	}
	if dep == nil {
		sendError(w, "Could not find deployment "+msg.Deployment)
		return
	}
	if !accessKey.CanGet(mb) {
		sendError(w, "Not allowed to respond to deploys")
		return
	}
	err = dep.AddResponse(msg.Mailbox, request.Response, request.Error)
	if err != nil {
		sendError(w, err.Error())
		return
	}
	response := api.SimpleResponse{Success: true}
	response.Sign(accessKey.Name, accessKey.Secret)
	log.Infof("Reponse added to %s from %s", dep.Id, msg.Mailbox)
	writeResponse(&w, response)
}
Example #10
0
// deployInfo is used to both report a list of recent deployments and get
// details and responses on a specific deployment.
func deployInfo(w http.ResponseWriter, r *http.Request) {
	var request api.DeploymentStatsRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, "Could not parse request")
		return
	}

	if request.NamePattern == "" {
		request.NamePattern = ".*"
	}

	if request.TokenPattern == "" {
		request.TokenPattern = ".*"
	}

	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if err != nil || accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}
	if !accessKey.CanAdmin() {
		sendError(w, "Not allowed to list deployments")
		return
	}
	if !request.Validate(accessKey.Secret) {
		sendError(w, "Signature invalid")
		return
	}
	response := api.DeploymentStatsResponse{}
	if request.Deployment == "" {
		log.Infof("Listing all deploys for %s", accessKey.Name)
		deployments, err := mailbox.ListDeployments(request.NamePattern,
			int(request.Count), request.TokenPattern)
		if err != nil {
			sendError(w, err.Error())
			return
		}
		for _, d := range deployments {
			dStats, err := d.Stats()
			if err != nil {
				sendError(w, err.Error())
				return
			}
			statsResp := api.DeploymentStats{
				Name:          d.Name,
				Id:            d.Id,
				PendingCount:  dStats.PendingCount,
				MessageCount:  dStats.MessageCount,
				ResponseCount: dStats.ResponseCount,
				CreatedAt:     d.DeployedAt,
				DeployedBy:    d.DeployedBy,
			}
			response.Deployments = append(response.Deployments, statsResp)
		}
	} else {
		dep, err := mailbox.FindDeployment(request.Deployment)
		if err != nil {
			sendError(w, err.Error())
			return
		}

		if dep == nil {
			sendError(w, "Deployment not found")
			return
		}

		dStats, err := dep.Stats()
		if err != nil {
			sendError(w, err.Error())
			return
		}
		deploymentResponses, err := dep.GetResponses()
		if err != nil {
			sendError(w, err.Error())
			return
		}
		statsResp := api.DeploymentStats{
			Name:          dep.Name,
			Id:            dep.Id,
			PendingCount:  dStats.PendingCount,
			MessageCount:  dStats.MessageCount,
			ResponseCount: dStats.ResponseCount,
			CreatedAt:     dep.DeployedAt,
			Responses:     []api.DeploymentResponse{},
		}
		for _, r := range deploymentResponses {
			apiR := api.DeploymentResponse{
				Mailbox:     r.Mailbox,
				Response:    r.Response,
				RespondedAt: r.RespondedAt,
				IsError:     r.IsError,
			}
			statsResp.Responses = append(statsResp.Responses, apiR)
		}
		response.Deployments = append(response.Deployments, statsResp)
	}
	response.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, response)
}
Example #11
0
// putMessage is used to deploy messages to mailboxes, etiher by a list of
// mailboxes or a pattern. Messages are organized into a deployment and
// persisted to the database. If any receiptients are currently polling the
// message will be forwarded to that session.
func putMessage(w http.ResponseWriter, r *http.Request) {
	var request api.PutMessageRequest
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, "Could not parse request")
	}
	mailboxes := []mailbox.Mailbox{}
	if request.Pattern != "" {
		results, err := mailbox.Search(request.Pattern)
		if err != nil {
			sendError(w, err.Error())
			return
		}
		for _, mb := range results {
			mailboxes = append(mailboxes, mb)
		}
	}
	for _, mbId := range request.Mailboxes {
		mb, err := mailbox.Find(mbId)
		if err != nil {
			sendError(w, err.Error())
		}
		if mb == nil {
			sendError(w, fmt.Sprintf("Mailbox not found (%s)", mbId))
			return
		}
		mailboxes = append(mailboxes, *mb)
	}

	if len(mailboxes) == 0 {
		sendError(w, "No mailboxes specified")
		return
	}

	if request.Asset != "" {
		assetPath := filepath.Join(filesPath(), request.Asset)
		if _, err := os.Stat(assetPath); os.IsNotExist(err) {
			sendError(w, "Asset does not exist on server")
			return
		}
	}

	mbList := []string{}
	// dep, err := mailbox.CreateDeployment(request.DeploymentName, request.Token,
	// 	request.Body)

	dep := mailbox.Deployment{
		Name:        request.DeploymentName,
		DeployedBy:  request.AccessKeyName,
		MessageBody: request.Body,
		Asset:       request.Asset,
	}

	err = dep.Create()

	if err != nil {
		sendError(w, err.Error())
		return
	}

	accessKey, err := mailbox.FindKeyByName(request.AccessKeyName)
	if err != nil || accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Signature not valid")
		return
	}

	for _, mb := range mailboxes {
		if !accessKey.CanPut(&mb) {
			sendError(w, "Not allowed to send messages to "+mb.Id)
			return
		}
		var msg *mailbox.Message
		msg, err = dep.Deploy(&mb)
		mbList = append(mbList, mb.Id)
		if err != nil {
			sendError(w, err.Error())
			return
		}
		if pollChannel, ok := pollingChannels[mb.Id]; ok {
			time.Sleep(50 * time.Millisecond)
			pollChannel <- msg
		}
	}

	resp := api.PutMessageResponse{
		MessageSize: r.ContentLength,
		Mailboxes:   mbList,
		Deployment:  dep.Id,
	}
	resp.Sign(accessKey.Name, accessKey.Secret)

	log.Infof("Message received for %d mailboxes from %s", len(mbList),
		dep.DeployedBy)
	writeResponse(&w, resp)
}
Example #12
0
func acceptFile(w http.ResponseWriter, r *http.Request) {
	var (
		request = api.UploadFileRequest{}
	)
	defer r.Body.Close()
	// parse form post data
	err := r.ParseMultipartForm(1000000)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	// Get reuqest data and unmarshal
	reqData := r.FormValue("data")
	err = json.Unmarshal([]byte(reqData), &request)
	if err != nil {
		sendError(w, "Could not parse request!")
		return
	}
	accessKey, _ := mailbox.FindKeyByName(request.AccessKeyName)
	if accessKey == nil {
		sendError(w, "Invalid access key")
		return
	}
	if !accessKey.CanAdmin() {
		sendError(w, "Not allowed to upload files")
		return
	}
	if !request.Validate(accessKey.Secret) {
		sendError(w, "Invalid signature")
		return
	}

	// Save the posted file
	file, _, err := r.FormFile("file")
	if file != nil {
		defer file.Close()
	}
	if err != nil {
		sendError(w, err.Error())
		return
	}

	path := filepath.Join(filesPath(), request.MD5)
	out, err := os.Create(path)
	if out != nil {
		defer out.Close()
	}
	if err != nil {
		sendError(w, err.Error())
		return
	}
	_, err = io.Copy(out, file)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	fileHash, err := hashFile(path)
	if err != nil {
		sendError(w, err.Error())
		return
	}

	if request.MD5 != fileHash {
		defer os.Remove(filepath.Join(filesPath(), request.MD5))
		sendError(w, fmt.Sprintf("MD5 missmatch %s != %s", request.MD5, fileHash))
		return
	}

	log.Infof("File uploaded %s", request.MD5)
	response := api.SimpleResponse{Success: true}
	response.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, response)
}
Example #13
0
// systemStats is json endpoint used to retrieve overall statistics. The token
// used to request the endpoint must have write priviledges.
func systemStats(w http.ResponseWriter, r *http.Request) {
	var (
		request  api.SimpleRequest
		memStats runtime.MemStats
	)
	err := readRequest(r, &request)
	if err != nil {
		sendError(w, "Could not get stats")
		return
	}

	accessKey, _ := mailbox.FindKeyByName(request.AccessKeyName)

	if accessKey == nil {
		sendError(w, "Access key is invalid")
		return
	}

	if !accessKey.CanAdmin() {
		sendError(w, "Not allowed to get statistics")
		return
	}

	if !request.Validate(accessKey.Secret) {
		sendError(w, "Could not validate signature")
		return
	}

	stats, err := mailbox.Stats()
	if err != nil {
		sendError(w, err.Error())
		return
	}

	dbVersion, err := mailbox.GetDBVersion()
	if err != nil {
		sendError(w, err.Error())
		return
	}

	var fileCount int64 = 0
	var filesSize int64 = 0
	files, _ := ioutil.ReadDir(filesPath())
	for _, f := range files {
		fileCount++
		filesSize += f.Size()
	}

	runtime.ReadMemStats(&memStats)

	response := api.SystemStatsResponse{
		TotalMailboxes:   stats.MailboxCount,
		PendingMessages:  stats.PendingMessages,
		MessageCount:     stats.MessageCount,
		ConnectedClients: int64(len(pollingChannels)),
		DBVersion:        dbVersion,
		CPUCount:         int64(runtime.NumCPU()),
		Threads:          int64(runtime.NumGoroutine()),
		MemoryAllocated:  memStats.Alloc,
		Lookups:          memStats.Lookups,
		NextGC:           memStats.NextGC,
		FileStoreSize:    filesSize,
		FileStoreCount:   fileCount,
	}
	response.Sign(accessKey.Name, accessKey.Secret)
	writeResponse(&w, response)
}