// SendQfile immediately tries to send the given qfile using FreeSWITCH func SendQfile(qfilename string) (int, error) { var err error // Open qfile qf, err := OpenQfile(qfilename) if err != nil { return sendFailed, fmt.Errorf("Cannot open qfile %v: %v", qfilename, err) } defer qf.Close() var jobid uint64 if jobidstr := qf.GetFirst("jobid"); jobidstr != "" { if jobid, err = strconv.ParseUint(jobidstr, 10, 0); err != nil { logger.Logger.Println("Error parsing jobid") } } // Create FreeSWITCH Job faxjob := NewFaxJob() faxjob.Number = qf.GetFirst("number") faxjob.Cidnum = gofaxlib.Config.Gofaxsend.FaxNumber //qf.GetFirst("faxnumber") faxjob.Cidname = qf.GetFirst("sender") faxjob.Ident = gofaxlib.Config.Freeswitch.Ident faxjob.Header = gofaxlib.Config.Freeswitch.Header if desiredec := qf.GetFirst("desiredec"); desiredec != "" { if ecmMode, err := strconv.Atoi(desiredec); err == nil { faxjob.UseECM = ecmMode != 0 } } // Query DynamicConfig if dcCmd := gofaxlib.Config.Gofaxsend.DynamicConfig; dcCmd != "" { logger.Logger.Println("Calling DynamicConfig script", dcCmd) dc, err := gofaxlib.DynamicConfig(dcCmd, *deviceID, qf.GetFirst("owner"), faxjob.Number) if err != nil { errmsg := fmt.Sprintln("Error calling DynamicConfig:", err) logger.Logger.Println(errmsg) qf.Set("status", errmsg) if err = qf.Write(); err != nil { logger.Logger.Println("Error updating qfile:", err) } return sendFailed, errors.New(errmsg) } // Check if call should be rejected if gofaxlib.DynamicConfigBool(dc.GetFirst("RejectCall")) { errmsg := "Transmission rejected by DynamicConfig" logger.Logger.Println(errmsg) qf.Set("status", errmsg) if err = qf.Write(); err != nil { logger.Logger.Println("Error updating qfile:", err) } return sendFailed, errors.New(errmsg) } // Check if a custom identifier should be set if dynamicTsi := dc.GetFirst("LocalIdentifier"); dynamicTsi != "" { faxjob.Ident = dynamicTsi } if tagline := dc.GetFirst("TagLine"); tagline != "" { faxjob.Header = tagline } if faxnumber := dc.GetFirst("FAXNumber"); faxnumber != "" { faxjob.Cidnum = faxnumber } } // Start session sessionlog, err := gofaxlib.NewSessionLogger() if err != nil { return sendFailed, err } qf.Set("commid", sessionlog.CommID()) logger.Logger.Println("Logging events for commid", sessionlog.CommID(), "to", sessionlog.Logfile()) sessionlog.Log(fmt.Sprintf("Processing HylaFAX Job %d as %v", jobid, faxjob.UUID)) // Add TIFFs from queue file faxparts := qf.GetAll("fax") if len(faxparts) == 0 { return sendFailed, errors.New("No fax file(s) found in qfile") } faxfile := FaxFile{} for _, fileentry := range faxparts { err := faxfile.AddItem(fileentry) if err != nil { return sendFailed, err } } // Merge TIFFs faxjob.Filename = filepath.Join(os.TempDir(), "gofaxsend_"+faxjob.UUID.String()+".tif") defer os.Remove(faxjob.Filename) if err := faxfile.WriteTo(faxjob.Filename); err != nil { return sendFailed, err } // Total attempted calls totdials, err := strconv.Atoi(qf.GetFirst("totdials")) if err != nil { totdials = 0 } // Consecutive failed attempts to place a call ndials, err := strconv.Atoi(qf.GetFirst("ndials")) if err != nil { ndials = 0 } // Total answered calls tottries, err := strconv.Atoi(qf.GetFirst("tottries")) if err != nil { tottries = 0 } // Send job qf.Set("status", "Dialing") totdials++ qf.Set("totdials", strconv.Itoa(totdials)) if err = qf.Write(); err != nil { sessionlog.Log("Error updating qfile:", err) } t := transmit(*faxjob, sessionlog) var result *gofaxlib.FaxResult // Wait for events returned := sendRetry done := false var faxerr FaxError for { select { case page := <-t.PageSent(): // Update qfile qf.Set("npages", strconv.Itoa(int(page.Page))) qf.Set("dataformat", page.EncodingName) case result = <-t.Result(): qf.Set("signalrate", strconv.Itoa(int(result.TransferRate))) qf.Set("csi", result.RemoteID) if result.Hangupcause != "" { // Fax Finished done = true qf.Set("status", result.ResultText) if result.Success { qf.Set("returned", strconv.Itoa(sendDone)) returned = sendDone sessionlog.Log(fmt.Sprintf("Success: %v, Hangup Cause: %v, Result: %v", result.Success, result.Hangupcause, result.ResultText)) } } else { // Negotiation finished negstatus := fmt.Sprint("Sending ", result.TransferRate) if result.Ecm { negstatus = negstatus + "/ECM" } qf.Set("status", negstatus) tottries++ qf.Set("tottries", strconv.Itoa(tottries)) ndials = 0 qf.Set("ndials", strconv.Itoa(ndials)) } case faxerr = <-t.Errors(): done = true ndials++ qf.Set("ndials", strconv.Itoa(ndials)) qf.Set("status", faxerr.Error()) if faxerr.Retry() { returned = sendRetry } else { returned = sendFailed } } if err = qf.Write(); err != nil { sessionlog.Log("Error updating qfile:", err) } if done { break } } if result != nil { xfl := gofaxlib.NewXFRecord(result) xfl.Modem = *deviceID xfl.Jobid = uint(jobid) xfl.Jobtag = qf.GetFirst("jobtag") xfl.Sender = qf.GetFirst("mailaddr") xfl.Destnum = faxjob.Number xfl.Owner = qf.GetFirst("owner") if err = xfl.SaveTransmissionReport(); err != nil { sessionlog.Log(err) } } return returned, faxerr }
// 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 }