<commit_before>//
// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main

import (
    "strings"

    "sippy"
    "sippy/conf"
    "sippy/types"
)

var global_static_route *B2BRoute
var global_rtp_proxy_clients []sippy_types.RtpProxyClient
var global_cmap *callMap
/*
from sippy.Timeout import Timeout
from sippy.Signal import Signal
from sippy.SipFrom import SipFrom
from sippy.SipTo import SipTo
from sippy.SipCiscoGUID import SipCiscoGUID
from sippy.UA import UA
from sippy.CCEvents import CCEventRing, CCEventConnect, CCEventDisconnect, CCEventTry, CCEventUpdate, CCEventFail
from sippy.UasStateTrying import UasStateTrying
from sippy.UasStateRinging import UasStateRinging
from sippy.UaStateDead import UaStateDead
from sippy.SipConf import SipConf
from sippy.SipHeader import SipHeader
from sippy.RadiusAuthorisation import RadiusAuthorisation
from sippy.RadiusAccounting import RadiusAccounting
from sippy.FakeAccounting import FakeAccounting
from sippy.SipLogger import SipLogger
from sippy.Rtp_proxy_session import Rtp_proxy_session
from sippy.Rtp_proxy_client import Rtp_proxy_client
from signal import SIGHUP, SIGPROF, SIGUSR1, SIGUSR2
from twisted.internet import reactor
from sippy.Cli_server_local import Cli_server_local
from sippy.SipTransactionManager import SipTransactionManager
from sippy.SipCallId import SipCallId
from sippy.StatefulProxy import StatefulProxy
from sippy.misc import daemonize
from sippy.B2BRoute import B2BRoute
import gc, getopt, os, sys
from re import sub
from time import time
from urllib import quote
from hashlib import md5
from sippy.MyConfigParser import MyConfigParser
from traceback import print_exc
from datetime import datetime

def re_replace(ptrn, s):
    s = s.split('#', 1)[0]
    ptrn = ptrn.split('/')
    while len(ptrn) > 0:
        op, p, r, mod = ptrn[:4]
        mod = mod.strip()
        if len(mod) > 0 && mod[0] != ';':
            ptrn[3] = mod[1:]
            mod = mod[0].lower()
        else:
            ptrn[3] = mod
        if 'g' in mod:
            s = sub(p, r, s)
        else:
            s = sub(p, r, s, 1)
        if len(ptrn) == 4 && ptrn[3] == '':
            break
        ptrn = ptrn[3:]
    return s

def reopen(signum, logfile):
    print('Signal %d received, reopening logs' % signum)
    fd = os.open(logfile, os.O_WRONLY | os.O_CREAT | os.O_APPEND)
    os.dup2(fd, sys.__stdout__.fileno())
    os.dup2(fd, sys.__stderr__.fileno())
    os.close(fd)

def usage(global_config, brief = false):
    print('usage: b2bua.py [--option1=value1] [--option2=value2] ... [--optionN==valueN]')
    if ! brief:
        print('\navailable options:\n')
        global_config.options_help()
    sys.exit(1)
*/

func main() {
    global_config := NewMyConfigParser()
    err := global_config.Parse()
    if err != nil {
        println(err.Error())
        return
    }

    if global_config.static_route != "" {
        global_static_route, err = NewB2BRoute(global_config.static_route, global_config)
        if err != nil {
            println("Error parsing the static route")
            println(err.Error())
            return
        }
    //} else if ! global_config.auth_enable {
    } else if true { // radius is not implemented
        println("ERROR: static route should be specified when Radius auth is disabled")
        return
    }
/*
    if writeconf != nil:
        global_config.write(open(writeconf, 'w'))

    if ! global_config['foreground']:
        daemonize(logfile = global_config['logfile'])
*/
    global_rtp_proxy_clients = make([]sippy_types.RtpProxyClient, len(global_config.rtp_proxy_clients))
    for i, address := range global_config.rtp_proxy_clients {
        opts := sippy.NewRtpProxyClientOpts()
        opts.SetSocketPath(address)
        opts.SetHeartbeatInterval(global_config.hrtb_ival)
        opts.SetHeartbeatRetryInterval(global_config.hrtb_retr_ival)
        rtpp, err := sippy.NewRtpProxyClient(opts, global_config, global_config.ErrorLogger())
        if err != nil {
            println("Cannot initialize rtpproxy client: " + err.Error())
            return
        }
        global_rtp_proxy_clients[i] = rtpp
    }
/*
    if global_config['auth_enable'] || global_config['acct_enable']:
        global_config['_radius_client'] = RadiusAuthorisation(global_config)
*/
    global_config.SetMyUAName("Sippy B2BUA (RADIUS)")

    global_cmap = NewCallMap(global_config)
/*
    if global_config.getdefault('xmpp_b2bua_id', nil) != nil:
        global_config['_xmpp_mode'] = true
*/
    sip_tm, err := sippy.NewSipTransactionManager(global_config, global_cmap)
    if err != nil {
        println("Cannot initialize SipTransactionManager: " + err.Error())
        return
    }
    //sip_tm.nat_traversal = global_config.nat_traversal
    global_cmap.sip_tm = sip_tm
    if global_config.sip_proxy != "" {
        var sip_proxy *sippy_conf.HostPort
        host_port := strings.SplitN(global_config.sip_proxy, ":", 2)
        if len(host_port) == 1 {
            sip_proxy = sippy_conf.NewHostPort(host_port[0], "5060")
        } else {
            sip_proxy = sippy_conf.NewHostPort(host_port[0], host_port[1])
        }
        global_cmap.proxy = sippy.NewStatefulProxy(sip_tm, sip_proxy, global_config)
    }

    cmdfile := global_config.b2bua_socket
    if strings.HasPrefix(cmdfile, "unix:") {
        cmdfile = cmdfile[5:]
    }
    cli_server, err := NewCli_server_local(global_cmap.recvCommand, cmdfile, global_config.ErrorLogger())
    if err != nil {
        println("Cannot initialize Cli_server: " + err.Error())
        return
    }
    cli_server.Start()
/*
    if ! global_config['foreground']:
        file(global_config['pidfile'], 'w').write(str(os.getpid()) + '\n')
        Signal(SIGUSR1, reopen, SIGUSR1, global_config['logfile'])
*/
    sip_tm.Run()
}
<commit_msg>Fix file permissions.<commit_after>//
// Copyright (c) 2003-2005 Maxim Sobolev. All rights reserved.
// Copyright (c) 2006-2014 Sippy Software, Inc. All rights reserved.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main

import (
    "strings"

    "sippy"
    "sippy/conf"
    "sippy/types"
)

var global_static_route *B2BRoute
var global_rtp_proxy_clients []sippy_types.RtpProxyClient
var global_cmap *callMap
/*
from sippy.Timeout import Timeout
from sippy.Signal import Signal
from sippy.SipFrom import SipFrom
from sippy.SipTo import SipTo
from sippy.SipCiscoGUID import SipCiscoGUID
from sippy.UA import UA
from sippy.CCEvents import CCEventRing, CCEventConnect, CCEventDisconnect, CCEventTry, CCEventUpdate, CCEventFail
from sippy.UasStateTrying import UasStateTrying
from sippy.UasStateRinging import UasStateRinging
from sippy.UaStateDead import UaStateDead
from sippy.SipConf import SipConf
from sippy.SipHeader import SipHeader
from sippy.RadiusAuthorisation import RadiusAuthorisation
from sippy.RadiusAccounting import RadiusAccounting
from sippy.FakeAccounting import FakeAccounting
from sippy.SipLogger import SipLogger
from sippy.Rtp_proxy_session import Rtp_proxy_session
from sippy.Rtp_proxy_client import Rtp_proxy_client
from signal import SIGHUP, SIGPROF, SIGUSR1, SIGUSR2
from twisted.internet import reactor
from sippy.Cli_server_local import Cli_server_local
from sippy.SipTransactionManager import SipTransactionManager
from sippy.SipCallId import SipCallId
from sippy.StatefulProxy import StatefulProxy
from sippy.misc import daemonize
from sippy.B2BRoute import B2BRoute
import gc, getopt, os, sys
from re import sub
from time import time
from urllib import quote
from hashlib import md5
from sippy.MyConfigParser import MyConfigParser
from traceback import print_exc
from datetime import datetime

def re_replace(ptrn, s):
    s = s.split('#', 1)[0]
    ptrn = ptrn.split('/')
    while len(ptrn) > 0:
        op, p, r, mod = ptrn[:4]
        mod = mod.strip()
        if len(mod) > 0 && mod[0] != ';':
            ptrn[3] = mod[1:]
            mod = mod[0].lower()
        else:
            ptrn[3] = mod
        if 'g' in mod:
            s = sub(p, r, s)
        else:
            s = sub(p, r, s, 1)
        if len(ptrn) == 4 && ptrn[3] == '':
            break
        ptrn = ptrn[3:]
    return s

def reopen(signum, logfile):
    print('Signal %d received, reopening logs' % signum)
    fd = os.open(logfile, os.O_WRONLY | os.O_CREAT | os.O_APPEND)
    os.dup2(fd, sys.__stdout__.fileno())
    os.dup2(fd, sys.__stderr__.fileno())
    os.close(fd)

def usage(global_config, brief = false):
    print('usage: b2bua.py [--option1=value1] [--option2=value2] ... [--optionN==valueN]')
    if ! brief:
        print('\navailable options:\n')
        global_config.options_help()
    sys.exit(1)
*/

func main() {
    global_config := NewMyConfigParser()
    err := global_config.Parse()
    if err != nil {
        println(err.Error())
        return
    }

    if global_config.static_route != "" {
        global_static_route, err = NewB2BRoute(global_config.static_route, global_config)
        if err != nil {
            println("Error parsing the static route")
            println(err.Error())
            return
        }
    //} else if ! global_config.auth_enable {
    } else if true { // radius is not implemented
        println("ERROR: static route should be specified when Radius auth is disabled")
        return
    }
/*
    if writeconf != nil:
        global_config.write(open(writeconf, 'w'))

    if ! global_config['foreground']:
        daemonize(logfile = global_config['logfile'])
*/
    global_rtp_proxy_clients = make([]sippy_types.RtpProxyClient, len(global_config.rtp_proxy_clients))
    for i, address := range global_config.rtp_proxy_clients {
        opts := sippy.NewRtpProxyClientOpts()
        opts.SetSocketPath(address)
        opts.SetHeartbeatInterval(global_config.hrtb_ival)
        opts.SetHeartbeatRetryInterval(global_config.hrtb_retr_ival)
        rtpp, err := sippy.NewRtpProxyClient(opts, global_config, global_config.ErrorLogger())
        if err != nil {
            println("Cannot initialize rtpproxy client: " + err.Error())
            return
        }
        global_rtp_proxy_clients[i] = rtpp
    }
/*
    if global_config['auth_enable'] || global_config['acct_enable']:
        global_config['_radius_client'] = RadiusAuthorisation(global_config)
*/
    global_config.SetMyUAName("Sippy B2BUA (RADIUS)")

    global_cmap = NewCallMap(global_config)
/*
    if global_config.getdefault('xmpp_b2bua_id', nil) != nil:
        global_config['_xmpp_mode'] = true
*/
    sip_tm, err := sippy.NewSipTransactionManager(global_config, global_cmap)
    if err != nil {
        println("Cannot initialize SipTransactionManager: " + err.Error())
        return
    }
    //sip_tm.nat_traversal = global_config.nat_traversal
    global_cmap.sip_tm = sip_tm
    if global_config.sip_proxy != "" {
        var sip_proxy *sippy_conf.HostPort
        host_port := strings.SplitN(global_config.sip_proxy, ":", 2)
        if len(host_port) == 1 {
            sip_proxy = sippy_conf.NewHostPort(host_port[0], "5060")
        } else {
            sip_proxy = sippy_conf.NewHostPort(host_port[0], host_port[1])
        }
        global_cmap.proxy = sippy.NewStatefulProxy(sip_tm, sip_proxy, global_config)
    }

    cmdfile := global_config.b2bua_socket
    if strings.HasPrefix(cmdfile, "unix:") {
        cmdfile = cmdfile[5:]
    }
    cli_server, err := NewCli_server_local(global_cmap.recvCommand, cmdfile, global_config.ErrorLogger())
    if err != nil {
        println("Cannot initialize Cli_server: " + err.Error())
        return
    }
    cli_server.Start()
/*
    if ! global_config['foreground']:
        file(global_config['pidfile'], 'w').write(str(os.getpid()) + '\n')
        Signal(SIGUSR1, reopen, SIGUSR1, global_config['logfile'])
*/
    sip_tm.Run()
}
<|endoftext|>


<commit_before>package circuitbreaker

import (
	"sync"
)

type Window interface {
	FailureRate() float64
	PutSuccess()
	PutFailure()
	Reset()
}

type countBasedWindow struct {
	idx            int
	failures       int
	size           int
	failureHistory []bool
	mu             sync.RWMutex
}

func CountWindow(size int) Window {
	return &countBasedWindow{
		size:           size,
		failureHistory: make([]bool, size),
	}
}

func (w *countBasedWindow) FailureRate() float64 {
	w.mu.RLock()
	defer w.mu.RUnlock()
	return float64(w.failures) / float64(w.size)
}

func (w *countBasedWindow) PutSuccess() {
	w.put(false)
}

func (w *countBasedWindow) PutFailure() {
	w.put(true)
}

func (w *countBasedWindow) Reset() {
	w.mu.Lock()
	defer w.mu.Unlock()
	for i := range w.failureHistory {
		w.failureHistory[i] = false
	}
	w.failures = 0
	w.idx = 0
}

func (w *countBasedWindow) put(failure bool) {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.failureHistory[w.idx] {
		w.failures--
	}
	if failure {
		w.failures++
	}

	w.failureHistory[w.idx] = failure
	w.idx = (w.idx + 1) % w.size
}
<commit_msg>Rename CountWindow function<commit_after>package circuitbreaker

import (
	"sync"
)

type Window interface {
	FailureRate() float64
	PutSuccess()
	PutFailure()
	Reset()
}

type countBaseWindow struct {
	idx            int
	failures       int
	size           int
	failureHistory []bool
	mu             sync.RWMutex
}

func NewCountBaseWindow(size int) Window {
	return &countBaseWindow{
		size:           size,
		failureHistory: make([]bool, size),
	}
}

func (w *countBaseWindow) FailureRate() float64 {
	w.mu.RLock()
	defer w.mu.RUnlock()
	return float64(w.failures) / float64(w.size)
}

func (w *countBaseWindow) PutSuccess() {
	w.put(false)
}

func (w *countBaseWindow) PutFailure() {
	w.put(true)
}

func (w *countBaseWindow) Reset() {
	w.mu.Lock()
	defer w.mu.Unlock()
	for i := range w.failureHistory {
		w.failureHistory[i] = false
	}
	w.failures = 0
	w.idx = 0
}

func (w *countBaseWindow) put(failure bool) {
	w.mu.Lock()
	defer w.mu.Unlock()

	if w.failureHistory[w.idx] {
		w.failures--
	}
	if failure {
		w.failures++
	}

	w.failureHistory[w.idx] = failure
	w.idx = (w.idx + 1) % w.size
}
<|endoftext|>


<commit_before>package fs

import (
	"io"
	"log"

	"bazil.org/bazil/cas/blobs"
	wirecas "bazil.org/bazil/cas/wire"
	"bazil.org/bazil/fs/wire"
	"bazil.org/bazil/util/env"
	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/boltdb/bolt"
)

type file struct {
	fs.NodeRef

	inode  uint64
	name   string
	parent *dir
	blob   *blobs.Blob

	// when was this entry last changed
	// TODO: written time.Time
}

var _ = node(&file{})
var _ = fs.Node(&file{})
var _ = fs.NodeForgetter(&file{})
var _ = fs.NodeOpener(&file{})
var _ = fs.NodeSetattrer(&file{})
var _ = fs.HandleFlusher(&file{})
var _ = fs.HandleReader(&file{})
var _ = fs.HandleWriter(&file{})

func (f *file) getName() string {
	return f.name
}

func (f *file) setName(name string) {
	f.name = name
}

func (f *file) marshal() (*wire.Dirent, error) {
	de := &wire.Dirent{
		Inode: f.inode,
	}
	manifest, err := f.blob.Save()
	if err != nil {
		return nil, err
	}
	de.Type.File = &wire.File{
		Manifest: wirecas.FromBlob(manifest),
	}
	return de, nil
}

func (f *file) Attr() fuse.Attr {
	return fuse.Attr{
		Inode: f.inode,
		Mode:  0644,
		Nlink: 1,
		Uid:   env.MyUID,
		Gid:   env.MyGID,
		Size:  f.blob.Size(),
	}
}

func (f *file) Forget() {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	f.parent.forgetChild(f)
}

func (f *file) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fs.Intr) (fs.Handle, fuse.Error) {
	// allow kernel to use buffer cache
	resp.Flags &^= fuse.OpenDirectIO
	return f, nil
}

func (f *file) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	n, err := f.blob.WriteAt(req.Data, req.Offset)
	resp.Size = n
	if err != nil {
		log.Printf("write error: %v", err)
		return fuse.EIO
	}
	return nil
}

func (f *file) Flush(req *fuse.FlushRequest, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	// TODO only if dirty
	err := f.parent.fs.db.Update(func(tx *bolt.Tx) error {
		return f.parent.save(tx, f)
	})
	return err
}

const maxInt64 = 9223372036854775807

func (f *file) Read(req *fuse.ReadRequest, resp *fuse.ReadResponse, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	if req.Offset < 0 {
		panic("unreachable")
	}
	if req.Offset > maxInt64 {
		log.Printf("offset is past int64 max: %d", req.Offset)
		return fuse.EIO
	}
	resp.Data = resp.Data[:req.Size]
	n, err := f.blob.ReadAt(resp.Data, int64(req.Offset))
	if err != nil && err != io.EOF {
		log.Printf("read error: %v", err)
		return fuse.EIO
	}
	resp.Data = resp.Data[:n]

	return nil
}

func (f *file) Setattr(req *fuse.SetattrRequest, resp *fuse.SetattrResponse, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	valid := req.Valid
	if valid.Size() {
		err := f.blob.Truncate(req.Size)
		if err != nil {
			return err
		}
		valid &^= fuse.SetattrSize
	}

	// things we don't need to explicitly handle
	valid &^= fuse.SetattrLockOwner | fuse.SetattrHandle

	if valid != 0 {
		// don't let an unhandled operation slip by without error
		log.Printf("Setattr did not handle %v", valid)
		return fuse.ENOSYS
	}
	return nil
}
<commit_msg>fs: Write in-memory data to disk on Fsync<commit_after>package fs

import (
	"io"
	"log"

	"bazil.org/bazil/cas/blobs"
	wirecas "bazil.org/bazil/cas/wire"
	"bazil.org/bazil/fs/wire"
	"bazil.org/bazil/util/env"
	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/boltdb/bolt"
)

type file struct {
	fs.NodeRef

	inode  uint64
	name   string
	parent *dir
	blob   *blobs.Blob

	// when was this entry last changed
	// TODO: written time.Time
}

var _ = node(&file{})
var _ = fs.Node(&file{})
var _ = fs.NodeForgetter(&file{})
var _ = fs.NodeOpener(&file{})
var _ = fs.NodeSetattrer(&file{})
var _ = fs.NodeFsyncer(&file{})
var _ = fs.HandleFlusher(&file{})
var _ = fs.HandleReader(&file{})
var _ = fs.HandleWriter(&file{})

func (f *file) getName() string {
	return f.name
}

func (f *file) setName(name string) {
	f.name = name
}

func (f *file) marshal() (*wire.Dirent, error) {
	de := &wire.Dirent{
		Inode: f.inode,
	}
	manifest, err := f.blob.Save()
	if err != nil {
		return nil, err
	}
	de.Type.File = &wire.File{
		Manifest: wirecas.FromBlob(manifest),
	}
	return de, nil
}

func (f *file) Attr() fuse.Attr {
	return fuse.Attr{
		Inode: f.inode,
		Mode:  0644,
		Nlink: 1,
		Uid:   env.MyUID,
		Gid:   env.MyGID,
		Size:  f.blob.Size(),
	}
}

func (f *file) Forget() {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	f.parent.forgetChild(f)
}

func (f *file) Open(req *fuse.OpenRequest, resp *fuse.OpenResponse, intr fs.Intr) (fs.Handle, fuse.Error) {
	// allow kernel to use buffer cache
	resp.Flags &^= fuse.OpenDirectIO
	return f, nil
}

func (f *file) Write(req *fuse.WriteRequest, resp *fuse.WriteResponse, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	n, err := f.blob.WriteAt(req.Data, req.Offset)
	resp.Size = n
	if err != nil {
		log.Printf("write error: %v", err)
		return fuse.EIO
	}
	return nil
}

func (f *file) flush(intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	// TODO only if dirty
	err := f.parent.fs.db.Update(func(tx *bolt.Tx) error {
		return f.parent.save(tx, f)
	})
	return err
}

func (f *file) Flush(req *fuse.FlushRequest, intr fs.Intr) fuse.Error {
	return f.flush(intr)
}

const maxInt64 = 9223372036854775807

func (f *file) Read(req *fuse.ReadRequest, resp *fuse.ReadResponse, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	if req.Offset < 0 {
		panic("unreachable")
	}
	if req.Offset > maxInt64 {
		log.Printf("offset is past int64 max: %d", req.Offset)
		return fuse.EIO
	}
	resp.Data = resp.Data[:req.Size]
	n, err := f.blob.ReadAt(resp.Data, int64(req.Offset))
	if err != nil && err != io.EOF {
		log.Printf("read error: %v", err)
		return fuse.EIO
	}
	resp.Data = resp.Data[:n]

	return nil
}

func (f *file) Setattr(req *fuse.SetattrRequest, resp *fuse.SetattrResponse, intr fs.Intr) fuse.Error {
	f.parent.fs.mu.Lock()
	defer f.parent.fs.mu.Unlock()

	valid := req.Valid
	if valid.Size() {
		err := f.blob.Truncate(req.Size)
		if err != nil {
			return err
		}
		valid &^= fuse.SetattrSize
	}

	// things we don't need to explicitly handle
	valid &^= fuse.SetattrLockOwner | fuse.SetattrHandle

	if valid != 0 {
		// don't let an unhandled operation slip by without error
		log.Printf("Setattr did not handle %v", valid)
		return fuse.ENOSYS
	}
	return nil
}

func (f *file) Fsync(req *fuse.FsyncRequest, intr fs.Intr) fuse.Error {
	// flush forces writes to backing stores; we don't current
	// differentiate between the backing stores writing vs syncing.
	return f.flush(intr)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

type service struct {
	gin                  *gin.Engine
	wsUpgrder            *websocket.Upgrader
	currentSubscriptions subscriptions
	timeout              time.Duration
	sync                 chan bool
	cache                *Cache
}

func newService(
	currentSubscriptions subscriptions,
	readBufferSize int,
	writeBufferSize int,
	timeout time.Duration,
	debugMode string,
	cache *Cache,
) *service {

	if debugMode == "DEBUG" {
		gin.SetMode(gin.ReleaseMode)
	}

	upgrader := &websocket.Upgrader{
		ReadBufferSize:  readBufferSize,
		WriteBufferSize: writeBufferSize,
		CheckOrigin:     func(request *http.Request) bool { return true },
	}

	return &service{
		gin.Default(),
		upgrader,
		currentSubscriptions,
		timeout,
		make(chan bool, 1),
		cache,
	}

}

func (s *service) run(
	listen string,
) {

	v1 := s.gin.Group("v1")
	{
		v1.GET("/subscribe", s.handleSubscription)
		v1.PUT("/publish", s.handlePublish)
		v1.GET("/stats", s.handleStats)
		v1.GET("/stats/sessions", s.handleStatsSessions)
		v1.GET("/health", s.handleHealth)
	}

	//	ginpprof.Wrap(s.gin)
	logger.Debugf("run service on %s", listen)
	s.gin.Run(listen)
}

func (s *service) handleSubscription(context *gin.Context) {

	logger.Debugf(
		"handling new request from %s",
		context.Request.RemoteAddr,
	)

	authInfo := make(chan string)
	isDisconnected := make(chan bool, 1)
	terminateAuthentication := make(chan bool, 1)
	terminateKeepAlive := make(chan bool, 1)
	incomingMessages := make(chan string)

	connection, err := s.wsUpgrder.Upgrade(
		context.Writer, context.Request, nil,
	)

	if err != nil {
		logger.Errorf(
			"something happing wrong during create websocket connection, "+
				"reason %s",
			err.Error(),
		)

		return
	}

	defer func() {
		err := connection.Close()

		if err != nil {
			logger.Errorf(
				"unable to close websocket connection from %s, reason: %s",
				connection.RemoteAddr().String(),
				err.Error(),
			)

			return
		}

		logger.Infof(
			"websocket connection successfully closed for client %s",
			connection.RemoteAddr().String(),
		)
		return
	}()

	connection.SetReadDeadline(time.Now().Add(s.timeout))
	connection.SetWriteDeadline(time.Now().Add(s.timeout))

	go readMessage(connection, isDisconnected, incomingMessages)
	go authenticateUser(
		incomingMessages,
		authInfo,
		terminateAuthentication,
	)

	var (
		authToken     string = ""
		sessionNumber int    = 0
		messages      chan []byte
	)

	for {
		select {
		case authToken = <-authInfo:
			logger.Debugf(
				"auth token received: %s",
				authToken,
			)

			go keepAlive(connection, s.timeout, terminateKeepAlive, s.sync)

			s.sync <- true
			sessionNumber, messages = s.currentSubscriptions.subscribeUser(
				authToken,
			)
			<-s.sync

			logger.Infof(
				"user %s was subscribed with ID %d",
				connection.RemoteAddr().String(),
				sessionNumber,
			)

			if len(s.cache.messages[authToken]) != 0 {
				logger.Debugf(
					"pop messages from cache for %s",
					authToken,
				)

				go func() {
					for _, cacheObject := range s.cache.pop(authToken) {
						messages <- []byte(cacheObject.message)
					}
				}()
			}

		case <-isDisconnected:
			s.sync <- true

			logger.Infof(
				"client %s with token %s with session %d will be disconnected",
				context.Request.RemoteAddr,
				authToken,
				sessionNumber,
			)

			terminateAuthentication <- true
			terminateKeepAlive <- true
			logger.Infof(
				"terminate message was successfully sended into keepalive" +
					" and authentication processes",
			)

			activeSubscribers := s.currentSubscriptions.getActiveSubscribers(
				authToken,
			)

			switch activeSubscribers {
			case 0:
				logger.Debug("there are no subscribers, exiting...")

			case 1:
				logger.Debugf(
					"there is only one subscription for user %s, "+
						"unsubscribe user",
					authToken,
				)

				s.currentSubscriptions.unsubscribeUser(authToken)

			default:
				logger.Debugf(
					"there are several sessions for user %s, "+
						"trying deactivate session %d",
					authToken,
					sessionNumber,
				)

				s.currentSubscriptions.setInactive(authToken, sessionNumber)
			}

			<-s.sync
			return

		case message := <-messages:
			s.sync <- true
			logger.Debugf("got message %s", message)

			err := connection.WriteMessage(websocket.TextMessage, message)
			if err != nil {
				logger.Errorf(
					"%s: can`t write message to websocket, reason %s",
					reflect.TypeOf(err).String(),
					err.Error(),
				)
				<-s.sync

				return
			}
			<-s.sync
		}
	}
}

func (s *service) handlePublish(context *gin.Context) {
	authToken := strings.TrimSpace(
		context.Request.Header.Get("Token"),
	)
	message, err := ioutil.ReadAll(context.Request.Body)

	if err != nil {
		logger.Errorf(
			"could not read response body, reason: %s",
			err.Error(),
		)
		context.JSON(makeInternalServerError(err.Error(), nil))
		return
	}

	if _, exists := s.currentSubscriptions[authToken]; exists {

		s.currentSubscriptions.publishMessage(authToken, message)

		context.JSON(makeStatusOk("message published", string(message)))
		return
	}

	s.cache.put(authToken, string(message))

	context.JSON(makeNotFoundError(
		fmt.Sprintf("recipient %s not found", authToken),
		string(message),
	))
	return
}

func (s *service) handleStats(context *gin.Context) {
	stat := ServiceStats{
		SubscribersCount: len(s.currentSubscriptions),
	}

	context.JSON(makeStatusOk(
		context.Request.URL.String(), stat,
	))

	return
}

func (s *service) handleStatsSessions(context *gin.Context) {

	sessionsCount := make(map[string]int)
	for authToken, sessions := range s.currentSubscriptions {
		sessionsCount[authToken] = len(sessions)

	}
	sortedCurrentSessions := newSortedSessions(sessionsCount)
	sortedCurrentSessions.Sort()

	stat := ActiveSessions{
		SubscribersSessionsCount: sessionsCount,
	}

	context.JSON(makeStatusOk(
		context.Request.URL.String(), stat,
	))

	return
}
func (s *service) handleHealth(context *gin.Context) {

	context.JSON(makeStatusOk(
		context.Request.URL.String(), "OK",
	))

	return
}
<commit_msg>fix concurrent map write when publish event<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

type service struct {
	gin                  *gin.Engine
	wsUpgrder            *websocket.Upgrader
	currentSubscriptions subscriptions
	timeout              time.Duration
	sync                 chan bool
	cache                *Cache
}

func newService(
	currentSubscriptions subscriptions,
	readBufferSize int,
	writeBufferSize int,
	timeout time.Duration,
	debugMode string,
	cache *Cache,
) *service {

	if debugMode == "DEBUG" {
		gin.SetMode(gin.ReleaseMode)
	}

	upgrader := &websocket.Upgrader{
		ReadBufferSize:  readBufferSize,
		WriteBufferSize: writeBufferSize,
		CheckOrigin:     func(request *http.Request) bool { return true },
	}

	return &service{
		gin.Default(),
		upgrader,
		currentSubscriptions,
		timeout,
		make(chan bool, 1),
		cache,
	}

}

func (s *service) run(
	listen string,
) {

	v1 := s.gin.Group("v1")
	{
		v1.GET("/subscribe", s.handleSubscription)
		v1.PUT("/publish", s.handlePublish)
		v1.GET("/stats", s.handleStats)
		v1.GET("/stats/sessions", s.handleStatsSessions)
		v1.GET("/health", s.handleHealth)
	}

	//	ginpprof.Wrap(s.gin)
	logger.Debugf("run service on %s", listen)
	s.gin.Run(listen)
}

func (s *service) handleSubscription(context *gin.Context) {

	logger.Debugf(
		"handling new request from %s",
		context.Request.RemoteAddr,
	)

	authInfo := make(chan string)
	isDisconnected := make(chan bool, 1)
	terminateAuthentication := make(chan bool, 1)
	terminateKeepAlive := make(chan bool, 1)
	incomingMessages := make(chan string)

	connection, err := s.wsUpgrder.Upgrade(
		context.Writer, context.Request, nil,
	)

	if err != nil {
		logger.Errorf(
			"something happing wrong during create websocket connection, "+
				"reason %s",
			err.Error(),
		)

		return
	}

	defer func() {
		err := connection.Close()

		if err != nil {
			logger.Errorf(
				"unable to close websocket connection from %s, reason: %s",
				connection.RemoteAddr().String(),
				err.Error(),
			)

			return
		}

		logger.Infof(
			"websocket connection successfully closed for client %s",
			connection.RemoteAddr().String(),
		)
		return
	}()

	connection.SetReadDeadline(time.Now().Add(s.timeout))
	connection.SetWriteDeadline(time.Now().Add(s.timeout))

	go readMessage(connection, isDisconnected, incomingMessages)
	go authenticateUser(
		incomingMessages,
		authInfo,
		terminateAuthentication,
	)

	var (
		authToken     string = ""
		sessionNumber int    = 0
		messages      chan []byte
	)

	for {
		select {
		case authToken = <-authInfo:
			logger.Debugf(
				"auth token received: %s",
				authToken,
			)

			go keepAlive(connection, s.timeout, terminateKeepAlive, s.sync)

			s.sync <- true
			sessionNumber, messages = s.currentSubscriptions.subscribeUser(
				authToken,
			)
			<-s.sync

			logger.Infof(
				"user %s was subscribed with ID %d",
				connection.RemoteAddr().String(),
				sessionNumber,
			)

			if len(s.cache.messages[authToken]) != 0 {
				logger.Debugf(
					"pop messages from cache for %s",
					authToken,
				)

				go func() {
					for _, cacheObject := range s.cache.pop(authToken) {
						messages <- []byte(cacheObject.message)
					}
				}()
			}

		case <-isDisconnected:
			s.sync <- true

			logger.Infof(
				"client %s with token %s with session %d will be disconnected",
				context.Request.RemoteAddr,
				authToken,
				sessionNumber,
			)

			terminateAuthentication <- true
			terminateKeepAlive <- true
			logger.Infof(
				"terminate message was successfully sended into keepalive" +
					" and authentication processes",
			)

			activeSubscribers := s.currentSubscriptions.getActiveSubscribers(
				authToken,
			)

			switch activeSubscribers {
			case 0:
				logger.Debug("there are no subscribers, exiting...")

			case 1:
				logger.Debugf(
					"there is only one subscription for user %s, "+
						"unsubscribe user",
					authToken,
				)

				s.currentSubscriptions.unsubscribeUser(authToken)

			default:
				logger.Debugf(
					"there are several sessions for user %s, "+
						"trying deactivate session %d",
					authToken,
					sessionNumber,
				)

				s.currentSubscriptions.setInactive(authToken, sessionNumber)
			}

			<-s.sync
			return

		case message := <-messages:
			s.sync <- true
			logger.Debugf("got message %s", message)

			err := connection.WriteMessage(websocket.TextMessage, message)
			if err != nil {
				logger.Errorf(
					"%s: can`t write message to websocket, reason %s",
					reflect.TypeOf(err).String(),
					err.Error(),
				)
				<-s.sync

				return
			}
			<-s.sync
		}
	}
}

func (s *service) handlePublish(context *gin.Context) {
	authToken := strings.TrimSpace(
		context.Request.Header.Get("Token"),
	)
	message, err := ioutil.ReadAll(context.Request.Body)

	if err != nil {
		logger.Errorf(
			"could not read response body, reason: %s",
			err.Error(),
		)
		context.JSON(makeInternalServerError(err.Error(), nil))
		return
	}

	s.sync <- true
	defer func() {
		<-s.sync
	}()

	if _, exists := s.currentSubscriptions[authToken]; exists {

		s.currentSubscriptions.publishMessage(authToken, message)

		context.JSON(makeStatusOk("message published", string(message)))
		return
	}

	s.cache.put(authToken, string(message))

	context.JSON(makeNotFoundError(
		fmt.Sprintf("recipient %s not found", authToken),
		string(message),
	))
	return
}

func (s *service) handleStats(context *gin.Context) {
	stat := ServiceStats{
		SubscribersCount: len(s.currentSubscriptions),
	}

	context.JSON(makeStatusOk(
		context.Request.URL.String(), stat,
	))

	return
}

func (s *service) handleStatsSessions(context *gin.Context) {

	sessionsCount := make(map[string]int)

	s.sync <- true
	defer func() {
		<-s.sync
	}()

	for authToken, sessions := range s.currentSubscriptions {
		sessionsCount[authToken] = len(sessions)

	}
	sortedCurrentSessions := newSortedSessions(sessionsCount)
	sortedCurrentSessions.Sort()

	stat := ActiveSessions{
		SubscribersSessionsCount: sessionsCount,
	}

	context.JSON(makeStatusOk(
		context.Request.URL.String(), stat,
	))

	return
}
func (s *service) handleHealth(context *gin.Context) {

	context.JSON(makeStatusOk(
		context.Request.URL.String(), "OK",
	))

	return
}
<|endoftext|>


<commit_before>// Copyright 2013 Mathias Monnerville. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cloudinary provides support for managing static assets
// on the Cloudinary service.
//
// The Cloudinary service allows image and raw files management in
// the cloud.
package cloudinary

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	baseUploadUrl   = "http://api.cloudinary.com/v1_1"
	baseResourceUrl = "http://res.cloudinary.com"
	imageType       = "image"
	rawType         = "raw"
)

type ResourceType int

const (
	ImageType ResourceType = iota
	RawType
)

type Service struct {
	cloudName     string
	apiKey        string
	apiSecret     string
	uploadURI     *url.URL     // To upload resources
	adminURI      *url.URL     // To use the admin API
	mongoDbURI    *url.URL     // Can be nil: upload sync disabled
	uploadResType ResourceType // Upload resource type
	basePathDir   string       // Base path directory
}

// Resource holds information about an image or a raw file.
type Resource struct {
	PublicId     string `json:"public_id"`
	Version      int    `json:"version"`
	ResourceType string `json:"resource_type"` // image or raw
	Size         int    `json:"bytes"`         // In bytes
	Url          string `json:"url"`           // Remote url
	SecureUrl    string `json:"secure_url"`    // Over https
}

type pagination struct {
	NextCursor int64 `json: "next_cursor"`
}

type resourceList struct {
	pagination
	Resources []*Resource `json: "resources"`
}

// Upload response after uploading a file.
type uploadResponse struct {
	PublicId     string `json:"public_id"`
	Version      uint   `json:"version"`
	Format       string `json:"format"`
	ResourceType string `json:"resource_type"` // "image" or "raw"
}

// Dial will use the url to connect to the Cloudinary service.
// The uri parameter must be a valid URI with the cloudinary:// scheme,
// e.g.
//  cloudinary://api_key:api_secret@cloud_name
func Dial(uri string) (*Service, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "cloudinary" {
		return nil, errors.New("Missing cloudinary:// scheme in URI")
	}
	secret, exists := u.User.Password()
	if !exists {
		return nil, errors.New("No API secret provided in URI.")
	}
	s := &Service{
		cloudName:     u.Host,
		apiKey:        u.User.Username(),
		apiSecret:     secret,
		uploadResType: ImageType,
	}
	// Default upload URI to the service. Can change at runtime in the
	// Upload() function for raw file uploading.
	up, err := url.Parse(fmt.Sprintf("%s/%s/image/upload/", baseUploadUrl, s.cloudName))
	if err != nil {
		return nil, err
	}
	s.uploadURI = up

	// Admin API url
	adm, err := url.Parse(fmt.Sprintf("%s/%s", baseAdminUrl, s.cloudName))
	if err != nil {
		return nil, err
	}
	adm.User = url.UserPassword(s.apiKey, s.apiSecret)
	s.adminURI = adm
	return s, nil
}

// UseDatabase connects to a mongoDB database and stores upload JSON
// responses, along with a source file checksum to prevent uploading
// the same file twice. Stored information is used by Url() to build
// a public URL for accessing the uploaded resource.
func (s *Service) UseDatabase(mongoDbURI string) error {
	u, err := url.Parse(mongoDbURI)
	if err != nil {
		return err
	}
	if u.Scheme != "mongodb" {
		return errors.New("Missing mongodb:// scheme in URI")
	}
	s.mongoDbURI = u
	return nil
}

// CloudName returns the cloud name used to access the Cloudinary service.
func (s *Service) CloudName() string {
	return s.cloudName
}

// ApiKey returns the API key used to access the Cloudinary service.
func (s *Service) ApiKey() string {
	return s.apiKey
}

// DefaultUploadURI returns the default URI used to upload images to the Cloudinary service.
func (s *Service) DefaultUploadURI() *url.URL {
	return s.uploadURI
}

// cleanAssetName returns an asset name from the parent dirname and
// the file name without extension. The path /tmp/css/default.css will
// return css/default.
func cleanAssetName(path, basePath string) string {
	var name string
	if basePath == "" {
		idx := strings.LastIndex(path, string(os.PathSeparator))
		if idx != -1 {
			idx = strings.LastIndex(path[:idx], string(os.PathSeparator))
		}
		name = path[idx+1:]
	} else {
		// Directory
		name = strings.Replace(path, basePath, "", 1)
		if name[0] == os.PathSeparator {
			name = name[1:]
		}
	}
	return name[:len(name)-len(filepath.Ext(name))]
}

func (s *Service) walkIt(path string, info os.FileInfo, err error) error {
	if info.IsDir() {
		return nil
	}
	if _, err := s.uploadFile(path, nil, false); err != nil {
		return err
	}
	return nil
}

// Upload file to the service. See Upload().
func (s *Service) uploadFile(path string, data io.Reader, randomPublicId bool) (string, error) {
	buf := new(bytes.Buffer)
	w := multipart.NewWriter(buf)

	// Write public ID
	var publicId string
	if !randomPublicId {
		publicId = cleanAssetName(path, s.basePathDir)
		pi, err := w.CreateFormField("public_id")
		if err != nil {
			return publicId, err
		}
		pi.Write([]byte(publicId))
	}

	// Write API key
	ak, err := w.CreateFormField("api_key")
	if err != nil {
		return publicId, err
	}
	ak.Write([]byte(s.apiKey))

	// Write timestamp
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	ts, err := w.CreateFormField("timestamp")
	if err != nil {
		return publicId, err
	}
	ts.Write([]byte(timestamp))

	// Write signature
	hash := sha1.New()
	part := fmt.Sprintf("timestamp=%s%s", timestamp, s.apiSecret)
	if !randomPublicId {
		part = fmt.Sprintf("public_id=%s&%s", publicId, part)
	}
	io.WriteString(hash, part)
	signature := fmt.Sprintf("%x", hash.Sum(nil))

	si, err := w.CreateFormField("signature")
	if err != nil {
		return publicId, err
	}
	si.Write([]byte(signature))

	// Write file field
	fw, err := w.CreateFormFile("file", path)
	if err != nil {
		return publicId, err
	}
	if (data != nil) { // file descriptor given
		tmp, err := ioutil.ReadAll(data)
		if err != nil {
			return publicId, err
		}
		fw.Write(tmp)
	} else { // no file descriptor, try opening the file
		fd, err := os.Open(path)
		if err != nil {
			return publicId, err
		}
		defer fd.Close()

		_, err = io.Copy(fw, fd)
		if err != nil {
			return publicId, err
		}
	}
	// Don't forget to close the multipart writer to get a terminating boundary
	w.Close()

	upURI := s.uploadURI.String()
	if s.uploadResType == RawType {
		upURI = strings.Replace(upURI, imageType, rawType, 1)
	}
	req, err := http.NewRequest("POST", upURI, buf)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		return "", err
	}

	if resp.StatusCode == http.StatusOK {
		// Body is JSON data and looks like:
		// {"public_id":"Downloads/file","version":1369431906,"format":"png","resource_type":"image"}
		dec := json.NewDecoder(resp.Body)
		upInfo := new(uploadResponse)
		if err := dec.Decode(upInfo); err != nil {
			return publicId, err
		}
		fmt.Println(upInfo.PublicId)
		return upInfo.PublicId, nil
	} else {
		return publicId, errors.New("Request error: " + resp.Status)
	}
}

// Upload a file or a set of files in the cloud. Set ramdomPublicId to true
// to let the service generate a unique random public id. If set to false,
// the resource's public id is computed using the absolute path to the file.
// Set rtype to the target resource type, e.g. image or raw file.
//
// For example, a raw file /tmp/css/default.css will be stored with a public
// name of css/default.css (raw file keeps its extension), but an image file
// /tmp/images/logo.png will be stored as images/logo.
//
// If the source path is a directory, all files are recursively uploaded to
// the cloud service.
func (s *Service) Upload(path string, data io.Reader, randomPublicId bool, rtype ResourceType) (string, error) {
	s.uploadResType = rtype
	s.basePathDir = ""
	if data == nil {
		info, err := os.Stat(path)
		if err != nil {
			return path, err
		}

		if info.IsDir() {
			s.basePathDir = path
			if err := filepath.Walk(path, s.walkIt); err != nil {
				return path, err
			}
		} else {
			return s.uploadFile(path, nil, randomPublicId)
		}
	} else {
		return s.uploadFile(path, data, randomPublicId)
	}
	return path, nil
}

// Url returns the complete access path in the cloud to the
// resource designed by publicId or the empty string if
// no match.
func (s *Service) Url(publicId string, rtype ResourceType) string {
	path := imageType
	if rtype == RawType {
		path = rawType
	}
	return fmt.Sprintf("%s/%s/%s/upload/%s", baseResourceUrl, s.cloudName, path, publicId)
}

func handleHttpResponse(resp *http.Response) (map[string]interface{}, error) {
	if resp == nil {
		return nil, errors.New("nil http response")
	}
	dec := json.NewDecoder(resp.Body)
	var msg interface{}
	if err := dec.Decode(&msg); err != nil {
		return nil, err
	}
	m := msg.(map[string]interface{})
	if resp.StatusCode != http.StatusOK {
		// JSON error looks like {"error":{"message":"Missing required parameter - public_id"}}
		if e, ok := m["error"]; ok {
			return nil, errors.New(e.(map[string]interface{})["message"].(string))
		}
		return nil, errors.New(resp.Status)
	}
	return m, nil
}

// Delete deletes a resource uploaded to Cloudinary.
func (s *Service) Delete(publicId string, rtype ResourceType) error {
	// TODO: also delete resource entry from database (if used)
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	data := url.Values{
		"api_key":   []string{s.apiKey},
		"public_id": []string{publicId},
		"timestamp": []string{timestamp},
	}

	// Signature
	hash := sha1.New()
	part := fmt.Sprintf("public_id=%s&timestamp=%s%s", publicId, timestamp, s.apiSecret)
	io.WriteString(hash, part)
	data.Set("signature", fmt.Sprintf("%x", hash.Sum(nil)))

	rt := imageType
	if rtype == RawType {
		rt = rawType
	}
	resp, err := http.PostForm(fmt.Sprintf("%s/%s/%s/destroy/", baseUploadUrl, s.cloudName, rt), data)
	if err != nil {
		return err
	}

	m, err := handleHttpResponse(resp)
	if err != nil {
		return err
	}
	if e, ok := m["result"]; ok {
		fmt.Println(e.(string))
	}
	return nil
}
<commit_msg>Delete a fmt.Printf<commit_after>// Copyright 2013 Mathias Monnerville. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package cloudinary provides support for managing static assets
// on the Cloudinary service.
//
// The Cloudinary service allows image and raw files management in
// the cloud.
package cloudinary

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	baseUploadUrl   = "http://api.cloudinary.com/v1_1"
	baseResourceUrl = "http://res.cloudinary.com"
	imageType       = "image"
	rawType         = "raw"
)

type ResourceType int

const (
	ImageType ResourceType = iota
	RawType
)

type Service struct {
	cloudName     string
	apiKey        string
	apiSecret     string
	uploadURI     *url.URL     // To upload resources
	adminURI      *url.URL     // To use the admin API
	mongoDbURI    *url.URL     // Can be nil: upload sync disabled
	uploadResType ResourceType // Upload resource type
	basePathDir   string       // Base path directory
}

// Resource holds information about an image or a raw file.
type Resource struct {
	PublicId     string `json:"public_id"`
	Version      int    `json:"version"`
	ResourceType string `json:"resource_type"` // image or raw
	Size         int    `json:"bytes"`         // In bytes
	Url          string `json:"url"`           // Remote url
	SecureUrl    string `json:"secure_url"`    // Over https
}

type pagination struct {
	NextCursor int64 `json: "next_cursor"`
}

type resourceList struct {
	pagination
	Resources []*Resource `json: "resources"`
}

// Upload response after uploading a file.
type uploadResponse struct {
	PublicId     string `json:"public_id"`
	Version      uint   `json:"version"`
	Format       string `json:"format"`
	ResourceType string `json:"resource_type"` // "image" or "raw"
}

// Dial will use the url to connect to the Cloudinary service.
// The uri parameter must be a valid URI with the cloudinary:// scheme,
// e.g.
//  cloudinary://api_key:api_secret@cloud_name
func Dial(uri string) (*Service, error) {
	u, err := url.Parse(uri)
	if err != nil {
		return nil, err
	}
	if u.Scheme != "cloudinary" {
		return nil, errors.New("Missing cloudinary:// scheme in URI")
	}
	secret, exists := u.User.Password()
	if !exists {
		return nil, errors.New("No API secret provided in URI.")
	}
	s := &Service{
		cloudName:     u.Host,
		apiKey:        u.User.Username(),
		apiSecret:     secret,
		uploadResType: ImageType,
	}
	// Default upload URI to the service. Can change at runtime in the
	// Upload() function for raw file uploading.
	up, err := url.Parse(fmt.Sprintf("%s/%s/image/upload/", baseUploadUrl, s.cloudName))
	if err != nil {
		return nil, err
	}
	s.uploadURI = up

	// Admin API url
	adm, err := url.Parse(fmt.Sprintf("%s/%s", baseAdminUrl, s.cloudName))
	if err != nil {
		return nil, err
	}
	adm.User = url.UserPassword(s.apiKey, s.apiSecret)
	s.adminURI = adm
	return s, nil
}

// UseDatabase connects to a mongoDB database and stores upload JSON
// responses, along with a source file checksum to prevent uploading
// the same file twice. Stored information is used by Url() to build
// a public URL for accessing the uploaded resource.
func (s *Service) UseDatabase(mongoDbURI string) error {
	u, err := url.Parse(mongoDbURI)
	if err != nil {
		return err
	}
	if u.Scheme != "mongodb" {
		return errors.New("Missing mongodb:// scheme in URI")
	}
	s.mongoDbURI = u
	return nil
}

// CloudName returns the cloud name used to access the Cloudinary service.
func (s *Service) CloudName() string {
	return s.cloudName
}

// ApiKey returns the API key used to access the Cloudinary service.
func (s *Service) ApiKey() string {
	return s.apiKey
}

// DefaultUploadURI returns the default URI used to upload images to the Cloudinary service.
func (s *Service) DefaultUploadURI() *url.URL {
	return s.uploadURI
}

// cleanAssetName returns an asset name from the parent dirname and
// the file name without extension. The path /tmp/css/default.css will
// return css/default.
func cleanAssetName(path, basePath string) string {
	var name string
	if basePath == "" {
		idx := strings.LastIndex(path, string(os.PathSeparator))
		if idx != -1 {
			idx = strings.LastIndex(path[:idx], string(os.PathSeparator))
		}
		name = path[idx+1:]
	} else {
		// Directory
		name = strings.Replace(path, basePath, "", 1)
		if name[0] == os.PathSeparator {
			name = name[1:]
		}
	}
	return name[:len(name)-len(filepath.Ext(name))]
}

func (s *Service) walkIt(path string, info os.FileInfo, err error) error {
	if info.IsDir() {
		return nil
	}
	if _, err := s.uploadFile(path, nil, false); err != nil {
		return err
	}
	return nil
}

// Upload file to the service. See Upload().
func (s *Service) uploadFile(path string, data io.Reader, randomPublicId bool) (string, error) {
	buf := new(bytes.Buffer)
	w := multipart.NewWriter(buf)

	// Write public ID
	var publicId string
	if !randomPublicId {
		publicId = cleanAssetName(path, s.basePathDir)
		pi, err := w.CreateFormField("public_id")
		if err != nil {
			return publicId, err
		}
		pi.Write([]byte(publicId))
	}

	// Write API key
	ak, err := w.CreateFormField("api_key")
	if err != nil {
		return publicId, err
	}
	ak.Write([]byte(s.apiKey))

	// Write timestamp
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	ts, err := w.CreateFormField("timestamp")
	if err != nil {
		return publicId, err
	}
	ts.Write([]byte(timestamp))

	// Write signature
	hash := sha1.New()
	part := fmt.Sprintf("timestamp=%s%s", timestamp, s.apiSecret)
	if !randomPublicId {
		part = fmt.Sprintf("public_id=%s&%s", publicId, part)
	}
	io.WriteString(hash, part)
	signature := fmt.Sprintf("%x", hash.Sum(nil))

	si, err := w.CreateFormField("signature")
	if err != nil {
		return publicId, err
	}
	si.Write([]byte(signature))

	// Write file field
	fw, err := w.CreateFormFile("file", path)
	if err != nil {
		return publicId, err
	}
	if (data != nil) { // file descriptor given
		tmp, err := ioutil.ReadAll(data)
		if err != nil {
			return publicId, err
		}
		fw.Write(tmp)
	} else { // no file descriptor, try opening the file
		fd, err := os.Open(path)
		if err != nil {
			return publicId, err
		}
		defer fd.Close()

		_, err = io.Copy(fw, fd)
		if err != nil {
			return publicId, err
		}
	}
	// Don't forget to close the multipart writer to get a terminating boundary
	w.Close()

	upURI := s.uploadURI.String()
	if s.uploadResType == RawType {
		upURI = strings.Replace(upURI, imageType, rawType, 1)
	}
	req, err := http.NewRequest("POST", upURI, buf)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	resp, err := http.DefaultClient.Do(req)

	if err != nil {
		return "", err
	}

	if resp.StatusCode == http.StatusOK {
		// Body is JSON data and looks like:
		// {"public_id":"Downloads/file","version":1369431906,"format":"png","resource_type":"image"}
		dec := json.NewDecoder(resp.Body)
		upInfo := new(uploadResponse)
		if err := dec.Decode(upInfo); err != nil {
			return publicId, err
		}
		return upInfo.PublicId, nil
	} else {
		return publicId, errors.New("Request error: " + resp.Status)
	}
}

// Upload a file or a set of files in the cloud. Set ramdomPublicId to true
// to let the service generate a unique random public id. If set to false,
// the resource's public id is computed using the absolute path to the file.
// Set rtype to the target resource type, e.g. image or raw file.
//
// For example, a raw file /tmp/css/default.css will be stored with a public
// name of css/default.css (raw file keeps its extension), but an image file
// /tmp/images/logo.png will be stored as images/logo.
//
// If the source path is a directory, all files are recursively uploaded to
// the cloud service.
func (s *Service) Upload(path string, data io.Reader, randomPublicId bool, rtype ResourceType) (string, error) {
	s.uploadResType = rtype
	s.basePathDir = ""
	if data == nil {
		info, err := os.Stat(path)
		if err != nil {
			return path, err
		}

		if info.IsDir() {
			s.basePathDir = path
			if err := filepath.Walk(path, s.walkIt); err != nil {
				return path, err
			}
		} else {
			return s.uploadFile(path, nil, randomPublicId)
		}
	} else {
		return s.uploadFile(path, data, randomPublicId)
	}
	return path, nil
}

// Url returns the complete access path in the cloud to the
// resource designed by publicId or the empty string if
// no match.
func (s *Service) Url(publicId string, rtype ResourceType) string {
	path := imageType
	if rtype == RawType {
		path = rawType
	}
	return fmt.Sprintf("%s/%s/%s/upload/%s", baseResourceUrl, s.cloudName, path, publicId)
}

func handleHttpResponse(resp *http.Response) (map[string]interface{}, error) {
	if resp == nil {
		return nil, errors.New("nil http response")
	}
	dec := json.NewDecoder(resp.Body)
	var msg interface{}
	if err := dec.Decode(&msg); err != nil {
		return nil, err
	}
	m := msg.(map[string]interface{})
	if resp.StatusCode != http.StatusOK {
		// JSON error looks like {"error":{"message":"Missing required parameter - public_id"}}
		if e, ok := m["error"]; ok {
			return nil, errors.New(e.(map[string]interface{})["message"].(string))
		}
		return nil, errors.New(resp.Status)
	}
	return m, nil
}

// Delete deletes a resource uploaded to Cloudinary.
func (s *Service) Delete(publicId string, rtype ResourceType) error {
	// TODO: also delete resource entry from database (if used)
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)
	data := url.Values{
		"api_key":   []string{s.apiKey},
		"public_id": []string{publicId},
		"timestamp": []string{timestamp},
	}

	// Signature
	hash := sha1.New()
	part := fmt.Sprintf("public_id=%s&timestamp=%s%s", publicId, timestamp, s.apiSecret)
	io.WriteString(hash, part)
	data.Set("signature", fmt.Sprintf("%x", hash.Sum(nil)))

	rt := imageType
	if rtype == RawType {
		rt = rawType
	}
	resp, err := http.PostForm(fmt.Sprintf("%s/%s/%s/destroy/", baseUploadUrl, s.cloudName, rt), data)
	if err != nil {
		return err
	}

	m, err := handleHttpResponse(resp)
	if err != nil {
		return err
	}
	if e, ok := m["result"]; ok {
		fmt.Println(e.(string))
	}
	return nil
}
<|endoftext|>


<commit_before>package graval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	welcomeMessage = "Welcome to the Go FTP Server"
)

type FTPConn struct {
	cwd           string
	conn          *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
	driver        FTPDriver
	reqUser       string
	user          string
}

// NewFTPConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDrive that
// will handle all auth and persistence details.
func NewFTPConn(tcpConn *net.TCPConn, driver FTPDriver) *FTPConn {
	c := new(FTPConn)
	c.cwd = "/"
	c.conn = tcpConn
	c.controlReader = bufio.NewReader(tcpConn)
	c.controlWriter = bufio.NewWriter(tcpConn)
	c.driver = driver
	return c
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err == nil {
			ftpConn.receiveLine(line)
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *FTPConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *FTPConn) receiveLine(line string) {
	log.Print(line)
	command, param := ftpConn.parseLine(line)
	switch command {
	case "USER":
		ftpConn.cmdUser(param)
		break
	case "PASS":
		ftpConn.cmdPass(param)
		break
	default:
		ftpConn.writeMessage(500, "Command not found")
	}
}

func (ftpConn *FTPConn) cmdUser(param string) {
	ftpConn.reqUser = param
	ftpConn.writeMessage(331, "User name ok, password required")
}

func (ftpConn *FTPConn) cmdPass(param string) {
	if ftpConn.driver.Authenticate(ftpConn.reqUser, param) {
		ftpConn.user = ftpConn.reqUser
		ftpConn.reqUser = ""
		ftpConn.writeMessage(230, "Password ok, continue")
	} else {
		ftpConn.writeMessage(530, "Incorrect password, not logged in")
	}
}

func (ftpConn *FTPConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], params[1]
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *FTPConn) writeMessage(code int, message string) (wrote int, err error) {
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}
<commit_msg>recognise the QUIT FTP command<commit_after>package graval

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
)

const (
	welcomeMessage = "Welcome to the Go FTP Server"
)

type FTPConn struct {
	cwd           string
	conn          *net.TCPConn
	controlReader *bufio.Reader
	controlWriter *bufio.Writer
	data          *net.TCPConn
	driver        FTPDriver
	reqUser       string
	user          string
}

// NewFTPConn constructs a new object that will handle the FTP protocol over
// an active net.TCPConn. The TCP connection should already be open before
// it is handed to this functions. driver is an instance of FTPDrive that
// will handle all auth and persistence details.
func NewFTPConn(tcpConn *net.TCPConn, driver FTPDriver) *FTPConn {
	c := new(FTPConn)
	c.cwd = "/"
	c.conn = tcpConn
	c.controlReader = bufio.NewReader(tcpConn)
	c.controlWriter = bufio.NewWriter(tcpConn)
	c.driver = driver
	return c
}

// Serve starts an endless loop that reads FTP commands from the client and
// responds appropriately. terminated is a channel that will receive a true
// message when the connection closes. This loop will be running inside a
// goroutine, so use this channel to be notified when the connection can be
// cleaned up.
func (ftpConn *FTPConn) Serve(terminated chan bool) {
	log.Print("Connection Established")
	// send welcome
	ftpConn.writeMessage(220, welcomeMessage)
	// read commands
	for {
		line, err := ftpConn.controlReader.ReadString('\n')
		if err == nil {
			ftpConn.receiveLine(line)
		}
	}
	terminated <- true
	log.Print("Connection Terminated")
}

// Close will manually close this connection, even if the client isn't ready.
func (ftpConn *FTPConn) Close() {
	ftpConn.conn.Close()
	if ftpConn.data != nil {
		ftpConn.data.Close()
	}
}

// receiveLine accepts a single line FTP command and co-ordinates an
// appropriate response.
func (ftpConn *FTPConn) receiveLine(line string) {
	log.Print(line)
	command, param := ftpConn.parseLine(line)
	switch command {
	case "USER":
		ftpConn.cmdUser(param)
		break
	case "PASS":
		ftpConn.cmdPass(param)
		break
	case "QUIT":
		ftpConn.Close()
		break
	default:
		ftpConn.writeMessage(500, "Command not found")
	}
}

// cmdUser responds to the USER FTP command by asking for the password
func (ftpConn *FTPConn) cmdUser(param string) {
	ftpConn.reqUser = param
	ftpConn.writeMessage(331, "User name ok, password required")
}

// cmdPass respond to the PASS FTP command by asking the driver if the supplied
// username and password are valid
func (ftpConn *FTPConn) cmdPass(param string) {
	if ftpConn.driver.Authenticate(ftpConn.reqUser, param) {
		ftpConn.user = ftpConn.reqUser
		ftpConn.reqUser = ""
		ftpConn.writeMessage(230, "Password ok, continue")
	} else {
		ftpConn.writeMessage(530, "Incorrect password, not logged in")
	}
}

func (ftpConn *FTPConn) parseLine(line string) (string, string) {
	params := strings.SplitN(strings.Trim(line, "\r\n"), " ", 2)
	if len(params) == 1 {
		return params[0], ""
	}
	return params[0], params[1]
}

// writeMessage will send a standard FTP response back to the client.
func (ftpConn *FTPConn) writeMessage(code int, message string) (wrote int, err error) {
	line := fmt.Sprintf("%d %s\r\n", code, message)
	wrote, err = ftpConn.controlWriter.WriteString(line)
	ftpConn.controlWriter.Flush()
	log.Print(message)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

func containerMetadataGet(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}
	metadataPath := filepath.Join(c.Path(), "metadata.yaml")

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Read the metadata
	metadataFile, err := os.Open(metadataPath)
	if err != nil {
		return InternalError(err)
	}
	defer metadataFile.Close()

	data, err := ioutil.ReadAll(metadataFile)
	if err != nil {
		return InternalError(err)
	}

	// Parse into the API struct
	metadata := api.ImageMetadata{}
	err = yaml.Unmarshal(data, &metadata)
	if err != nil {
		return SmartError(err)
	}

	return SyncResponse(true, metadata)
}

func containerMetadataPut(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}
	metadataPath := filepath.Join(c.Path(), "metadata.yaml")

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Read the new metadata
	metadata := api.ImageMetadata{}
	if err := json.NewDecoder(r.Body).Decode(&metadata); err != nil {
		return BadRequest(err)
	}

	// Write as YAML
	data, err := yaml.Marshal(metadata)
	if err != nil {
		return BadRequest(err)
	}

	if err := ioutil.WriteFile(metadataPath, data, 0644); err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

// Return a list of templates used in a container or the content of a template
func containerMetadataTemplatesGet(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		// List templates
		templatesPath := filepath.Join(c.Path(), "templates")
		filesInfo, err := ioutil.ReadDir(templatesPath)
		if err != nil {
			return InternalError(err)
		}

		templates := []string{}
		for _, info := range filesInfo {
			if !info.IsDir() {
				templates = append(templates, info.Name())
			}
		}

		return SyncResponse(true, templates)
	}

	// Check if the template exists
	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if !shared.PathExists(templatePath) {
		return NotFound(fmt.Errorf("Path '%s' not found", templatePath))
	}

	// Create a temporary file with the template content (since the container
	// storage might not be available when the file is read from FileResponse)
	template, err := os.Open(templatePath)
	if err != nil {
		return SmartError(err)
	}
	defer template.Close()

	tempfile, err := ioutil.TempFile("", "lxd_template")
	if err != nil {
		return SmartError(err)
	}
	defer tempfile.Close()

	_, err = io.Copy(tempfile, template)
	if err != nil {
		return InternalError(err)
	}

	files := make([]fileResponseEntry, 1)
	files[0].identifier = templateName
	files[0].path = tempfile.Name()
	files[0].filename = templateName
	return FileResponse(r, files, nil, true)
}

// Add a container template file
func containerMetadataTemplatesPostPut(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		return BadRequest(fmt.Errorf("missing path argument"))
	}

	// Check if the template already exists
	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if r.Method == "POST" && shared.PathExists(templatePath) {
		return BadRequest(fmt.Errorf("Template already exists"))
	}

	// Write the new template
	template, err := os.OpenFile(templatePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return SmartError(err)
	}
	defer template.Close()

	_, err = io.Copy(template, r.Body)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

// Delete a container template
func containerMetadataTemplatesDelete(d *Daemon, r *http.Request) Response {
	project := projectParam(r)

	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		return BadRequest(fmt.Errorf("missing path argument"))
	}

	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if !shared.PathExists(templatePath) {
		return NotFound(fmt.Errorf("Path '%s' not found", templatePath))
	}

	// Delete the template
	err = os.Remove(templatePath)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

// Return the full path of a container template.
func getContainerTemplatePath(c container, filename string) (string, error) {
	if strings.Contains(filename, "/") {
		return "", fmt.Errorf("Invalid template filename")
	}

	return filepath.Join(c.Path(), "templates", filename), nil
}
<commit_msg>lxd/containers: Fix template handling<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

func containerMetadataGet(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}
	metadataPath := filepath.Join(c.Path(), "metadata.yaml")

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// If missing, just return empty result
	if !shared.PathExists(metadataPath) {
		return SyncResponse(true, api.ImageMetadata{})
	}

	// Read the metadata
	metadataFile, err := os.Open(metadataPath)
	if err != nil {
		return InternalError(err)
	}
	defer metadataFile.Close()

	data, err := ioutil.ReadAll(metadataFile)
	if err != nil {
		return InternalError(err)
	}

	// Parse into the API struct
	metadata := api.ImageMetadata{}
	err = yaml.Unmarshal(data, &metadata)
	if err != nil {
		return SmartError(err)
	}

	return SyncResponse(true, metadata)
}

func containerMetadataPut(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}
	metadataPath := filepath.Join(c.Path(), "metadata.yaml")

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Read the new metadata
	metadata := api.ImageMetadata{}
	if err := json.NewDecoder(r.Body).Decode(&metadata); err != nil {
		return BadRequest(err)
	}

	// Write as YAML
	data, err := yaml.Marshal(metadata)
	if err != nil {
		return BadRequest(err)
	}

	if err := ioutil.WriteFile(metadataPath, data, 0644); err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

// Return a list of templates used in a container or the content of a template
func containerMetadataTemplatesGet(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		templates := []string{}
		if !shared.PathExists(filepath.Join(c.Path(), "templates")) {
			return SyncResponse(true, templates)
		}

		// List templates
		templatesPath := filepath.Join(c.Path(), "templates")
		filesInfo, err := ioutil.ReadDir(templatesPath)
		if err != nil {
			return InternalError(err)
		}

		for _, info := range filesInfo {
			if !info.IsDir() {
				templates = append(templates, info.Name())
			}
		}

		return SyncResponse(true, templates)
	}

	// Check if the template exists
	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if !shared.PathExists(templatePath) {
		return NotFound(fmt.Errorf("Template '%s' not found", templateName))
	}

	// Create a temporary file with the template content (since the container
	// storage might not be available when the file is read from FileResponse)
	template, err := os.Open(templatePath)
	if err != nil {
		return SmartError(err)
	}
	defer template.Close()

	tempfile, err := ioutil.TempFile("", "lxd_template")
	if err != nil {
		return SmartError(err)
	}
	defer tempfile.Close()

	_, err = io.Copy(tempfile, template)
	if err != nil {
		return InternalError(err)
	}

	files := make([]fileResponseEntry, 1)
	files[0].identifier = templateName
	files[0].path = tempfile.Name()
	files[0].filename = templateName
	return FileResponse(r, files, nil, true)
}

// Add a container template file
func containerMetadataTemplatesPostPut(d *Daemon, r *http.Request) Response {
	project := projectParam(r)
	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		return BadRequest(fmt.Errorf("missing path argument"))
	}

	if !shared.PathExists(filepath.Join(c.Path(), "templates")) {
		err := os.MkdirAll(filepath.Join(c.Path(), "templates"), 0711)
		if err != nil {
			return SmartError(err)
		}
	}

	// Check if the template already exists
	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if r.Method == "POST" && shared.PathExists(templatePath) {
		return BadRequest(fmt.Errorf("Template already exists"))
	}

	// Write the new template
	template, err := os.OpenFile(templatePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return SmartError(err)
	}
	defer template.Close()

	_, err = io.Copy(template, r.Body)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

// Delete a container template
func containerMetadataTemplatesDelete(d *Daemon, r *http.Request) Response {
	project := projectParam(r)

	name := mux.Vars(r)["name"]

	// Handle requests targeted to a container on a different node
	response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
	if err != nil {
		return SmartError(err)
	}
	if response != nil {
		return response
	}

	// Load the container
	c, err := containerLoadByProjectAndName(d.State(), project, name)
	if err != nil {
		return SmartError(err)
	}

	// Start the storage if needed
	ourStart, err := c.StorageStart()
	if err != nil {
		return SmartError(err)
	}
	if ourStart {
		defer c.StorageStop()
	}

	// Look at the request
	templateName := r.FormValue("path")
	if templateName == "" {
		return BadRequest(fmt.Errorf("missing path argument"))
	}

	templatePath, err := getContainerTemplatePath(c, templateName)
	if err != nil {
		return SmartError(err)
	}

	if !shared.PathExists(templatePath) {
		return NotFound(fmt.Errorf("Template '%s' not found", templateName))
	}

	// Delete the template
	err = os.Remove(templatePath)
	if err != nil {
		return InternalError(err)
	}

	return EmptySyncResponse
}

// Return the full path of a container template.
func getContainerTemplatePath(c container, filename string) (string, error) {
	if strings.Contains(filename, "/") {
		return "", fmt.Errorf("Invalid template filename")
	}

	return filepath.Join(c.Path(), "templates", filename), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
	recursionStr := r.FormValue("recursion")
	recursion, err := strconv.Atoi(recursionStr)
	if err != nil {
		recursion = 0
	}

	cname := mux.Vars(r)["name"]
	// Makes sure the requested container exists.
	_, err = containerLXDLoad(d, cname)
	if err != nil {
		return SmartError(err)
	}

	results, err := dbContainerGetSnapshots(d.db, cname)
	if err != nil {
		return SmartError(err)
	}

	resultString := []string{}
	resultMap := []shared.Jmap{}

	for _, name := range results {
		sc, err := containerLXDLoad(d, name)
		if err != nil {
			shared.Log.Error("Failed to load snapshot", log.Ctx{"snapshot": name})
			continue
		}

		snapName := strings.TrimPrefix(name, name+shared.SnapshotDelimiter)
		if recursion == 0 {
			url := fmt.Sprintf("/%s/containers/%s/snapshots/%s", shared.APIVersion, cname, snapName)
			resultString = append(resultString, url)
		} else {
			body := shared.Jmap{"name": snapName, "stateful": shared.PathExists(sc.StateDir())}
			resultMap = append(resultMap, body)
		}
	}

	if recursion == 0 {
		return SyncResponse(true, resultString)
	}

	return SyncResponse(true, resultMap)
}

/*
 * Note, the code below doesn't deal with snapshots of snapshots.
 * To do that, we'll need to weed out based on # slashes in names
 */
func nextSnapshot(d *Daemon, name string) int {
	base := name + shared.SnapshotDelimiter + "snap"
	length := len(base)
	q := fmt.Sprintf("SELECT MAX(name) FROM containers WHERE type=? AND SUBSTR(name,1,?)=?")
	var numstr string
	inargs := []interface{}{cTypeSnapshot, length, base}
	outfmt := []interface{}{numstr}
	results, err := dbQueryScan(d.db, q, inargs, outfmt)
	if err != nil {
		return 0
	}
	max := 0

	for _, r := range results {
		numstr = r[0].(string)
		if len(numstr) <= length {
			continue
		}
		substr := numstr[length:]
		var num int
		count, err := fmt.Sscanf(substr, "%d", &num)
		if err != nil || count != 1 {
			continue
		}
		if num >= max {
			max = num + 1
		}
	}

	return max
}

func containerSnapshotsPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	/*
	 * snapshot is a three step operation:
	 * 1. choose a new name
	 * 2. copy the database info over
	 * 3. copy over the rootfs
	 */
	c, err := containerLXDLoad(d, name)
	if err != nil {
		return SmartError(err)
	}

	raw := shared.Jmap{}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		return BadRequest(err)
	}

	snapshotName, err := raw.GetString("name")
	if err != nil || snapshotName == "" {
		// come up with a name
		i := nextSnapshot(d, name)
		snapshotName = fmt.Sprintf("snap%d", i)
	}

	stateful, err := raw.GetBool("stateful")
	if err != nil {
		return BadRequest(err)
	}

	fullName := name +
		shared.SnapshotDelimiter +
		snapshotName

	snapshot := func(op *operation) error {
		config := c.Config()
		args := containerLXDArgs{
			Ctype:        cTypeSnapshot,
			Config:       config,
			Profiles:     c.Profiles(),
			Ephemeral:    c.IsEphemeral(),
			BaseImage:    config["volatile.base_image"],
			Architecture: c.Architecture(),
			Devices:      c.Devices(),
		}

		_, err := containerLXDCreateAsSnapshot(d, fullName, args, c, stateful)
		if err != nil {
			return err
		}

		return nil
	}

	resources := map[string][]string{}
	resources["containers"] = []string{name}

	op, err := operationCreate(operationClassTask, resources, nil, snapshot, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}

func snapshotHandler(d *Daemon, r *http.Request) Response {
	containerName := mux.Vars(r)["name"]
	snapshotName := mux.Vars(r)["snapshotName"]

	sc, err := containerLXDLoad(
		d,
		containerName+
			shared.SnapshotDelimiter+
			snapshotName)
	if err != nil {
		return SmartError(err)
	}

	switch r.Method {
	case "GET":
		return snapshotGet(sc, snapshotName)
	case "POST":
		return snapshotPost(r, sc, containerName)
	case "DELETE":
		return snapshotDelete(sc, snapshotName)
	default:
		return NotFound
	}
}

func snapshotGet(sc container, name string) Response {
	body := shared.Jmap{"name": name, "stateful": shared.PathExists(sc.StateDir())}
	return SyncResponse(true, body)
}

func snapshotPost(r *http.Request, sc container, containerName string) Response {
	raw := shared.Jmap{}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		return BadRequest(err)
	}

	newName, err := raw.GetString("name")
	if err != nil {
		return BadRequest(err)
	}

	rename := func(op *operation) error {
		return sc.Rename(containerName + shared.SnapshotDelimiter + newName)
	}

	resources := map[string][]string{}
	resources["containers"] = []string{containerName}

	op, err := operationCreate(operationClassTask, resources, nil, rename, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}

func snapshotDelete(sc container, name string) Response {
	remove := func(op *operation) error {
		return sc.Delete()
	}

	resources := map[string][]string{}
	resources["containers"] = []string{sc.Name()}

	op, err := operationCreate(operationClassTask, resources, nil, remove, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
<commit_msg>Fix snapshots list rendering<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/gorilla/mux"

	"github.com/lxc/lxd/shared"

	log "gopkg.in/inconshreveable/log15.v2"
)

func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
	recursionStr := r.FormValue("recursion")
	recursion, err := strconv.Atoi(recursionStr)
	if err != nil {
		recursion = 0
	}

	cname := mux.Vars(r)["name"]
	// Makes sure the requested container exists.
	_, err = containerLXDLoad(d, cname)
	if err != nil {
		return SmartError(err)
	}

	results, err := dbContainerGetSnapshots(d.db, cname)
	if err != nil {
		return SmartError(err)
	}

	resultString := []string{}
	resultMap := []shared.Jmap{}

	for _, name := range results {
		sc, err := containerLXDLoad(d, name)
		if err != nil {
			shared.Log.Error("Failed to load snapshot", log.Ctx{"snapshot": name})
			continue
		}

		snapName := strings.SplitN(name, shared.SnapshotDelimiter, 2)[1]
		if recursion == 0 {
			url := fmt.Sprintf("/%s/containers/%s/snapshots/%s", shared.APIVersion, cname, snapName)
			resultString = append(resultString, url)
		} else {
			body := shared.Jmap{"name": snapName, "stateful": shared.PathExists(sc.StateDir())}
			resultMap = append(resultMap, body)
		}
	}

	if recursion == 0 {
		return SyncResponse(true, resultString)
	}

	return SyncResponse(true, resultMap)
}

/*
 * Note, the code below doesn't deal with snapshots of snapshots.
 * To do that, we'll need to weed out based on # slashes in names
 */
func nextSnapshot(d *Daemon, name string) int {
	base := name + shared.SnapshotDelimiter + "snap"
	length := len(base)
	q := fmt.Sprintf("SELECT MAX(name) FROM containers WHERE type=? AND SUBSTR(name,1,?)=?")
	var numstr string
	inargs := []interface{}{cTypeSnapshot, length, base}
	outfmt := []interface{}{numstr}
	results, err := dbQueryScan(d.db, q, inargs, outfmt)
	if err != nil {
		return 0
	}
	max := 0

	for _, r := range results {
		numstr = r[0].(string)
		if len(numstr) <= length {
			continue
		}
		substr := numstr[length:]
		var num int
		count, err := fmt.Sscanf(substr, "%d", &num)
		if err != nil || count != 1 {
			continue
		}
		if num >= max {
			max = num + 1
		}
	}

	return max
}

func containerSnapshotsPost(d *Daemon, r *http.Request) Response {
	name := mux.Vars(r)["name"]

	/*
	 * snapshot is a three step operation:
	 * 1. choose a new name
	 * 2. copy the database info over
	 * 3. copy over the rootfs
	 */
	c, err := containerLXDLoad(d, name)
	if err != nil {
		return SmartError(err)
	}

	raw := shared.Jmap{}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		return BadRequest(err)
	}

	snapshotName, err := raw.GetString("name")
	if err != nil || snapshotName == "" {
		// come up with a name
		i := nextSnapshot(d, name)
		snapshotName = fmt.Sprintf("snap%d", i)
	}

	stateful, err := raw.GetBool("stateful")
	if err != nil {
		return BadRequest(err)
	}

	fullName := name +
		shared.SnapshotDelimiter +
		snapshotName

	snapshot := func(op *operation) error {
		config := c.Config()
		args := containerLXDArgs{
			Ctype:        cTypeSnapshot,
			Config:       config,
			Profiles:     c.Profiles(),
			Ephemeral:    c.IsEphemeral(),
			BaseImage:    config["volatile.base_image"],
			Architecture: c.Architecture(),
			Devices:      c.Devices(),
		}

		_, err := containerLXDCreateAsSnapshot(d, fullName, args, c, stateful)
		if err != nil {
			return err
		}

		return nil
	}

	resources := map[string][]string{}
	resources["containers"] = []string{name}

	op, err := operationCreate(operationClassTask, resources, nil, snapshot, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}

func snapshotHandler(d *Daemon, r *http.Request) Response {
	containerName := mux.Vars(r)["name"]
	snapshotName := mux.Vars(r)["snapshotName"]

	sc, err := containerLXDLoad(
		d,
		containerName+
			shared.SnapshotDelimiter+
			snapshotName)
	if err != nil {
		return SmartError(err)
	}

	switch r.Method {
	case "GET":
		return snapshotGet(sc, snapshotName)
	case "POST":
		return snapshotPost(r, sc, containerName)
	case "DELETE":
		return snapshotDelete(sc, snapshotName)
	default:
		return NotFound
	}
}

func snapshotGet(sc container, name string) Response {
	body := shared.Jmap{"name": name, "stateful": shared.PathExists(sc.StateDir())}
	return SyncResponse(true, body)
}

func snapshotPost(r *http.Request, sc container, containerName string) Response {
	raw := shared.Jmap{}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		return BadRequest(err)
	}

	newName, err := raw.GetString("name")
	if err != nil {
		return BadRequest(err)
	}

	rename := func(op *operation) error {
		return sc.Rename(containerName + shared.SnapshotDelimiter + newName)
	}

	resources := map[string][]string{}
	resources["containers"] = []string{containerName}

	op, err := operationCreate(operationClassTask, resources, nil, rename, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}

func snapshotDelete(sc container, name string) Response {
	remove := func(op *operation) error {
		return sc.Delete()
	}

	resources := map[string][]string{}
	resources["containers"] = []string{sc.Name()}

	op, err := operationCreate(operationClassTask, resources, nil, remove, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
<|endoftext|>