Example #1
0
// 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
}
Example #2
0
// 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
}