From 2a7a82094d50b75380b9858eae5eb41ed169378d Mon Sep 17 00:00:00 2001 From: Martin Szulecki Date: Thu, 19 Mar 2009 22:52:58 +0100 Subject: Initial import of gfax 0.7.7 --- src/efax.cs | 605 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 605 insertions(+) create mode 100755 src/efax.cs (limited to 'src/efax.cs') diff --git a/src/efax.cs b/src/efax.cs new file mode 100755 index 0000000..d9df4dc --- /dev/null +++ b/src/efax.cs @@ -0,0 +1,605 @@ +// GFAX - Gnome fax application +// Copyright (C) 2003 - 2008 George A. Farris +// +// 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 Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + // Sequence to send a file is: + // 1) Make sure we're connected + // 2) Store the filename on the server [storefile_open] + // 3) Send the file line by line + // 4) Close the stream for sending the file + // 5) For all the phone numbers do: + // a) job_new [job_new] + // b) set all job parms [job_parm_set] + // c) submit the job [submit_job] + // 6) Close the connection. + +// Properties +// string Hostname, Username, Password +// int IPPort + +//#define DEBUGEFAX + +namespace gfax { + using Gtk; + using Mono.Unix; + using System; + using System.IO; + using System.Text; //Encoding.ASCII.GetBytes + using System.Collections; + using System.Net; + using System.Threading; + using System.Net.Sockets; + using System.Diagnostics; + + + public class Efax + { + private static System.Threading.Thread thread; + string user; + + + //Speaker Volume + string[] speakerVolume = {"L0","L1","L2","L3"}; + const int VERY_LOW = 0; + const int LOW = 1; + const int MEDIUM = 2; + const int HIGH = 3; + + // Speaker modes + string[] speakerMode = {"M0","M1","M2","M3"}; + const int NEVER = 0; + const int UNTIL_CARRIER = 1; + const int ALWAYS_ON = 2; + const int ON_RECEIVE_ONLY = 3; + + string[] modemType = {@"-j\Q4",@"-j\Q1",@"-j*F1",@"-j&H2&I0&R1&D3I4",@"-or"}; + + + /* + # FCINIT='-j\Q4' # AT&T (Dataport, Paradyne) + # FCINIT='-j\Q1' # Motorola (Power Modem, 3400 Pro,...) + # FCINIT='-j*F1' # QuickComm (Spirit II) + # FCINIT='-j&H2&I0&R1&D3I4' # USR (Courier, Sportster) + # FCINIT='-or' # Multi-Tech (for bit reversal) + */ + + // Don't need this anymore + //TextWriter statusfile = null; + + public Efax () + { + + // Set initial modem settings, gconfsharp-schemagen doesn't like "&" + // so can't set a default in gfax.schemas + if ( Settings.EfaxModemInit == "" ) + Settings.EfaxModemInit = "-iZ -i&FE0&D2S7=120 -i&C0"; + if ( Settings.EfaxModemFcinit == "" ) + Settings.EfaxModemFcinit = "-j&H2&I0&R1&D3I4"; + if ( Settings.EfaxModemReset == "" ) + Settings.EfaxModemReset = "-kZ"; + + //Don't need this anymore + //statusfile = TextWriter.Synchronized(File.CreateText(gfax.Procfile)); + } + + public void close () + { + } + + // Method status(queue) + // + // queue is the queue fax system it can be one of: + // 'sendq', 'doneq' or 'recvq' + // + // Return a string containing lines formatted like so + // "jobid=number=status=owner=pages=dials=error=sendat\n" + // such as: + // "2=5551212=S=george=2=1=error message=2004/03/09 18.01.51\n" + public string status (string queue) + { + StreamReader infile = null; + string[] sts = new string[11]; + string path; + string buf; + + + if (queue == "sendq") + path = gfax.SpoolDirectory; + else if (queue == "doneq") + path = gfax.SpoolDirectory + "/doneq"; + else + path = gfax.SpoolDirectory + "/recq"; + + string[] control_files = Directory.GetFiles(path, "C_*"); + string[] lines = new string[control_files.Length]; + int num_lines = 0; + + foreach (string s in control_files) { + try { + infile = File.OpenText(s); + + for (int i=0; (buf = infile.ReadLine()) != null; i++) { + string[] sa = buf.Split('='); + if ( sa[1].Length != 0 ) + sts[i] = sa[1]; + else + sts[i] = "-"; + } + infile.Close(); + // Purge doneq files if older than 5 days. + if ( queue == "doneq" ) + if ( (DateTime.Now).Subtract(File.GetCreationTime(s)).Days > 5 ) + File.Delete(s); + + lines[num_lines++] = String.Format("{0}={1}={2}={3}={4}={5}={6}={7}\n", + sts[0],sts[2],sts[3],sts[4],sts[5],sts[6],sts[10],sts[7]); + } + catch (Exception e) { + //return; + } + } + + return String.Concat(lines); + } + + // send_init (string filename) + // + // Here we should convert the file with ghostscript and return a directory + // that points to the converted files to send, one per page. There should + // be some status messages passed to the user to let them know whats going on. + // Maybe a dialog box. + // + // We also setup the status message system. + public string send_init (string fname) + { + StreamReader fp = null; + double lines = 0; + string resolution = "204x98"; // normal res + + // Get new directory name and make it + Random rand = new Random(); + string rand_file = rand.Next().ToString(); + string dir_name = String.Format("{0}/D_{1}",gfax.SpoolDirectory, rand_file); + // TODO proper checks here, this is nasty + Directory.CreateDirectory(dir_name); + + + // get the fax options + if (gfax.sendWizardResolution) { + resolution = "204x196"; + }else { + if (Settings.HiResolution) + resolution = "204x196"; + } + + string papersize = Settings.EfaxPapersize; + + + //figure out how many lines in the file for progress bar + // TODO progress bar and error + try { fp = File.OpenText(fname); } + catch (Exception e) { } + + while ( (fp.ReadLine()) != null ) { + lines = lines + 1; + } + fp.Close(); + + + try { fp = File.OpenText(fname); } + catch (Exception e) { } + + if (Settings.Faxtracing == true) { + Console.WriteLine("[Efax.send_init] File :{0} is open and has {1} lines", fname,lines); + } + + fp.Close(); + + + // TODO need random temp file name + if (Settings.Faxtracing == true) { + Console.WriteLine("[Efax.send_init] Converting file with gs"); + Console.WriteLine(" Filename -> {0}",fname); + Console.WriteLine(" Resolution -> {0}",resolution); + Console.WriteLine(" Directory -> {0}",dir_name); + Console.WriteLine(" Papersize -> {0}\n",papersize); + } + + ProcessStartInfo pidInfo = new ProcessStartInfo(); + pidInfo.FileName = "gs"; + + pidInfo.Arguments = String.Concat( + "-q -sDEVICE=tiffg3 -r", + resolution, + " -dNOPAUSE -dSAFER -dBATCH", + " -sOutputFile=", + dir_name, + "/tmp.%03d -sPAPERSIZE=", + papersize, " ",fname); + + if (Settings.Faxtracing == true) { + Console.WriteLine("[Efax.send_init]\n {0}", pidInfo.Arguments); + } + + Process pid = Process.Start(pidInfo); + pid.WaitForExit(); + + return dir_name; + } + + // Method send(string directory, contact) + // + // Sequence to send a file is: + public void send (string directory, GfaxContact contact) + { + StreamWriter outfile; + int pages; + + + string emailAddress = Settings.EmailAddress; + string emailNotify = "none"; + + // Get advanced options + if (gfax.fromSendWizard) { + if (gfax.sendWizardEmailNotify) { + emailNotify = "done"; + emailAddress = gfax.sendWizardEmailAddress; + } + } else { + if (Settings.EmailNotify) + emailNotify = "done"; + } + + //2004/03/09 18.01.51 + // Format time to send + DateTime st = DateTime.Now; + string tts = String.Format("{0}/{1:00}/{2:00} {3:00}.{4:00}.00", + st.Year, st.Month, st.Day, st.Hour, st.Minute); + + // get next jobid + if ( Settings.EfaxNextJobid > 998 ) + Settings.EfaxNextJobid = 1; + else + Settings.EfaxNextJobid++; + + // open directory and count files, that will be the number of pages. + pages = Directory.GetFiles(directory).Length; + + // build filename + Random rand = new Random(); + string rand_file = rand.Next().ToString(); + string tfname = String.Format("{0}/C_{1}.{2}",gfax.SpoolDirectory, contact.PhoneNumber, rand_file); + + // TODO error stuff + //Open new status file + try { outfile = File.CreateText(tfname); } + catch (Exception e) { return; } + outfile.Write("Jobid="); + outfile.WriteLine(Settings.EfaxNextJobid); + outfile.Write("JobDirectory="); + outfile.WriteLine(directory); + outfile.Write("PhoneNumber="); + outfile.WriteLine(contact.PhoneNumber); + outfile.Write("Status="); + outfile.WriteLine("P"); + outfile.Write("Owner="); + outfile.WriteLine(Environment.GetEnvironmentVariable("USERNAME")); + outfile.Write("Pages="); + outfile.WriteLine(pages.ToString()); + outfile.Write("Dials="); + outfile.WriteLine(0); + outfile.Write("Sendat="); + outfile.WriteLine(tts); + outfile.Write("Notification="); + outfile.WriteLine(emailNotify); + outfile.Write("Email="); + outfile.WriteLine(emailAddress); + outfile.Write("ErrorMessage="); + outfile.WriteLine(""); + outfile.Close(); + + // jobid might need to be Convert.ToInt32 + + } + + public void job_delete (string jobid) + { + StreamReader infile = null; + string[] sts = new string[11]; + string buf; + + string[] controlFiles = Directory.GetFiles(gfax.SpoolDirectory, "C_*"); + + foreach (string controlFile in controlFiles) { + try { + infile = File.OpenText(controlFile); + + for (int i=0; (buf = infile.ReadLine()) != null; i++) { + string[] sa = buf.Split('='); + if ( sa[1].Length != 0 ) + sts[i] = sa[1]; + else + sts[i] = "-"; + } + infile.Close(); + + if ( sts[0] == jobid ) { + File.Delete(controlFile); + Directory.Delete(sts[1], true); + break; + } + } + catch (Exception e) {continue; } + } + + // end up here with control file name + return; + } + + public string job_kill (string jobid) + { + return null; + } + + public void run_efaxd () + { + // Create the thread object, passing in the efaxd method + WaitCallback callback = new WaitCallback(efaxd); + ThreadPool.QueueUserWorkItem(callback); + } + + /* efaxd + * For now just cycle through the control files and send the fax + * + */ + private void efaxd (object state) + { + thread = System.Threading.Thread.CurrentThread; + + StreamReader infile = null; + string[] sts = new string[11]; + string buf; + //bool fatalError = false; + int retries; + string speakerModeVolume; + + //string jobid, number, status, owner, pages, dials, error, sendat; + System.Threading.Thread.Sleep(15000); // Don't start immediately + + while (true) { + Application.Invoke (delegate {gfax.GAppbar.ClearStack();}); + Application.Invoke (delegate {gfax.GAppbar.Push(Catalog.GetString("Scanning control files."));}); + + // while there are job files in the folder + while (Directory.GetFiles(gfax.SpoolDirectory, "C_*").Length > 0) { + + string[] controlFiles = Directory.GetFiles(gfax.SpoolDirectory, "C_*"); + // for each job try send it + foreach (string controlFile in controlFiles) { + if (Settings.Faxtracing == true) { + Console.WriteLine("Control files are ----> {0}", controlFile); + } + + //Application.Invoke (delegate {gfax.GAppbar.ClearStack();}); + Application.Invoke (delegate {gfax.GStatusTextBuffer.InsertAtCursor( + Catalog.GetString("Running job " + controlFile));}); + + try { + infile = File.OpenText(controlFile); + + for (int i=0; (buf = infile.ReadLine()) != null; i++) { + string[] sa = buf.Split('='); + if ( sa[1].Length != 0 ) + sts[i] = sa[1]; + else + sts[i] = "-"; + } + infile.Close(); + if (Settings.Faxtracing == true) { + Console.WriteLine("[Efaxd] Buffer is {0}", buf); + } + } + catch (Exception e) {continue; } + + //lines[num_lines++] = String.Format("{0}={1}={2}={3}={4}={5}={6}={7}\n", + // sts[0],sts[2],sts[3],sts[4],sts[5],sts[6],sts[10],sts[7]); + + // If we aborted for some reason check the retries + if (Convert.ToInt32(sts[6]) >= Settings.EfaxRetries) { + sts[10] = Catalog.GetString("Busy retries exceeded"); + update_status_code(sts, controlFile, "F"); + continue; + } + + sts[10] = ""; // clear error message + update_status_code(sts, controlFile, "R"); + + // default "-iM1L0" + speakerModeVolume = String.Concat("-i", + speakerMode[Settings.EfaxModemSpeakerMode], + speakerVolume[Settings.EfaxModemSpeakerVolume]); + + string [] tifFiles = Directory.GetFiles(sts[1], "tmp.*"); + StringBuilder filesToSend = new StringBuilder(); + foreach (string f in tifFiles) { + filesToSend.Append(f); + filesToSend.Append(" "); + } + + + ProcessStartInfo pidInfo = new ProcessStartInfo(); + pidInfo.FileName = "efax"; + pidInfo.RedirectStandardError = true; + pidInfo.UseShellExecute = false; + //used for testing + //Thread.Sleep(5000); + //pidInfo.FileName = "echo"; + //pidInfo.FileName = "sleep 5"; + + pidInfo.Arguments = String.Concat( + "-d /dev/", Settings.EfaxModemDevice, //modem port + " -x ", Settings.EfaxLockfile, Settings.EfaxModemDevice, // lockfile + " \"", Settings.EfaxModemInit, "\"", // init sequence + " \"", speakerModeVolume, "\"", // speaker enable / mode + " -l \"", Settings.FaxNumber, "\"", // Our fax number + " \"", Settings.EfaxModemReset, "\"", // how to reset modem + " -f /usr/bin/efaxfont", + " -h \'%d/%d\'", // should be name number and date + " -v i", // session progress information + " -t T", sts[2], // should make sure there are no bad chars + " ", filesToSend); // files to send + + if (Settings.Faxtracing == true) { + Console.WriteLine("[Efax.send]\n {0}", pidInfo.Arguments); + } + Application.Invoke (delegate {gfax.GAppbar.ClearStack();}); + Application.Invoke (delegate {gfax.GAppbar.Push(Catalog.GetString("Sending facsimile..."));}); + Process pid = Process.Start(pidInfo); + StreamReader myStreamReader = pid.StandardError; + + //start the pulser + Application.Invoke (delegate {gfax.Pulser.StartPulse();}); + Application.Invoke (delegate {gfax.Status.Append("");}); + + while (!pid.HasExited) { + string str = myStreamReader.ReadLine(); + Application.Invoke (delegate {gfax.Status.Append(str);}); + System.Threading.Thread.Sleep(200); + } + //stop the pulser. + Application.Invoke (delegate {gfax.Pulser.EndPulse();}); + + if (Settings.Faxtracing == true) { + Console.WriteLine("[Efax.efaxd] Exit code - {0}", pid.ExitCode); + } + + switch (pid.ExitCode) { + case 0: + // If successful then mv the control file to the done queue and flag + // a date for it's removal. + sts[10] = Catalog.GetString("Success"); + update_status_code(sts, controlFile, "D"); + // remove directory path + string basefilename = controlFile.Remove(0, gfax.SpoolDirectory.Length + 1); + string newfilename = String.Concat(gfax.SpoolDirectory,"/doneq/", basefilename); + File.Move(controlFile, newfilename); + break; + case 1: + // busy number, continue after timeout + // update listview as well + retries = Convert.ToInt32(sts[6]); + if (retries++ < Settings.EfaxRetries) { + sts[6] = retries.ToString(); + update_status_code(sts, controlFile, "W"); + } + else { + sts[10] = Catalog.GetString("Busy retries exceeded"); + update_status_code(sts, controlFile, "F"); + } + break; + case 2: + // fatal errors - no retry + //fatalError = true; + // change code in file + sts[10] = Catalog.GetString("Fatal error"); + update_status_code(sts, controlFile, "F"); + break; + case 3: + // Modem error - no retry + //fatalError = true; + sts[10] = Catalog.GetString("Fatal modem error"); + update_status_code(sts, controlFile, "F"); + break; + case 4: + // Modem not responding + //fatalError = true; + sts[10] = Catalog.GetString("Modem not responding"); + update_status_code(sts, controlFile, "B"); + break; + case 5: + // Program terminated + //fatalError = true; + sts[10] = Catalog.GetString("Program terminated"); + update_status_code(sts, controlFile, "F"); + break; + }// end of switch + + //sleep between each fax for the modem to stablize + System.Threading.Thread.Sleep(3000); + } // end of foreach + // This basically sleeps between busy numbers. + System.Threading.Thread.Sleep(30000); + } + System.Threading.Thread.Sleep(30000); // sleep 30 seconds + // Moved this from case 0 above + if (Settings.Faxtracing == true) { + Console.WriteLine("Deleting --->{0}", sts[1]); + } + //Directory.Delete(sts[1], true); + }// end of while + } + + + // Updates the status code in the control file and writes it back to disk + public void update_status_code (string[] job, string file, string status) + { + string fname = file; + + if ( status == "F" ) { + string filename = file.Remove(0, gfax.SpoolDirectory.Length + 1); + fname = String.Concat(gfax.SpoolDirectory,"/doneq/", filename); + File.Delete(file); + Directory.Delete(job[1], true); + } + + + // Thread safe + TextWriter outfile = TextWriter.Synchronized(File.CreateText(fname)); + + // TODO change this to string.concat and just issue one write + try { + //outfile = File.CreateText(file); + + outfile.Write("Jobid="); + outfile.WriteLine(job[0]); + outfile.Write("JobDirectory="); + outfile.WriteLine(job[1]); + outfile.Write("PhoneNumber="); + outfile.WriteLine(job[2]); + outfile.Write("Status="); + outfile.WriteLine(status); + outfile.Write("Owner="); + outfile.WriteLine(job[4]); + outfile.Write("Pages="); + outfile.WriteLine(job[5]); + outfile.Write("Dials="); + outfile.WriteLine(job[6]); + outfile.Write("Sendat="); + outfile.WriteLine(job[7]); + outfile.Write("Notification="); + outfile.WriteLine(job[8]); + outfile.Write("Email="); + outfile.WriteLine(job[9]); + outfile.Write("ErrorMessage="); + outfile.WriteLine(job[10]); + + outfile.Close(); + } + catch (Exception e) {} + } + + } +} -- cgit v1.1-32-gdbae