Skip to content

Commit

Permalink
Adding working Execute and Send()
Browse files Browse the repository at this point in the history
  • Loading branch information
0x19 committed Feb 8, 2015
1 parent ff292c1 commit 7cdca2c
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 40 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

GoESL is a small wrapper around [FreeSwitch](https://freeswitch.org/) [EventSocketLibrary](https://wiki.freeswitch.org/wiki/Event_Socket_Library) written in [Go](http://golang.org).

Point of this library is to fully implement Freeswitch ESL and bring outbound server as inbound client to you, fellow go developer :)

**This package is in DEVELOPMENT mode. It does not work and I don't know when will I get it to work due to lack of the free time in this moment.**


Expand Down
116 changes: 87 additions & 29 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package goesl

import (
"bufio"
"bytes"
"fmt"
"net"
"strconv"
"strings"
)

type SocketConnection struct {
Expand All @@ -11,18 +15,92 @@ type SocketConnection struct {
m chan *Message
}

func (c *SocketConnection) Send() error {
// Send -
func (c *SocketConnection) Send(cmd string) error {

if strings.Contains(cmd, "\r\n") {
fmt.Errorf("Invalid command provided. Command cannot contain \\r and/or \\n within. Command you provided is: %s", cmd)
}

fmt.Fprintf(c, "%s\r\n\r\n", cmd)

return nil
}

func (c *SocketConnection) Execute() error {
return nil
func (c *SocketConnection) Execute(command, args string, lock bool) error {

return c.SendMsg(map[string]string{
"call-command": "execute",
"execute-app-name": command,
"execute-app-arg": args,
"event-lock": strconv.FormatBool(lock),
}, "", "")
}

func (c *SocketConnection) ExecuteUUID() error {
return nil
}

func (c *SocketConnection) SendMsg(msg map[string]string, uuid, data string) error {

b := bytes.NewBufferString("sendmsg")

if uuid != "" {
if strings.Contains(uuid, "\r\n") {
return fmt.Errorf("Invalid command provided. Command cannot contain \\r and/or \\n within. Command you provided is: %s", msg)
}

b.WriteString(" " + uuid)
}

b.WriteString("\n")

for k, v := range msg {
// Make sure there's no \r or \n in the key, and value.
if strings.Contains(k, "\r\n") {
return fmt.Errorf("Invalid command provided. Command cannot contain \\r and/or \\n within. Command you provided is: %s", msg)
}

if v != "" {
if strings.Contains(v, "\r\n") {
return fmt.Errorf("Invalid command provided. Command cannot contain \\r and/or \\n within. Command you provided is: %s", msg)
}

b.WriteString(fmt.Sprintf("%s: %s\n", k, v))
}
}

b.WriteString("\n")

if msg["content-length"] != "" && data != "" {
b.WriteString(data)
}

if _, err := b.WriteTo(c); err != nil {
return err
}

/*
var (
ev *Event
err error
)
select {
case err = <-h.err:
return nil, err
case ev = <-h.cmd:
return ev, nil
}
*/

return nil
}

// OriginatorAdd - Will return REMOTE ADDR value
func (c *SocketConnection) OriginatorAddr() net.Addr {
return c.RemoteAddr()
}

func (c *SocketConnection) ReadMessage() (*Message, error) {
Debug("Waiting for connection message to be received ...")

Expand All @@ -34,7 +112,7 @@ func (c *SocketConnection) ReadMessage() (*Message, error) {
}
}

func (c *SocketConnection) readMessage() {
func (c *SocketConnection) handleMessage() {

msg, err := newMessage(bufio.NewReaderSize(c, READER_BUFFER_SIZE))

Expand All @@ -46,35 +124,15 @@ func (c *SocketConnection) readMessage() {
c.m <- msg
}

func (c *SocketConnection) handle() {
//defer c.Close()
func (c *SocketConnection) Handle() {

c.readMessage()

//for c.readMessage() {
//}

//for c.readMessage() {
//}

/*
cmr, err := c.tr.ReadMIMEHeader()
if err != nil {
Error("Error while reading MIME headers: %s", err)
c.err <- err
return
}
Debug("Freeswitch connection headers: %q", cmr)
*/
c.handleMessage()
}

func (c *SocketConnection) Close() error {
//if err := c.Close(); err != nil {
//return err
//}
if err := c.Close(); err != nil {
return err
}

return nil
}
17 changes: 15 additions & 2 deletions examples/server.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// Copyright 2015 Nevio Vesic

package main

import (
. "github.com/0x19/goesl"
"runtime"
//"time"
)

func main() {
Expand All @@ -19,19 +22,29 @@ func main() {

}

// handle We'll basically
func handle(s *OutboundServer) {
select {
case conn := <-s.Conn:
Notice("Got new connection: %v", conn)
Notice("New incomming connection: %v", conn)

// About to connect ....
conn.Send("connect")

if err := conn.Execute("answer", "", false); err != nil {
Error("Got error while executing answer against call: %s", err)
break
}

for {
msg, err := conn.ReadMessage()

if err != nil {
Error("Got error while reading Freeswitch message: %s", err)
continue
}

Debug("Got message: %q", msg)
Info("%s", msg.Dump())
}

}
Expand Down
100 changes: 93 additions & 7 deletions message.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,119 @@ package goesl
import (
"bufio"
"fmt"
"io"
"net/textproto"
"sort"
"strconv"
)

// Message -
type Message struct {
Header map[string]string
Body string
Headers map[string]string
Body []byte

r *bufio.Reader
tr *textproto.Reader
}

// String - Will return message representation as string
func (m *Message) String() string {
return fmt.Sprintf("%s body=%s", m.Header, m.Body)
return fmt.Sprintf("%s body=%s", m.Headers, string(m.Body))
}

// GetHeader - Will return event header value, or "" if the key is not set.
// GetHeader - Will return message header value, or "" if the key is not set.
func (m *Message) GetHeader(key string) string {
return m.Header[key]
return m.Headers[key]
}

func (m *Message) Parse() error {

cmr, err := m.tr.ReadMIMEHeader()

if err != nil {
Error("Error while reading MIME headers: %s", err)
return err
}

// Will handle content length by checking if appropriate lenght is here and if it is than
// we are going to read it into body
if lv := cmr.Get("Content-Length"); lv != "" {
l, err := strconv.Atoi(lv)

if err != nil {
Error("Got error while parsing message content-length value: %s", err)
return err
}

m.Body = make([]byte, l)

if _, err := io.ReadFull(m.r, m.Body); err != nil {
Error("Got error while reading reader body: %s", err)
return err
}
}

return m.parseMessageType(&cmr)
}

func (m *Message) parseMessageType(cmr *textproto.MIMEHeader) error {
msgType := cmr.Get("Content-Type")

Debug("Got message content (type: %s). Searching if we can handle it ...", msgType)

if !StringInSlice(msgType, AvailableMessageTypes) {
return fmt.Errorf("Unsupported message type! We got '%s'. Supported types are: %v ", msgType, AvailableMessageTypes)
}

// Assing message headers IF message is not type of event-json
if msgType != "text/event-json" {
for k, v := range *cmr {
m.Headers[k] = v[0]
}
}

switch msgType {
case "text/disconnect-notice":
for k, v := range *cmr {
Debug("Message (header: %s) -> (value: %v)", k, v)
}
}

return nil
}

// Dump - Will return message prepared to be dumped out. It's like prettify message for output
func (m *Message) Dump() (resp string) {
var keys []string

resp = ""

for k := range m.Headers {
keys = append(keys, k)
}

sort.Strings(keys)

for _, k := range keys {
resp += fmt.Sprintf("%s: %#v\n", k, m.Headers[k])
}

resp += fmt.Sprintf("BODY: %#v\n", m.Body)

return
}

// newMessage -
func newMessage(r *bufio.Reader) (*Message, error) {

msg := Message{
r: r,
tr: textproto.NewReader(r),
r: r,
tr: textproto.NewReader(r),
Headers: make(map[string]string),
}

if err := msg.Parse(); err != nil {
return &msg, err
}

return &msg, nil
Expand Down
3 changes: 3 additions & 0 deletions semaphore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package goesl

type Semaphore struct{}
5 changes: 3 additions & 2 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ func (s *OutboundServer) Start() error {
Warning("Waiting for incoming connections ...")

c, err := s.Accept()
Debug("Got new connection: %q", c)

if err != nil {
Error("Got connection error: %s", err)
Expand All @@ -46,7 +45,9 @@ func (s *OutboundServer) Start() error {
m: make(chan *Message),
}

go conn.handle()
Debug("Got new connection from: %s", conn.OriginatorAddr())

go conn.Handle()

s.Conn <- &conn
}
Expand Down
13 changes: 13 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package goesl

import ()

// StringInSlice - Will check if string in list. This is equivalent to python if x in []
func StringInSlice(str string, list []string) bool {
for _, value := range list {
if value == str {
return true
}
}
return false
}
7 changes: 7 additions & 0 deletions vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package goesl

var (

// availableMessageTypes - Returned freeswitch content-type that we have logic (support) for
AvailableMessageTypes = []string{"text/disconnect-notice", "text/event-json", "text/event-plain", "api/response", "command/reply"}
)

0 comments on commit 7cdca2c

Please sign in to comment.