// SubmitLongMsg sends a long message (more than 140 bytes) // and returns and updates the given sm with the response status. // It returns the same sm object. func (t *Transmitter) SubmitLongMsg(sm *ShortMessage) (*ShortMessage, error) { maxLen := 134 // 140-6 (UDH) rawMsg := sm.Text.Encode() countParts := int((len(rawMsg)-1)/maxLen) + 1 ri := uint8(t.r.Intn(128)) UDHHeader := make([]byte, 6) UDHHeader[0] = 5 UDHHeader[1] = 0 UDHHeader[2] = 3 UDHHeader[3] = ri UDHHeader[4] = uint8(countParts) for i := 0; i < countParts; i++ { UDHHeader[5] = uint8(i + 1) p := pdu.NewSubmitSM() f := p.Fields() f.Set(pdufield.SourceAddr, sm.Src) f.Set(pdufield.DestinationAddr, sm.Dst) if i != countParts-1 { f.Set(pdufield.ShortMessage, pdutext.Raw(append(UDHHeader, rawMsg[i*maxLen:(i+1)*maxLen]...))) } else { f.Set(pdufield.ShortMessage, pdutext.Raw(append(UDHHeader, rawMsg[i*maxLen:]...))) } f.Set(pdufield.RegisteredDelivery, uint8(sm.Register)) if sm.Validity != time.Duration(0) { f.Set(pdufield.ValidityPeriod, convertValidity(sm.Validity)) } f.Set(pdufield.ServiceType, sm.ServiceType) f.Set(pdufield.SourceAddrTON, sm.SourceAddrTON) f.Set(pdufield.SourceAddrNPI, sm.SourceAddrNPI) f.Set(pdufield.DestAddrTON, sm.DestAddrTON) f.Set(pdufield.DestAddrNPI, sm.DestAddrNPI) f.Set(pdufield.ESMClass, 0x40) f.Set(pdufield.ProtocolID, sm.ProtocolID) f.Set(pdufield.PriorityFlag, sm.PriorityFlag) f.Set(pdufield.ScheduleDeliveryTime, sm.ScheduleDeliveryTime) f.Set(pdufield.ReplaceIfPresentFlag, sm.ReplaceIfPresentFlag) f.Set(pdufield.SMDefaultMsgID, sm.SMDefaultMsgID) f.Set(pdufield.DataCoding, uint8(sm.Text.Type())) resp, err := t.do(p) if err != nil { return nil, err } sm.resp.Lock() sm.resp.p = resp.PDU sm.resp.Unlock() if id := resp.PDU.Header().ID; id != pdu.SubmitSMRespID { return sm, fmt.Errorf("unexpected PDU ID: %s", id) } if s := resp.PDU.Header().Status; s != 0 { return sm, s } if resp.Err != nil { return sm, resp.Err } } return sm, nil }
func TestShortMessageWindowSize(t *testing.T) { s := smpptest.NewUnstartedServer() s.Handler = func(c smpptest.Conn, p pdu.Body) { time.Sleep(200 * time.Millisecond) r := pdu.NewSubmitSMResp() r.Header().Seq = p.Header().Seq r.Fields().Set(pdufield.MessageID, "foobar") c.Write(r) } s.Start() defer s.Close() tx := &Transmitter{ Addr: s.Addr(), User: smpptest.DefaultUser, Passwd: smpptest.DefaultPasswd, WindowSize: 2, RespTimeout: time.Second, } defer tx.Close() conn := <-tx.Bind() switch conn.Status() { case Connected: default: t.Fatal(conn.Error()) } msgc := make(chan *ShortMessage, 3) defer close(msgc) errc := make(chan error, 3) for i := 0; i < 3; i++ { go func(msgc chan *ShortMessage, errc chan error) { m := <-msgc if m == nil { return } _, err := tx.Submit(m) errc <- err }(msgc, errc) msgc <- &ShortMessage{ Src: "root", Dst: "foobar", Text: pdutext.Raw("Lorem ipsum"), Validity: 10 * time.Minute, Register: NoDeliveryReceipt, } } nerr := 0 for i := 0; i < 3; i++ { if <-errc == ErrMaxWindowSize { nerr++ } } if nerr != 1 { t.Fatalf("unexpected # of errors. want 1, have %d", nerr) } }
func TestLongMessage(t *testing.T) { s := smpptest.NewUnstartedServer() s.Handler = func(c smpptest.Conn, p pdu.Body) { switch p.Header().ID { case pdu.SubmitSMID: r := pdu.NewSubmitSMResp() r.Header().Seq = p.Header().Seq r.Fields().Set(pdufield.MessageID, "foobar") c.Write(r) default: smpptest.EchoHandler(c, p) } } s.Start() defer s.Close() tx := &Transmitter{ Addr: s.Addr(), User: smpptest.DefaultUser, Passwd: smpptest.DefaultPasswd, } defer tx.Close() conn := <-tx.Bind() switch conn.Status() { case Connected: default: t.Fatal(conn.Error()) } sm, err := tx.SubmitLongMsg(&ShortMessage{ Src: "root", Dst: "foobar", Text: pdutext.Raw("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam consequat nisl enim, vel finibus neque aliquet sit amet. Interdum et malesuada fames ac ante ipsum primis in faucibus."), Validity: 10 * time.Minute, Register: NoDeliveryReceipt, }) if err != nil { t.Fatal(err) } msgid := sm.RespID() if msgid == "" { t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) } if msgid != "foobar" { t.Fatalf("unexpected msgid: want foobar, have %q", msgid) } }
func TestShortMessage(t *testing.T) { s := smpptest.NewUnstartedServer() s.Handler = func(c smpptest.Conn, p pdu.Body) { switch p.Header().ID { case pdu.SubmitSMID: r := pdu.NewSubmitSMResp() r.Header().Seq = p.Header().Seq r.Fields().Set(pdufield.MessageID, "foobar") c.Write(r) default: smpptest.EchoHandler(c, p) } } s.Start() defer s.Close() tx := &Transmitter{ Addr: s.Addr(), User: smpptest.DefaultUser, Passwd: smpptest.DefaultPasswd, } defer tx.Close() conn := <-tx.Bind() switch conn.Status() { case Connected: default: t.Fatal(conn.Error()) } sm, err := tx.Submit(&ShortMessage{ Src: "root", Dst: "foobar", Text: pdutext.Raw("Lorem ipsum"), Register: NoDeliveryReceipt, }) if err != nil { t.Fatal(err) } msgid := sm.RespID() if msgid == "" { t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) } if msgid != "foobar" { t.Fatalf("unexpected msgid: want foobar, have %q", msgid) } }
func ExampleTransceiver() { f := func(p pdu.Body) { switch p.Header().ID { case pdu.DeliverSMID: f := p.Fields() src := f[pdufield.SourceAddr] dst := f[pdufield.DestinationAddr] txt := f[pdufield.ShortMessage] log.Printf("Short message from=%q to=%q: %q", src, dst, txt) } } tx := &smpp.Transceiver{ Addr: "localhost:2775", User: "******", Passwd: "secret", Handler: f, } conn := tx.Bind() // make persistent connection. go func() { for c := range conn { log.Println("SMPP connection status:", c.Status()) } }() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { sm, err := tx.Submit(&smpp.ShortMessage{ Src: r.FormValue("src"), Dst: r.FormValue("dst"), Text: pdutext.Raw(r.FormValue("text")), Register: smpp.FinalDeliveryReceipt, }) if err == smpp.ErrNotConnected { http.Error(w, "Oops.", http.StatusServiceUnavailable) return } if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } io.WriteString(w, sm.RespID()) }) log.Fatal(http.ListenAndServe(":8080", nil)) }
func TestNotConnected(t *testing.T) { s := smpptest.NewUnstartedServer() s.Handler = func(c smpptest.Conn, p pdu.Body) { switch p.Header().ID { case pdu.SubmitSMID: r := pdu.NewSubmitSMResp() r.Header().Seq = p.Header().Seq r.Fields().Set(pdufield.MessageID, "foobar") c.Write(r) default: smpptest.EchoHandler(c, p) } } s.Start() defer s.Close() tx := &Transmitter{ Addr: s.Addr(), User: smpptest.DefaultUser, Passwd: smpptest.DefaultPasswd, } // Open connection and then close it conn := <-tx.Bind() switch conn.Status() { case Connected: default: t.Fatal(conn.Error()) } tx.Close() _, err := tx.Submit(&ShortMessage{ Src: "root", Dst: "foobar", Text: pdutext.Raw("Lorem ipsum"), Validity: 10 * time.Minute, Register: NoDeliveryReceipt, }) if err != ErrNotConnected { t.Fatalf("Error should be not connect, got %s", err.Error()) } }
func (rpc *SM) submit(req url.Values) (resp *ShortMessageResp, status int, err error) { sm := &smpp.ShortMessage{} var msg, enc, register string f := form{ {"src", "number of sender", false, nil, &sm.Src}, {"dst", "number of recipient", true, nil, &sm.Dst}, {"text", "text message", true, nil, &msg}, {"enc", "text encoding", false, []string{"latin1", "ucs2"}, &enc}, {"register", "registered delivery", false, []string{"final", "failure"}, ®ister}, } if err := f.Validate(req); err != nil { return nil, http.StatusBadRequest, err } switch enc { case "": sm.Text = pdutext.Raw(msg) case "latin1", "latin-1": sm.Text = pdutext.Latin1(msg) case "ucs2", "ucs-2": sm.Text = pdutext.UCS2(msg) } switch register { case "final": sm.Register = smpp.FinalDeliveryReceipt case "failure": sm.Register = smpp.FailureDeliveryReceipt } sm, err = rpc.tx.Submit(sm) if err == smpp.ErrNotConnected { return nil, http.StatusServiceUnavailable, err } if err != nil { return nil, http.StatusBadGateway, err } resp = &ShortMessageResp{MessageID: sm.RespID()} return resp, http.StatusOK, nil }
sender := c.Args()[0] recipient := c.Args()[1] text := strings.Join(c.Args()[2:], " ") log.Printf("Command: send %q %q %q", sender, recipient, text) var register smpp.DeliverySetting if c.Bool("register") { register = smpp.FinalDeliveryReceipt } var codec pdutext.Codec switch c.String("encoding") { case "ucs2", "ucs-2": codec = pdutext.UCS2(text) case "latin1", "latin-1": codec = pdutext.Latin1(text) default: codec = pdutext.Raw(text) } sm, err := tx.Submit(&smpp.ShortMessage{ Src: sender, Dst: recipient, Text: codec, Register: register, ServiceType: c.String("service-type"), SourceAddrTON: uint8(c.Int("source-addr-ton")), SourceAddrNPI: uint8(c.Int("source-addr-npi")), DestAddrTON: uint8(c.Int("dest-addr-ton")), DestAddrNPI: uint8(c.Int("dest-addr-npi")), ESMClass: uint8(c.Int("esm-class")), ProtocolID: uint8(c.Int("protocol-id")), PriorityFlag: uint8(c.Int("priority-flag")), ScheduleDeliveryTime: c.String("schedule-delivery-time"),
func TestSubmitMulti(t *testing.T) { //construct a byte array with the UnsuccessSme var bArray []byte bArray = append(bArray, byte(0x00)) // TON bArray = append(bArray, byte(0x00)) // NPI bArray = append(bArray, []byte("123")...) // Address bArray = append(bArray, byte(0x00)) // Error bArray = append(bArray, byte(0x00)) // Error bArray = append(bArray, byte(0x00)) // Error bArray = append(bArray, byte(0x11)) // Error bArray = append(bArray, byte(0x00)) // null terminator s := smpptest.NewUnstartedServer() s.Handler = func(c smpptest.Conn, p pdu.Body) { switch p.Header().ID { case pdu.SubmitMultiID: r := pdu.NewSubmitMultiResp() r.Header().Seq = p.Header().Seq r.Fields().Set(pdufield.MessageID, "foobar") r.Fields().Set(pdufield.NoUnsuccess, uint8(1)) r.Fields().Set(pdufield.UnsuccessSme, bArray) c.Write(r) default: smpptest.EchoHandler(c, p) } } s.Start() defer s.Close() tx := &Transmitter{ Addr: s.Addr(), User: smpptest.DefaultUser, Passwd: smpptest.DefaultPasswd, } defer tx.Close() conn := <-tx.Bind() switch conn.Status() { case Connected: default: t.Fatal(conn.Error()) } sm, err := tx.Submit(&ShortMessage{ Src: "root", DstList: []string{"123", "2233", "32322", "4234234"}, DLs: []string{"DistributionList1"}, Text: pdutext.Raw("Lorem ipsum"), Validity: 10 * time.Minute, Register: NoDeliveryReceipt, }) if err != nil { t.Fatal(err) } msgid := sm.RespID() if msgid == "" { t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) } if msgid != "foobar" { t.Fatalf("unexpected msgid: want foobar, have %q", msgid) } noUncess, _ := sm.NumbUnsuccess() if noUncess != 1 { t.Fatalf("unexpected number of unsuccess %d", noUncess) } uncessSmes, _ := sm.UnsuccessSmes() if len(uncessSmes) != 1 { t.Fatalf("unsucess sme list should have a size of 1, has %d", len(uncessSmes)) } }
func TestTransceiver(t *testing.T) { s := smpptest.NewUnstartedServer() s.Handler = func(c smpptest.Conn, p pdu.Body) { switch p.Header().ID { case pdu.SubmitSMID: r := pdu.NewSubmitSMResp() r.Header().Seq = p.Header().Seq r.Fields().Set(pdufield.MessageID, "foobar") c.Write(r) pf := p.Fields() rd := pf[pdufield.RegisteredDelivery] if rd.Bytes()[0] == 0 { return } r = pdu.NewDeliverSM() f := r.Fields() f.Set(pdufield.SourceAddr, pf[pdufield.SourceAddr]) f.Set(pdufield.DestinationAddr, pf[pdufield.DestinationAddr]) f.Set(pdufield.ShortMessage, pf[pdufield.ShortMessage]) c.Write(r) default: smpptest.EchoHandler(c, p) } } s.Start() defer s.Close() ack := make(chan error) receiver := func(p pdu.Body) { defer close(ack) if p.Header().ID != pdu.DeliverSMID { ack <- fmt.Errorf("unexpected PDU: %s", p.Header().ID) } } tc := &Transceiver{ Addr: s.Addr(), User: smpptest.DefaultUser, Passwd: smpptest.DefaultPasswd, Handler: receiver, } defer tc.Close() conn := <-tc.Bind() switch conn.Status() { case Connected: default: t.Fatal(conn.Error()) } sm, err := tc.Submit(&ShortMessage{ Src: "root", Dst: "foobar", Text: pdutext.Raw("Lorem ipsum"), Register: FinalDeliveryReceipt, }) if err != nil { t.Fatal(err) } msgid := sm.RespID() if msgid == "" { t.Fatalf("pdu does not contain msgid: %#v", sm.Resp()) } if msgid != "foobar" { t.Fatalf("unexpected msgid: want foobar, have %q", msgid) } select { case err := <-ack: if err != nil { t.Fatal(err) } case <-time.After(time.Second): t.Fatal("timeout waiting for ack") } }