// Connect to FreeSWITCH and originate a txfax func (t *transmission) start() { if t.faxjob.Number == "" { t.errorChan <- NewFaxError("Number to dial is empty", false) return } if _, err := os.Stat(t.faxjob.Filename); err != nil { t.errorChan <- NewFaxError(err.Error(), false) return } var err error t.conn, err = eventsocket.Dial(gofaxlib.Config.Freeswitch.Socket, gofaxlib.Config.Freeswitch.Password) if err != nil { t.errorChan <- NewFaxError(err.Error(), true) return } defer t.conn.Close() // Enable event filter and events _, err = t.conn.Send(fmt.Sprintf("filter Unique-ID %v", t.faxjob.UUID)) if err != nil { t.errorChan <- NewFaxError(err.Error(), true) return } _, err = t.conn.Send("event plain CHANNEL_CALLSTATE CUSTOM spandsp::txfaxnegociateresult spandsp::txfaxpageresult spandsp::txfaxresult") if err != nil { t.errorChan <- NewFaxError(err.Error(), true) return } // Check if T.38 should be disabled disableT38 := gofaxlib.Config.Freeswitch.DisableT38 if disableT38 { t.sessionlog.Log("T.38 disabled by configuration") } else { disableT38, err = gofaxlib.GetSoftmodemFallback(t.conn, t.faxjob.Number) if err != nil { t.sessionlog.Log(err) disableT38 = false } if disableT38 { t.sessionlog.Log(fmt.Sprintf("Softmodem fallback active for destination %s, disabling T.38", t.faxjob.Number)) } } // Assemble dialstring dsVariablesMap := map[string]string{ "ignore_early_media": "true", "origination_uuid": t.faxjob.UUID.String(), "origination_caller_id_number": t.faxjob.Cidnum, "origination_caller_id_name": t.faxjob.Cidname, "fax_ident": t.faxjob.Ident, "fax_header": t.faxjob.Header, "fax_use_ecm": strconv.FormatBool(t.faxjob.UseECM), "fax_verbose": strconv.FormatBool(gofaxlib.Config.Freeswitch.Verbose), } if disableT38 { dsVariablesMap["fax_enable_t38"] = "false" } else { dsVariablesMap["fax_enable_t38"] = "true" } dsVariablesPairs := make([]string, len(dsVariablesMap)) i := 0 for k, v := range dsVariablesMap { dsVariablesPairs[i] = fmt.Sprintf("%v='%v'", k, v) i++ } dsVariables := strings.Join(dsVariablesPairs, ",") // Try gateways in configured order dsGatewaysStrings := make([]string, len(gofaxlib.Config.Freeswitch.Gateway)) for i, gw := range gofaxlib.Config.Freeswitch.Gateway { dsGatewaysStrings[i] = fmt.Sprintf("sofia/gateway/%v/%v", gw, t.faxjob.Number) } dsGateways := strings.Join(dsGatewaysStrings, "|") dialstring := fmt.Sprintf("{%v}%v", dsVariables, dsGateways) //t.sessionlog.Log(fmt.Sprintf("%v Dialstring: %v", faxjob.UUID, dialstring)) // Originate call t.sessionlog.Log("Originating channel to", t.faxjob.Number) _, err = t.conn.Send(fmt.Sprintf("api originate %v, &txfax(%v)", dialstring, t.faxjob.Filename)) if err != nil { t.conn.Send(fmt.Sprintf("uuid_dump %v", t.faxjob.UUID)) hangupcause := strings.TrimSpace(err.Error()) t.sessionlog.Log("Originate failed with hangup cause", hangupcause) t.errorChan <- NewFaxError(hangupcause, true) return } t.sessionlog.Log("Originate successful") result := gofaxlib.NewFaxResult(t.faxjob.UUID, t.sessionlog) es := gofaxlib.NewEventStream(t.conn) var pages uint // Listen for system signals to be able to kill the channel sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT) for { select { case ev := <-es.Events(): result.AddEvent(ev) if result.Hangupcause != "" { // If transmission failed: // Check if softmodem fallback should be enabled on the next call if gofaxlib.Config.Freeswitch.SoftmodemFallback && !result.Success { var activateFallback bool if result.NegotiateCount > 1 { // Activate fallback if negotiation was repeated t.sessionlog.Log(fmt.Sprintf("Fax failed with %d negotiations, enabling softmodem fallback for calls from/to %s.", result.NegotiateCount, t.faxjob.Number)) activateFallback = true } else { var badrows uint for _, p := range result.PageResults { badrows += p.BadRows } if badrows > 0 { // Activate fallback if any bad rows were present t.sessionlog.Log(fmt.Sprintf("Fax failed with %d bad rows in %d pages, enabling softmodem fallback for calls from/to %s.", badrows, result.TransferredPages, t.faxjob.Number)) activateFallback = true } } if activateFallback { err = gofaxlib.SetSoftmodemFallback(t.conn, t.faxjob.Number, true) if err != nil { t.sessionlog.Log(err) } } } t.resultChan <- result return } if ev.Get("Event-Subclass") == "spandsp::txfaxnegociateresult" { t.resultChan <- result } else if result.TransferredPages != pages { pages = result.TransferredPages t.pageChan <- &result.PageResults[pages-1] } case err := <-es.Errors(): t.errorChan <- NewFaxError(err.Error(), true) return case kill := <-sigchan: t.sessionlog.Log(fmt.Sprintf("%v Received signal %v, destroying channel", t.faxjob.UUID, kill)) t.conn.Send(fmt.Sprintf("api uuid_kill %v", t.faxjob.UUID)) os.Remove(t.faxjob.Filename) t.errorChan <- NewFaxError(fmt.Sprintf("Killed by signal %v", kill), false) } } }
// Handle incoming call func (e *EventSocketServer) handler(c *eventsocket.Connection) { logger.Logger.Println("Incoming Event Socket connection from", c.RemoteAddr()) connectev, err := c.Send("connect") // Returns: Ganzer Event mit alles if err != nil { c.Send("exit") logger.Logger.Print(err) return } channelUUID := uuid.Parse(connectev.Get("Unique-Id")) if channelUUID == nil { c.Send("exit") logger.Logger.Print(err) return } defer logger.Logger.Println(channelUUID, "Handler ending") // Filter and subscribe to events c.Send("linger") c.Send(fmt.Sprintf("filter Unique-ID %v", channelUUID)) c.Send("event plain CHANNEL_CALLSTATE CUSTOM spandsp::rxfaxnegociateresult spandsp::rxfaxpageresult spandsp::rxfaxresult") // Extract Caller/Callee recipient := connectev.Get("Variable_sip_to_user") cidname := connectev.Get("Channel-Caller-Id-Name") cidnum := connectev.Get("Channel-Caller-Id-Number") logger.Logger.Printf("Incoming call to %v from %v <%v>", recipient, cidname, cidnum) var device *Device if gofaxlib.Config.Gofaxd.AllocateInboundDevices { // Find free device device, err := devmanager.FindDevice(fmt.Sprintf("Receiving facsimile")) if err != nil { logger.Logger.Println(err) c.Execute("respond", "404", true) c.Send("exit") return } defer device.SetReady() } var usedDevice string if device != nil { usedDevice = device.Name } else { usedDevice = defaultDevice } csi := gofaxlib.Config.Freeswitch.Ident // Query DynamicConfig if dcCmd := gofaxlib.Config.Gofaxd.DynamicConfig; dcCmd != "" { logger.Logger.Println("Calling DynamicConfig script", dcCmd) dc, err := gofaxlib.DynamicConfig(dcCmd, usedDevice, cidnum, cidname, recipient) if err != nil { logger.Logger.Println("Error calling DynamicConfig:", err) } else { // Check if call should be rejected if gofaxlib.DynamicConfigBool(dc.GetFirst("RejectCall")) { logger.Logger.Println("DynamicConfig decided to reject this call") c.Execute("respond", "404", true) c.Send("exit") return } // Check if a custom identifier should be set if dynamicCsi := dc.GetFirst("LocalIdentifier"); dynamicCsi != "" { csi = dynamicCsi } } } sessionlog, err := gofaxlib.NewSessionLogger() if err != nil { c.Send("exit") logger.Logger.Print(err) return } logger.Logger.Println(channelUUID, "Logging events for commid", sessionlog.CommID(), "to", sessionlog.Logfile()) sessionlog.Log("Inbound channel UUID: ", channelUUID) // Check if T.38 should be disabled disableT38 := gofaxlib.Config.Freeswitch.DisableT38 if disableT38 { sessionlog.Log("T.38 disabled by configuration") } else { disableT38, err = gofaxlib.GetSoftmodemFallback(nil, cidnum) if err != nil { sessionlog.Log(err) disableT38 = false } if disableT38 { sessionlog.Log(fmt.Sprintf("Softmodem fallback active for caller %s, disabling T.38", cidnum)) } } sessionlog.Log(fmt.Sprintf("Accepting call to %v from %v <%v> with commid %v", recipient, cidname, cidnum, sessionlog.CommID())) if device != nil { // Notify faxq gofaxlib.Faxq.ModemStatus(device.Name, "I"+sessionlog.CommID()) gofaxlib.Faxq.ReceiveStatus(device.Name, "B") gofaxlib.Faxq.ReceiveStatus(device.Name, "S") defer gofaxlib.Faxq.ReceiveStatus(device.Name, "E") } // Start interacting with the caller if gofaxlib.Config.Gofaxd.Answerafter != 0 { c.Execute("ring_ready", "", true) c.Execute("sleep", strconv.FormatUint(gofaxlib.Config.Gofaxd.Answerafter, 10), true) } c.Execute("answer", "", true) if gofaxlib.Config.Gofaxd.Waittime != 0 { c.Execute("playback", "silence_stream://"+strconv.FormatUint(gofaxlib.Config.Gofaxd.Waittime, 10), true) } // Find filename in recvq to save received .tif seq, err := gofaxlib.GetSeqFor(recvqDir) if err != nil { c.Send("exit") sessionlog.Log(err) return } filename := filepath.Join(recvqDir, fmt.Sprintf(recvqFileFormat, seq)) filenameAbs := filepath.Join(gofaxlib.Config.Hylafax.Spooldir, filename) sessionlog.Log("Rxfax to", filenameAbs) if disableT38 { c.Execute("set", "fax_enable_t38=false", true) } else { c.Execute("set", "fax_enable_t38_request=true", true) c.Execute("set", "fax_enable_t38=true", true) } c.Execute("set", fmt.Sprintf("fax_ident=%s", csi), true) c.Execute("rxfax", filenameAbs, true) result := gofaxlib.NewFaxResult(channelUUID, sessionlog) es := gofaxlib.NewEventStream(c) pages := result.TransferredPages EventLoop: for { select { case ev := <-es.Events(): if ev.Get("Content-Type") == "text/disconnect-notice" { sessionlog.Log("Received disconnect message") //c.Close() //break EventLoop } else { result.AddEvent(ev) if result.Hangupcause != "" { c.Close() break EventLoop } if pages != result.TransferredPages { pages = result.TransferredPages if device != nil { gofaxlib.Faxq.ReceiveStatus(device.Name, "P") } } } case err := <-es.Errors(): if err.Error() == "EOF" { sessionlog.Log("Event socket client disconnected") } else { sessionlog.Log("Error:", err) } break EventLoop case _ = <-e.killChan: sessionlog.Log("Kill reqeust received, destroying channel") c.Send(fmt.Sprintf("api uuid_kill %v", channelUUID)) c.Close() return } } if device != nil { gofaxlib.Faxq.ReceiveStatus(device.Name, "D") } sessionlog.Log(fmt.Sprintf("Success: %v, Hangup Cause: %v, Result: %v", result.Success, result.Hangupcause, result.ResultText)) xfl := gofaxlib.NewXFRecord(result) xfl.Modem = usedDevice xfl.Filename = filename xfl.Destnum = recipient xfl.Cidnum = cidnum xfl.Cidname = cidname if err = xfl.SaveReceptionReport(); err != nil { sessionlog.Log(err) } // If reception failed: // Check if softmodem fallback should be enabled on the next call if gofaxlib.Config.Freeswitch.SoftmodemFallback && !result.Success { var activateFallback bool if result.NegotiateCount > 1 { // Activate fallback if negotiation was repeated sessionlog.Log(fmt.Sprintf("Fax failed with %d negotiations, enabling softmodem fallback for calls from/to %s.", result.NegotiateCount, cidnum)) activateFallback = true } else { var badrows uint for _, p := range result.PageResults { badrows += p.BadRows } if badrows > 0 { // Activate fallback if any bad rows were present sessionlog.Log(fmt.Sprintf("Fax failed with %d bad rows in %d pages, enabling softmodem fallback for calls from/to %s.", badrows, result.TransferredPages, cidnum)) activateFallback = true } } if activateFallback { err = gofaxlib.SetSoftmodemFallback(nil, cidnum, true) if err != nil { sessionlog.Log(err) } } } // Process received file rcvdcmd := gofaxlib.Config.Gofaxd.FaxRcvdCmd if rcvdcmd == "" { rcvdcmd = defaultFaxrcvdCmd } errmsg := "" if !result.Success { errmsg = result.ResultText } cmd := exec.Command(rcvdcmd, filename, usedDevice, sessionlog.CommID(), errmsg, cidnum, cidname, recipient) sessionlog.Log("Calling", cmd.Path, cmd.Args) if output, err := cmd.CombinedOutput(); err != nil { sessionlog.Log(cmd.Path, "ended with", err) sessionlog.Log(output) } else { sessionlog.Log(cmd.Path, "ended successfully") } return }