summaryrefslogtreecommitdiffstats
path: root/src/efax.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/efax.cs')
-rwxr-xr-xsrc/efax.cs605
1 files changed, 605 insertions, 0 deletions
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) {}
+ }
+
+ }
+}