diff --git a/cdrs/cdrs.go b/cdrs/cdrs.go index 7cb0688a1..550174c24 100644 --- a/cdrs/cdrs.go +++ b/cdrs/cdrs.go @@ -45,16 +45,15 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) { } }() } else { - engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %v", err)) + engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %s", err.Error())) } } func cgrCdrHandler(w http.ResponseWriter, r *http.Request) { - body, _ := ioutil.ReadAll(r.Body) - if genCdr, err := new(GenCdr).New(body); err == nil { - storage.SetCdr(genCdr) + if cgrCdr, err := NewCgrCdrFromHttpReq(r); err == nil { + storage.SetCdr(cgrCdr) if cfg.CDRSMediator == "internal" { - errMedi := medi.MediateDBCDR(genCdr, storage) + errMedi := medi.MediateDBCDR(cgrCdr, storage) if errMedi != nil { engine.Logger.Err(fmt.Sprintf("Could not run mediation on CDR: %s", errMedi.Error())) } @@ -62,7 +61,7 @@ func cgrCdrHandler(w http.ResponseWriter, r *http.Request) { //TODO: use the connection to mediator } } else { - engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %v", err)) + engine.Logger.Err(fmt.Sprintf("Could not create CDR entry: %s", err.Error())) } } @@ -76,7 +75,7 @@ func New(s engine.CdrStorage, m *mediator.Mediator, c *config.CGRConfig) *CDRS { } func (cdrs *CDRS) StartCapturingCDRs() { - http.HandleFunc("/cgr_json", cgrCdrHandler) // Attach CGR CDR Handler + http.HandleFunc("/cgr", cgrCdrHandler) // Attach CGR CDR Handler http.HandleFunc("/freeswitch_json", fsCdrHandler) // Attach FreeSWITCH JSON CDR Handler http.ListenAndServe(cfg.CDRSListen, nil) } diff --git a/cdrs/cgrcdr.go b/cdrs/cgrcdr.go new file mode 100644 index 000000000..f1042b002 --- /dev/null +++ b/cdrs/cgrcdr.go @@ -0,0 +1,121 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2013 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package cdrs + +import ( + "github.com/cgrates/cgrates/utils" + "strconv" + "time" + "net/http" +) + +const ( + ACCID = "accid" + CDRHOST = "cdrhost" + REQTYPE = "reqtype" + DIRECTION = "direction" + TENANT = "tenant" + TOR = "tor" + ACCOUNT = "account" + SUBJECT = "subject" + DESTINATION = "destination" + TIME_ANSWER = "time_answer" + DURATION = "duration" +) + +var primaryFields []string = []string{ACCID, CDRHOST, REQTYPE, DIRECTION, TENANT, TOR, ACCOUNT, SUBJECT, DESTINATION, TIME_ANSWER, DURATION} + +func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) { + if req.Form == nil { + if err := req.ParseForm(); err != nil { + return nil, err + } + } + cgrCdr := make(CgrCdr) + for k, vals := range req.Form { + cgrCdr[k] = vals[0] // We only support the first value for now, if more are provided it is considered remote's fault + } + return cgrCdr, nil +} + + +type CgrCdr map[string]string + +func (cgrCdr CgrCdr) GetCgrId() string { + return utils.FSCgrId(cgrCdr[ACCID]) +} + +func (cgrCdr CgrCdr) GetAccId() string { + return cgrCdr[ACCID] +} + +func (cgrCdr CgrCdr) GetCdrHost() string { + return cgrCdr[CDRHOST] +} +func (cgrCdr CgrCdr) GetDirection() string { + //TODO: implement direction + return "*out" +} +func (cgrCdr CgrCdr) GetOrigId() string { + return cgrCdr[CDRHOST] +} +func (cgrCdr CgrCdr) GetSubject() string { + return cgrCdr[SUBJECT] +} +func (cgrCdr CgrCdr) GetAccount() string { + return cgrCdr[ACCOUNT] +} + +// Charging destination number +func (cgrCdr CgrCdr) GetDestination() string { + return cgrCdr[DESTINATION] +} + +func (cgrCdr CgrCdr) GetTOR() string { + return cgrCdr[TOR] +} + +func (cgrCdr CgrCdr) GetTenant() string { + return cgrCdr[TENANT] +} +func (cgrCdr CgrCdr) GetReqType() string { + return cgrCdr[REQTYPE] +} +func (cgrCdr CgrCdr) GetExtraFields() map[string]string { + extraFields := make(map[string]string) + for k, v := range cgrCdr { + if !utils.IsSliceMember(primaryFields, k) { + extraFields[k] = v + } + } + return extraFields +} +func (cgrCdr CgrCdr) GetFallbackSubj() string { + return cfg.DefaultSubject +} +func (cgrCdr CgrCdr) GetAnswerTime() (t time.Time, err error) { + st, err := strconv.ParseInt(cgrCdr[TIME_ANSWER], 0, 64) + t = time.Unix(st, 0) + return +} +// Extracts duration as considered by the telecom switch +func (cgrCdr CgrCdr) GetDuration() int64 { + dur, _ := strconv.ParseInt(cgrCdr[DURATION], 0, 64) + return dur +} diff --git a/cdrs/cgrcdr_test.go b/cdrs/cgrcdr_test.go new file mode 100644 index 000000000..5b25cee08 --- /dev/null +++ b/cdrs/cgrcdr_test.go @@ -0,0 +1,85 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2013 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package cdrs + +import ( + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/cgrates/config" + "testing" + "time" +) + +/* +curl --data "accid=asbfdsaf&cdrhost=192.168.1.1&reqtype=rated&direction=*out&tenant=cgrates.org&tor=call&account=1001&subject=1001&destination=1002&time_answer=1383813746&duration=10&field_extr1=val_extr1&fieldextr2=valextr2" http://ipbxdev:2022/cgr +*/ + +func TestCgrCdrFields(t *testing.T) { + cfg, _ = config.NewDefaultCGRConfig() + cgrCdr := CgrCdr{ "accid":"dsafdsaf", "cdrhost":"192.168.1.1", "reqtype":"rated", "direction":"*out", "tenant":"cgrates.org", "tor":"call", + "account":"1001", "subject":"1001", "destination":"1002", "time_answer":"1383813746", "duration":"10", "field_extr1":"val_extr1", "fieldextr2":"valextr2"} + if cgrCdr.GetCgrId() != utils.FSCgrId("dsafdsaf") { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetAccId() != "dsafdsaf" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetCdrHost() != "192.168.1.1" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetDirection() != "*out" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetSubject() != "1001" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetAccount() != "1001" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetDestination() != "1002" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetTOR() != "call" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetTenant() != "cgrates.org" { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetReqType() != utils.RATED { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetFallbackSubj() != "0" { + t.Error("Error parsing cdr: ", cgrCdr) + } + answerTime, _ := cgrCdr.GetAnswerTime() + expectedATime, _ := time.Parse(time.RFC3339, "2013-11-07T08:42:26Z") + if answerTime.UTC() != expectedATime { + t.Error("Error parsing cdr: ", cgrCdr) + } + if cgrCdr.GetDuration() != 10 { + t.Error("Error parsing cdr: ", cgrCdr) + } + extraFields := cgrCdr.GetExtraFields() + if len(extraFields) != 2 { + t.Error("Error parsing extra fields: ", extraFields) + } + if extraFields["field_extr1"] != "val_extr1" { + t.Error("Error parsing extra fields: ", extraFields) + } + +} diff --git a/cdrs/gencdr.go b/cdrs/gencdr.go deleted file mode 100644 index 8eb7ec0ae..000000000 --- a/cdrs/gencdr.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package cdrs - -import ( - "encoding/json" - "errors" - "github.com/cgrates/cgrates/utils" - "strconv" - "time" -) - -const ( - CDR_MAP = "variables" - DIRECTION = "direction" - ORIG_ID = "id" - SUBJECT = "subject" - ACCOUNT = "account" - DESTINATION = "destination" - REQTYPE = "reqtype" //prepaid or postpaid - TOR = "tor" - UUID = "uuid" // -Unique ID for this call leg - CSTMID = "tenant" - CALL_DEST_NR = "dialed_extension" - PARK_TIME = "start_epoch" - ANSWER_TIME = "time_answer" - HANGUP_TIME = "time_hangup" - DURATION = "duration" - USERNAME = "user_name" - IP = "sip_local_network_addr" -) - -type GenCdr map[string]string - -func (genCdr GenCdr) New(body []byte) (utils.CDR, error) { - genCdr = make(map[string]string) - var tmp map[string]interface{} - var err error - if err = json.Unmarshal(body, &tmp); err == nil { - if variables, ok := tmp[CDR_MAP]; ok { - if variables, ok := variables.(map[string]interface{}); ok { - for k, v := range variables { - genCdr[k] = v.(string) - } - } - return genCdr, nil - } - } - return nil, err -} - -func (genCdr GenCdr) GetCgrId() string { - return utils.FSCgrId(genCdr[UUID]) -} -func (genCdr GenCdr) GetAccId() string { - return genCdr[UUID] -} -func (genCdr GenCdr) GetCdrHost() string { - return genCdr[FS_IP] -} -func (genCdr GenCdr) GetDirection() string { - //TODO: implement direction - return "*out" -} -func (genCdr GenCdr) GetOrigId() string { - return genCdr[ORIG_ID] -} -func (genCdr GenCdr) GetSubject() string { - return utils.FirstNonEmpty(genCdr[SUBJECT], genCdr[USERNAME]) -} -func (genCdr GenCdr) GetAccount() string { - return utils.FirstNonEmpty(genCdr[ACCOUNT], genCdr[USERNAME]) -} - -// Charging destination number -func (genCdr GenCdr) GetDestination() string { - return utils.FirstNonEmpty(genCdr[DESTINATION], genCdr[CALL_DEST_NR]) -} - -func (genCdr GenCdr) GetTOR() string { - return utils.FirstNonEmpty(genCdr[TOR], cfg.DefaultTOR) -} - -func (genCdr GenCdr) GetTenant() string { - return utils.FirstNonEmpty(genCdr[CSTMID], cfg.DefaultTenant) -} -func (genCdr GenCdr) GetReqType() string { - return utils.FirstNonEmpty(genCdr[REQTYPE], cfg.DefaultReqType) -} -func (genCdr GenCdr) GetExtraFields() map[string]string { - extraFields := make(map[string]string, len(cfg.CDRSExtraFields)) - for _, field := range cfg.CDRSExtraFields { - extraFields[field] = genCdr[field] - } - return extraFields -} -func (genCdr GenCdr) GetFallbackSubj() string { - return cfg.DefaultSubject -} -func (genCdr GenCdr) GetAnswerTime() (t time.Time, err error) { - st, err := strconv.ParseInt(genCdr[ANSWER_TIME], 0, 64) - t = time.Unix(0, st*1000) - return -} -func (genCdr GenCdr) GetHangupTime() (t time.Time, err error) { - st, err := strconv.ParseInt(genCdr[HANGUP_TIME], 0, 64) - t = time.Unix(0, st*1000) - return -} - -// Extracts duration as considered by the telecom switch -func (genCdr GenCdr) GetDuration() int64 { - dur, _ := strconv.ParseInt(genCdr[DURATION], 0, 64) - return dur -} - -func (genCdr GenCdr) Store() (result string, err error) { - result += genCdr.GetCgrId() + "|" - result += genCdr.GetAccId() + "|" - result += genCdr.GetCdrHost() + "|" - result += genCdr.GetDirection() + "|" - result += genCdr.GetOrigId() + "|" - result += genCdr.GetSubject() + "|" - result += genCdr.GetAccount() + "|" - result += genCdr.GetDestination() + "|" - result += genCdr.GetTOR() + "|" - result += genCdr.GetAccId() + "|" - result += genCdr.GetTenant() + "|" - result += genCdr.GetReqType() + "|" - st, err := genCdr.GetAnswerTime() - if err != nil { - return "", err - } - result += strconv.FormatInt(st.UnixNano(), 10) + "|" - et, err := genCdr.GetHangupTime() - if err != nil { - return "", err - } - result += strconv.FormatInt(et.UnixNano(), 10) + "|" - result += strconv.FormatInt(genCdr.GetDuration(), 10) + "|" - result += genCdr.GetFallbackSubj() + "|" - return -} - -func (genCdr GenCdr) Restore(input string) error { - return errors.New("Not implemented") -} diff --git a/utils/cdr.go b/utils/cdr.go index fe75b53d0..b8dbff990 100644 --- a/utils/cdr.go +++ b/utils/cdr.go @@ -39,51 +39,3 @@ type CDR interface { GetFallbackSubj() string GetExtraFields() map[string]string //Stores extra CDR Fields } - -type GenericCdr map[string]string - -func (gcdr GenericCdr) GetCgrId() string { - return "" -} -func (gcdr GenericCdr) GetAccId() string { - return "" -} -func (gcdr GenericCdr) GetCdrHost() string { - return "" -} -func (gcdr GenericCdr) GetDirection() string { - return "" -} -func (gcdr GenericCdr) GetOrigId() string { - return "" -} -func (gcdr GenericCdr) GetSubject() string { - return "" -} -func (gcdr GenericCdr) GetAccount() string { - return "" -} -func (gcdr GenericCdr) GetDestination() string { - return "" -} -func (gcdr GenericCdr) GetTOR() string { - return "" -} -func (gcdr GenericCdr) GetTenant() string { - return "" -} -func (gcdr GenericCdr) GetReqType() string { - return "" -} -func (gcdr GenericCdr) GetAnswerTime() (time.Time, error) { - return time.Now(), nil -} -func (gcdr GenericCdr) GetDuration() int64 { - return 0.0 -} -func (gcdr GenericCdr) GetFallbackSubj() string { - return "" -} -func (gcdr GenericCdr) GetExtraFields() map[string]string { - return nil -}