#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# hylaPEx, an hylafax client written in python
#
# www.unipex.it/hylapex/
#
# License:
# GNU General Public License (GPL)
# For more info see LICENSE.txt and LICENSE-GPL.txt
#
# Copyright (C) 2004-2006 Unipex s.r.l., All Rights Reserved.
# Via Vittorio Veneto 83/A
# 33050 Gonars (UD) - Italy
# phone 0039 0432 931511 - fax 0039 0432 931378
# www.unipex.it - michele.petrazzo@unipex.it
#Twisted
from twisted.internet import reactor,threads
from myftp import FClient
from twisted.protocols import basic
from twisted.internet.protocol import Protocol,ClientCreator
#System
from cStringIO import StringIO
from datetime import datetime
from operator import itemgetter
from tempfile import mkstemp
import string , time, os, sys, pprint, copy, traceback
#Image
import hylaimage
#Key to hylafax (hfaxd) codes. See man hfaxd for more info
H_DONE_F = {'comp':'C','dial':'D','pri':'I','jtag':'J','stat':'a','num':'e','id':'j','own':'o',
'last':'s','kill':'k','pag':'p','dat':'Y','rate':'E', 'tts':'z', 'loc': 'L',
'from_send': 'S', 'to_pers': 'R'}
H_RCV_F = {'err':'e', 'img':'f', 'pag':'p','snd':'s', 'dat':'t', 'err':'e'}
H_SEND_F = {'comp':'C','dial':'D','pri':'I','jtag':'J','stat':'a','num':'e','id':'j','own':'o',
'last':'s','kill':'k','pag':'P','tts':'z', 'sched':'Z', 'rate':'E', 'loc': 'L',
'from_send': 'S', 'to_pers': 'R'}
#Formats
#DONE_FMT = "%j|%C|%L|%I|%o|%a|%D|%e|%P|%z|%s"
#SND_FMT = "%j|%C|%Y|%L|%o|%a|%e|%p|%I|%s"
#RCV_FMT = "%f|%p|%s|%t|%e"
#Orders
DONE_ORDER = ["id", "comp", "dat", "to_pers", "own", "stat", 'num', 'pag', 'pri', 'last']
SEND_ORDER = ["id", "comp", "to_pers", "pri", "own", "stat", 'dial', 'num', 'pag', 'tts', 'last']
RCV_ORDER = ["img", "pag", "snd", "dat", 'err']
RCV_FORMAT_DATE = "%d-%m-%y %H:%M"
DONE_FORMAT_DATE = "%d-%m-%y %H:%M"
#View
DONE_VIEW = 0
RCV_VIEW = 1
SEND_VIEW = 2
STATUS_VIEW = 100
ALL_VIEW = 101
LINE_SEP = "\r\n"
FILE_TYPE_PS = 0
FILE_TYPE_PDF = 1
FILE_TYPE_TIFF = 2
D_FYLE_TYPE = {FILE_TYPE_TIFF: ('.tif', ""),
FILE_TYPE_PDF: ('.pdf', "%PDF"),
FILE_TYPE_PS: ('.ps', "%!PS"), }
#Not want the tiff test
NUM_F_TYPE = len( D_FYLE_TYPE ) -1
class NotConnected(Exception):
pass
class _BufferingProtocol(Protocol, basic.LineReceiver):
"""Simple utility class that holds all data written to it in a buffer."""
def __init__(self):
self._buffer = StringIO()
def dataReceived(self, data):
self._buffer.write(data)
def GetBuffer(self):
return self._buffer
buffer = property(GetBuffer)
class SendFax(object):
""" Class useful for send a fax
"""
def __init__(self):
""" Init the class and create default values
"""
super(SendFax, self).__init__()
def isdigit(value):
if isinstance(value, basestring):
return value.isdigit()
else:
return False
#Don't call the __setattr__ funct
self.__dict__["_startUp"] = 1
#Parameters list, with value name, default value, possible values (or function to control it)
self.paramtersList = {
#"leave" as are, unless you know what are you doing
"chopthreshold": ("3", True),
"pagechop": ("default", True),
"pagelength": ("296", isdigit),
"pagewidth": ("209", isdigit),
"vres": ("196", isdigit),
#user paramenters
"dialstring": (None, True), # the fax number
"fromuser": (None, True), #the user that send the fax
"notifyaddr": ("", False), # email where send the notify
"notify": ("done+requeue", # and when notify it
("done+requeue","done", "requeue", "none", True) ),
"maxdials": ("10", isdigit), #how many dials
"lasttime": ("010000", isdigit), #when the tranmission will fail
"tolocation": ("", False), #location. I'm using it has subject
"tocompany": ("", False), #company where send the fax
"schedpri": ("127", isdigit), #fax priority 0<->255. 0 is high
"sendtime": ("Now", True), # when send the fax
#Not directly fax parameters
"file_list": ([], True), #File list
"convert": (True, False), #Convert fax before send
"gspath": ("", False), #Gs path
}
for name, (val, _) in self.paramtersList.iteritems():
#Set defaults values
setattr(self, name, val)
self._parametersAdded = {}
self._always_skip = ["convert", "gspath"]
self.__dict__["_startUp"] = 0
def GetSendValues(self, skip_value=()):
""" Send the fax with the paramters passed
"""
d_ret = {}
#Control the data and its default values
for name, (value, default) in self.paramtersList.iteritems():
#If a paremeter was added, fetch that value, otherwise fetch the defalut one
if name in self._parametersAdded:
value = self._parametersAdded[name]
#Control the values
if default in (None, False) or default == True and value:
#If any value are accepted, skip the control
pass
elif callable(default):
if not default(value):
raise ValueError, "Value incorrect for attribute: %s %s" % (name, value)
elif isinstance( default, (list, tuple) ):
if not value in default:
raise ValueError, ("Value not in list attribute list: %s %s" %
(value, default) )
elif default == True and not value:
raise ValueError, "Value not present for attribute: %s" % name
else:
raise ValueError, ("Default value %s not understood for attribute: %s" %
(default, name) )
d_ret[name] = value
d_ret.update( self._parametersAdded )
#The caller not need a value, skip (k in skip_value)
#If the value are empty, skip it. Hylfax not want empty parameters! (not v)
return dict( [(k,v) for k, v in d_ret.iteritems()
if v and not k in skip_value and not k in self._always_skip] )
def addParameter(self, name, value):
""" Add a parameter to the send class
"""
name, value = self._ctrlParameters(name, value)
self._parametersAdded[name] = value
def _ctrlParameters(self, name, value):
""" Control how the user pass us the values
"""
if name in ("dialstring", "file_list"):
if not isinstance(value, (tuple, list)):
value = [value]
if name == "lasttime":
value += (6 - len(value)) * "0"
return name, value
def __setattr__(self, name, value):
""" Trickle that leave the user add a paramter directly
"""
if not self._startUp:
if isinstance( name, basestring ):
name = name.lower()
if name in self.paramtersList and not name.startswith("_"):
self.addParameter(name, value)
name, value = self._ctrlParameters(name, value)
#print name, value
self.__dict__[name] = value
def __repr__(self):
return ( "Fax instance.%s My values are: %s" %
( super(SendFax, self).__repr__(),
pprint.pformat(self.__dict__) ) )
sendvalues = property(GetSendValues)
class HylafaxProto(object):
""" Hylafax proto class made with twisted
"""
chunk_size_send = 8192
mText = "Modem"
def __init__(self, host, user, passwd="", adminpasswd="",
passive=1, tzone=0,
callBackOnUpdate=None, callBackOnError=None,
timeUpdate=30,
filterSend=None, filterDone=None, filterRcv=None,
orderSend=None, orderDone=None, orderRcv=None,
sortSend=None, sortDone=None, sortRcv=None, sortReverse=True,
callBackOnStatus=None,
mode="S", port=4559,
dateTimeRcv=None, dateTimeRcvUse=True,
filterSendActive=True, filterDoneActive=True, filterRcvActive=True,
advancedFilters=None,
fetch_view=None,
debug=0, debugFunct=None):
""" Values to pass me:
host: the host name
user: user for login
passwd: its password, if any. default null
adminpasswd: the administratio password, if any. default null
callBackOnUpdate: a callable called when an update are done
callBackOnError: a callable called when an update are done
timeUpdate: how many seconds wait for an update
filterSend, filterDone, filterRcv = Filter the number of fax or filter by date. dict with "d" or "n" as key
sortSend, sortDone, sortRcv = column where sort. Can be an integer or a the column string
sortReverse = do a reverse sort? defalut true
callBackOnStatus = callback when I receive the hylafax status info
mode, port = ftp mode communication (string/image) as string and port integer
dateTimeRcv, dateTimeRcvUse = Does I change the date representation for the received fax? If yes,
passme a dict with the already seen fax
filterSendActive, filterDoneActive, filterRcvActive = does I have to filter the data?
advancedFilters = do advanced filtering. See filters.py
fetch_view: if None I'll fetch all the three views, otherwise I'll *delete*, from my views that passed
debug, debugFunct = debug and call the debugFunct with the parameters
"""
self._startingUp = True
#Setup debug
self.debugFunct = debugFunct
self._debug = debug
self._init_debug()
#Sorted columns
self.sortSend = sortSend
self.sortDone = sortDone
self.sortRcv= sortRcv
self.sortReverse = sortReverse
#Filters
self.filterDone = filterDone
self.filterRcv = filterRcv
self.filterSend = filterSend
#Orders
self.orderSend = orderSend
self.orderDone = orderDone
self.orderRcv = orderRcv
#AdvancedFilters
self.advancedFilters = advancedFilters
#Filters activation
self.filterDoneActive = filterDoneActive
self.filterRcvActive = filterRcvActive
self.filterSendActive = filterSendActive
#Internal buffers. Real (passed from the server) and with the order and filter Applied
self._buff_status = []
self._buff_sendR, self._buff_doneR, self._buff_recvR = (),(),()
self._buff_sendA, self._buff_doneA, self._buff_recvA = (),(),()
#self._orderSend = SEND_ORDER; self._orderDone = DONE_ORDER; self._orderRcv = RCV_ORDER
#self._filterDone = {}; self._filterRcv = {}; self._filterSend = {}
#Server info
self.host = host; self.user = user; self.passwd = passwd; self.adminpasswd = adminpasswd
self._port = port; self._passive = passive; self._mode = mode; self._tzone = tzone
#Some other values
self._callBackOnUpdate = callBackOnUpdate
self._callBackOnError = callBackOnError
self._callBackOnStatus = callBackOnStatus
self._timeUpdate = timeUpdate
self._dateTimeRcv = dateTimeRcv or {}
self._dateTimeRcvUse = dateTimeRcvUse
#sequence for call the fetch data
self._lst_sequence_fetch_order = [STATUS_VIEW, SEND_VIEW, RCV_VIEW, DONE_VIEW, ALL_VIEW]
if fetch_view is None:
fetch_view = ()
elif not isinstance(fetch_view, (list, tuple)):
fetch_view = (fetch_view, )
for view_del in fetch_view:
self._lst_sequence_fetch_order.remove(view_del)
self._lst_sequence_fetch = { STATUS_VIEW: (self._RetrStatus, self._RetStatus),
SEND_VIEW: (self._RetrSend, self._RetSend), DONE_VIEW: (self._RetrDone, self._RetDone),
RCV_VIEW: (self._RetrRecvq, self._RetRecvq), ALL_VIEW: (self._RetrAll, ) }
self._on_update = False
self._current_fetch_idx = 0
self._lstIdDateChanged = []
self._delayCall = None
#Are us connected?
self._connected = 0
self._mdtmCalled = 0
#List where save the SendFax classes not already done
self.queue_send = []
#Only for multitasking tests
if self._debug4:
reactor.callLater(0.2, self._timePassed)
self._startingUp = False
self._hClient = None
self._CreateFormats()
self._SendStartupParameters()
#
# Init process
#
def _SendStartupParameters(self):
""" Send login and other startup parameters
"""
if self._startingUp: return
#Reset the connection, if need
if self._connected and self._hClient.connected:
self._hClient.quit()
self._connected = 0
# Create the client
creator = ClientCreator(reactor, FClient, self._user, self._passwd,
self._passive, self._loggedin, fail=self._Fail)
#FTPClient.fail = self._Fail
cre = creator.connectTCP(self._host, self._port)
cre.addCallback(self._ConnectionMade)
cre.addErrback(self._Fail)
def _loggedin(self, res):
""" Called when we are loggedin
"""
if self._adminpasswd and self._connected:
dadmin = self._hClient.queueStringCommand("ADMIN %s" % self._adminpasswd)
dadmin.addErrback(self._Fail)
if not self._connected: return
#set mode and time zone
if not self._connected: return
d = self.SetMode()
if not self._connected: return
reactor.callLater(0.5, self.SetTzone)
d.addErrback(self._Fail)
#Load the first-time data
if self._timeUpdate > 0:
self._delayCall = reactor.callLater(self._timeUpdate, self._get_current_fetch())
self._delayCall.delay(10)
reactor.callLater(0, self._get_current_fetch())
elif self._timeUpdate == 0:
reactor.callLater(0, self._RetrStatus, onlyStatus=True)
def _ConnectionMade(self, ftpClient):
"""
"""
if self._debug1: self._Debug("HP:_ConnectionMade")
self._hClient = ftpClient
self._connected = 1
#
# Retrieve data
#
def _RetrStatus(self, *args, **kw):
""" Retrieve the status
"""
if self._debug2: self._Debug("HP:start _RetrStatus")
if self._on_update:
if self._debug2: self._Debug("HP:We are already updating, skip")
return
self._on_update = True
if not self._hClient:
self._Debug("HP:No connection on _RetrStatus. Why?")
return
proto = _BufferingProtocol()
conn = self._hClient.list('status', proto)
conn.addCallback(self._get_current_fetch_cb(), proto, *args, **kw)
conn.addErrback(self._Fail)
def _RetrSend(self):
""" Retrieve the send queue
"""
if self._debug2: self._Debug("HP:start _RetrSend")
self._SetJFormat(SEND_VIEW)
proto = _BufferingProtocol()
conn = self._hClient.list('sendq', proto)
conn.addCallbacks(self._get_current_fetch_cb(), self._Fail, callbackArgs=(proto,))
def _RetrDone(self):
""" Retrieve the done queue
"""
if self._debug2: self._Debug("HP:start _RetrDone")
self._SetJFormat(DONE_VIEW)
proto = _BufferingProtocol()
conn = self._hClient.list('doneq', proto)
conn.addCallbacks(self._get_current_fetch_cb(), self._Fail, callbackArgs=(proto,))
def _RetrRecvq(self):
""" Retrieve the recvq queue
"""
if self._debug2: self._Debug("HP:start _RetrRecvq")
self._SetJFormat(RCV_VIEW)
proto = _BufferingProtocol()
conn = self._hClient.list('recvq', proto)
conn.addCallbacks(self._get_current_fetch_cb(), self._Fail, callbackArgs=(proto,))
def _RetrMDTM(self):
""" Fetch the date for the receive fax, if need/want
"""
if not ( self._dateTimeRcvUse and self._connected ): return
posImg = self._orderRcv.index("img")
sSrv = set( [ x[posImg] for x in self._buff_recvR ] )
sDone = set( self._dateTimeRcv )
numToRecon = sSrv - sDone
#Clean the dict
for k in sDone - sSrv: self._dateTimeRcv.pop(k)
for numId in sorted( list( numToRecon ), reverse=True):
if numId in self._dateTimeRcv: continue
break
else:
return
if not self._hClient.connected:
self._sendStartupParameters()
return
d = self._hClient.queueStringCommand("MDTM recvq/%s" % numId)
d.addCallback(self._RetMDTM, numId, self._dateTimeRcv)
d.addErrback(self._Fail)
#
# - Return functions from retrieve
#
def _RetStatus(self, result, b, *args, **kw):
if self._debug2: self._Debug("HP:receved _RetStatus")
buf = b.buffer.getvalue()
#Keep only the lines that has some data
self._buff_status = LINE_SEP.join( [ x for x in buf.split(LINE_SEP) if x.strip() ] )
#If need, callit
if callable(self._callBackOnStatus):
reactor.callLater(0, self._callBackOnStatus)
if kw.get("onlyStatus", None): return
self._get_current_fetch()()
def _RetSend(self, result, b):
if self._debug2: self._Debug("HP:receved _RetSend")
self._buff_sendR = self._CreateDataSets( b.buffer.getvalue() )
self._get_current_fetch()()
def _RetDone(self, result, b):
if self._debug2: self._Debug("HP:receved _RetDone")
buff = self._CreateDataSets( b.buffer.getvalue() )
if buff != self._buff_doneR:
self._lstIdDateChanged = []
self._buff_doneR = buff
self._get_current_fetch()()
def _RetRecvq(self, result, b):
if self._debug2: self._Debug("HP:receved _RetRecvq")
self._buff_recvR = self._CreateDataSets( b.buffer.getvalue() )
self._get_current_fetch()()
def _RetrAll(self):
""" We have done the update, so call sort, filter and restart the timer;
at the end, call the parent update, if need
"""
if self._debug2: self._Debug("HP:receved _RetAll")
self._on_update = False
self._current_fetch_idx = 0
self._SetFiltersAndOrders()
if self._delayCall and self._delayCall.active():
self._delayCall.reset(self._timeUpdate)
elif self._timeUpdate > 0:
reactor.callLater(self._timeUpdate, self._get_current_fetch())
if callable(self._callBackOnUpdate):
reactor.callLater(0, self._callBackOnUpdate)
if not self._mdtmCalled:
reactor.callLater( 0.1, self._RetrMDTM )
self._mdtmCalled = 1
def _RetMDTM(self, ret, numId, d):
"""
"""
if not (len(ret) > 0 and ret[0].startswith("213") ):
print "Error while retrieve date for file %s" % numId
return
mydate = ret[0].split(" ", 1)[1]
lstSplit = (4,2,2,2,2,2)
myIndex = 0
lstOut = []
for idx in lstSplit:
lstOut.append( mydate[ myIndex : myIndex+idx ] )
myIndex += idx
dateOut = datetime(*map(int, lstOut)).timetuple()
d[numId] = dateOut
reactor.callLater( 0.3, self._RetrMDTM )
def _RetRetrFile(self, result, proto, *args, **kw):
""" Into kw passme:
callBack -> callable
fileOut -> None
createFileOut -> True
returnBuffer -> False
"""
if len(args) > 0: callBack = args[0]
else: callBack = kw.get("callBack", None)
if not callable(callBack):
raise ValueError, "Pass me a valid callable at retrieve file"
fileOut = kw.get("fileOut", None)
createFileOut = kw.get("createFileOut", True)
returnBuffer = kw.get("returnBuffer", False)
#The buffer
buff = proto.buffer.getvalue()
#Control the header
filetype = filter(lambda x: buff.startswith( D_FYLE_TYPE[x][1] ),
range(NUM_F_TYPE) )
if filetype: filetype = filetype[0]
else: filetype = FILE_TYPE_TIFF
#Have to create the file?
if createFileOut:
if fileOut:
f = open(fileOut, "wb")
else:
ext = D_FYLE_TYPE[filetype][0]
fd, fileOut = mkstemp(prefix="hylapex_tmp_%s_" % self._user, suffix=ext)
f = os.fdopen(fd, "wb")
f.write( buff )
f.close()
par = {}
#Paramenters
par.update(kw)
par["filetype"] = filetype
if returnBuffer:
par["returnBuffer"] = buff
reactor.callLater(0, callBack, fileOut, **par)
def _Fail(self, response):
""" Internal fail command
"""
#FTP connection lost
if hasattr( response, "getTraceback" ):
resp = response.getTraceback()
else:
resp = "UnexpectedResponse"
if ("FTP connection lost" in resp or
( hasattr( response, "type" ) and
issubclass(response.type, NotConnected) )
) :
#We lost connection, try to reconned silently!
print "Error silently skipped:", resp
self._SendStartupParameters()
print response.stack
if callable( self._callBackOnError ):
self._callBackOnError( "Connection are lost, but I'll reconnect to the server" )
return
self._connected = 0
#Delete the call, if need
if self._delayCall and self._delayCall.active():
self._delayCall.cancel()
#Call error, if need
if callable( self._callBackOnError ):
self._callBackOnError( resp )
if not self._connected:
return
self._hClient.quit()
#
# Interface methods
#
def Close(self):
""" Close the connection and exit
"""
self._hClient.quit()
self._connected = 0
def GetDataViewRaw(self, view):
""" Get the buffer for the view passed
"""
if view == DONE_VIEW:
return self._buff_doneR
elif view == RCV_VIEW:
return self._buff_recvR
elif view == SEND_VIEW:
return self._buff_sendR
else:
raise ValueError
def GetDataView(self, view):
""" Get the buffer for the view passed
"""
if view == DONE_VIEW:
return self._buff_doneA
elif view == RCV_VIEW:
return self._buff_recvA
elif view == SEND_VIEW:
return self._buff_sendA
else:
raise ValueError
def SetFilterView(self, f, view):
""" Set the whole filter by the view
"""
fi = self._getFilterDictByView(view)
fi = f
def SetFilterOptView(self, option, value, view):
""" Set the filter option for the view.
@see: __ChangeData
"""
self._getFilterDictByView(view)[option] = value
def SetFilterActiveView(self, value, view):
""" Set filter activation by the view
"""
if view == DONE_VIEW:
self.filterDoneActive = value
elif view == RCV_VIEW:
self.filterRcvActive = value
elif view == SEND_VIEW:
self.filterSendActive = value
else:
raise ValueError
def RetrFile(self, fid, view, *args, **kw):
""" Retrieve a file
@par fid: File if
@par view: My view
@par kw:
callBack -> callable
fileOut -> None
createFileOut -> True
returnBuffer -> False
"""
proto = _BufferingProtocol()
if view == RCV_VIEW:
conn = self._hClient.retrieveFile("recvq/" + fid, proto)
else:
raise NotImplementedError("Like now I can retrieve the files on recvq dir")
conn.addCallback(self._RetRetrFile, proto, *args, **kw)
conn.addErrback(self._Fail)
def SendFax(self, fax, callback=None):
""" Send the fax passed. fax must be a SendFax instance
For make the work, we convert and stor the file into ftp server.
@see: _SendFax_Conversion and the rest for more info
"""
if not isinstance(fax, SendFax):
raise ValueError, "The fax passed aren't a SendFax istance!"
#create a copy for every destination and send them one at a time
for fax_ds in fax.dialstring:
fax_copied = copy.copy(fax)
fax_copied.dialstring = fax_ds
self.queue_send.append(fax_copied)
self._on_sending = False
#and call the process
reactor.callLater(0.1, self._SendFax,callback)
def SuspendJob(self, jobid):
""" Suspend a job
"""
self._sendCommandList( 'JOB %s' % jobid, 'JSUSP' )
self._get_current_fetch()()
def DeleteJob(self, jobid):
""" Delete the job
"""
cmdLst = []
if jobid.startswith('fax'):
cmdLst.append('DELE recvq/%s' % jobid)
else:
cmdLst.append('JOB %s' % jobid)
cmdLst.append('JDELE')
self._sendCommandList(cmdLst)
self._get_current_fetch()()
def AlterJob(self, jobid, paramters):
""" Alter the job
"""
self.SuspendJob(jobid)
cmdList = list()
cmdList.append('JOB %s' % jobid)
for k, value in paramters.iteritems():
cmdList.append( 'JPARM %s %s' % (k, value) )
cmdList.append('JSUBM')
self._sendCommandList(cmdList)
def UpdateStatus(self):
""" Call only the update of the status queue
"""
self._get_current_fetch()(onlyStatus=True)
def UpdateAll(self, resetTime=None):
""" Force the update of the queue before the time has passed
"""
if resetTime == None: resetTime = True
if self._delayCall and self._delayCall.active() and resetTime:
#Reset the timer, so it not will be call when I already receiving data
self._delayCall.reset(self._timeUpdate)
self._get_current_fetch()()
def FakeUpdate(self):
""" Call a not "true" update. Need when you want to say me to call the
update callbaack
"""
self._RetrAll()
#
# Send fax helps
#
def _SendFax(self, callback):
""" Internal send fax
"""
if self._hClient.actionQueue or self._on_sending:
#If there are other commands or other sending fax, wait for them
reactor.callLater(0.1, self._SendFax, callback)
return
else:
if not self.queue_send:
if callable(callback):
reactor.callLater(0, callback)
return
self._on_sending = True
#Return for see if all are end
reactor.callLater(0.1, self._SendFax, callback)
fax = self.queue_send.pop()
if self._debug1: self._Debug("HP:Start send fax")
if self._debug2: self._Debug(fax)
if fax.convert:
d = threads.deferToThread(self._SendFax_Conversion, fax)
d.addCallback(self._SendFax_EndConversion, fax)
d.addErrback(self._Fail)
else:
self._SendFax_EndConversion(fax.file_list[0], fax)
def _SendFax_Conversion(self, fax):
""" Start the file conversion
"""
if self._debug1: self._Debug("HP:Start conversion")
#Create a new hylaimage instance
hi = hylaimage.Hylaimage(path_gs_exe=fax.gspath,
debug=self._debug, debug_funct=self.debugFunct)
hi.DeleteFiles()
#add the files
hi.AddFile( fax.file_list )
return hi.GetSingleImage()
def _SendFax_EndConversion(self, filename, fax):
""" Conversion are end. Say to hylafax that we want to
open a channel for send the file
"""
if self._debug1: self._Debug("HP:End conversion. File path: ", filename)
d_start, d_end = self._hClient.storeFileT()
def create_store(channel):
d_store = threads.deferToThread( self._SendFax_Store, channel, filename)
d_store.addErrback(self._Fail)
d_start.addCallback(create_store)
d_start.addErrback(self._Fail)
d_end.addCallback(self._SendFax_EndStore, fax)
d_end.addErrback(self._Fail)
return d_end
def _SendFax_Store(self, channel, filename):
""" Now store the file
"""
if self._debug1: self._Debug("HP:Start store file (fax) to the server")
f = open(filename, 'rb')
while True:
buff = f.read(self.chunk_size_send)
if not buff: break
channel.write(buff)
if self._debug3: self._Debug("HP:i sent", self.chunk_size_send)
channel.finish()
def _SendFax_EndStore(self, ret, fax):
"""
"""
try:
start, end = ret[0][1][0][1]
if not end.startswith('226'): raise
filepath = start.split()[2]
except:
raise ValueError, "Response are not on the form that I expect: %s" % pprint.pformat(ret)
if self._debug1: self._Debug("HP:Send the fax parameter to the server")
cmds = ['JOB DEFAULT', 'JNEW']
for k, v in fax.GetSendValues(("file_list", "dialstring")).iteritems():
cmds.append("JPARM %s %s" % (k, v) )
if self._debug2: self._Debug("HP:Parameter %s: %s" % (k, v))
cmds.append('JPARM dialstring "%s"' % fax.dialstring[0])
cmds.append('JPARM document %s' % filepath)
cmds.append('JSUBM')
self._sendCommandList(cmds)
#End the process
self._on_sending = False
return "Done senfax"
# -- Help methods
def _CreateFormats(self, onlyFor=None):
""" Create the formats string from the current order
"""
if self._startingUp: return
lstActions = list()
D = self._CreateFDone; S = self._CreateFSend; R = self._CreateFRcv
if onlyFor == None:
lstActions = (D, S, R)
elif onlyFor == DONE_VIEW: lstActions = (D,)
elif onlyFor == RCV_VIEW: lstActions = (R,)
elif onlyFor == SEND_VIEW: lstActions = (S,)
else: raise ValueError
for action in lstActions:
action()
#Create the parameters for send to the server
def _CreateFDone(self):
self._Fdone = "|".join( [ "%" + H_DONE_F[k] for k in self._orderDone ] )
def _CreateFSend(self):
self._Fsend = "|".join( [ "%" + H_SEND_F[k] for k in self._orderSend ] )
def _CreateFRcv(self):
self._Frcv = "|".join( [ "%" + H_RCV_F[k] for k in self._orderRcv ] )
def __SortData(self, data, sortCol, order):
""" Sort the data passed
"""
if ( isinstance(sortCol, basestring) and
sortCol in order ):
colOrder = order.index( sortCol )
elif isinstance(self._sortDone, int):
colOrder = sortCol
else:
raise ValueError, "I need string (that must be into the current order) or integer"
if order[colOrder] == "id":
def compare(x, y):
if x.isdigit() and y.isdigit(): f_comp = int
else: f_comp = str
return cmp( f_comp(x or 0), f_comp(y or 0))
else:
compare = cmp
return sorted(data, cmp=compare, key=itemgetter(colOrder), reverse=self.sortReverse)
def __FilterData(self, data, dFilter, view=-1):
""" Filter the data by the passed filter
"""
#Type control
if not isinstance(dFilter, dict):
raise ValueError, "Filter must be a dict!"
if "d" in dFilter and dFilter["d"] > 0:
#control the filter by date
if view == -1:
raise ValueError("Date filter must have the view!")
if view == DONE_VIEW and "dat" in DONE_ORDER:
dat_pos = self._orderDone.index("dat")
elif view == RCV_VIEW and "dat" in RCV_ORDER:
dat_pos = self._orderRcv.index("dat")
else:
return data
date_filter = time.time() - (dFilter["d"] *24*60*60)
data_tmp = []
#there is a data, work on it!
for line in data:
date_value = line[dat_pos]
if (date_value.count("-") != 2 and date_value.count(":") != 1):
#I cannot control non valid data
data_tmp.append(line)
continue
if not " " in date_value:
#skip strange values
data_tmp.append(line)
continue
d_, h_ = date_value.split()
d,m,y, h,mi = map(int, d_.split("-") + h_.split(":"))
sec_line = time.mktime( datetime(y,m,d, h,mi).timetuple() )
if sec_line > date_filter:
data_tmp.append(line)
data = data_tmp
if "n" in dFilter and dFilter["n"] > 0:
#Reverse the data. Here we have the old to new, but we
#want the opposite.
data.reverse()
data = data[-dFilter["n"]:]
return data
def __ChangeData(self):
""" Change data into the buffer due the specification
"""
#Receive, change date
posId = self._orderRcv.index("img")
posDate = self._orderRcv.index("dat")
for data in self._buff_recvA:
faxId = data[posId]
if not faxId in self._dateTimeRcv: continue
data[posDate] = time.strftime( RCV_FORMAT_DATE, self._dateTimeRcv[faxId] )
#Done, change date and other
posId = self._orderDone.index("id")
posDate = self._orderDone.index("dat")
for data in self._buff_doneA:
if data[posId] in self._lstIdDateChanged: continue
lstOut = []
date, hour = data[posDate].split(" ", 1)
for i in date.split("/", 2):
lstOut.append(i)
for i in hour.split(".", 2):
lstOut.append(i)
dateOut = datetime(*map(int, lstOut)).timetuple()
data[posDate] = time.strftime( RCV_FORMAT_DATE, dateOut )
self._lstIdDateChanged.append( data[posId] )
filterDoneOwn = self._filterDone.get("own", None)
filterSendOwn = self._filterSend.get("own", None)
#Control if we want to show only the user data
if filterDoneOwn and "own" in DONE_ORDER:
pos_own = DONE_ORDER.index("own")
self._buff_doneA = filter(lambda row: row[pos_own] == self.user,self._buff_doneA)
if filterSendOwn and "own" in SEND_ORDER:
pos_own = SEND_ORDER.index("own")
self._buff_sendA = filter(lambda row: row[pos_own] == self.user,self._buff_sendA)
self.__DoAdvancedFilters()
def __ControlStringInside(self, to_control, to_find, case_sensitive):
""" Control if the string to_find are inside the string st_control
Make the control lower case, if need
"""
if not case_sensitive:
to_find = to_find.lower()
to_control = map(string.lower, to_control)
for i in to_control:
if i in to_find: return False
else:
return True
def __DoAdvancedFilters(self):
""" Make advanced filters, so remove from buffer some data.
"""
if not self.advancedFilters: return
case_sensitive = self.advancedFilters.GetCaseSensitive()
fnv = self.advancedFilters.GetFiltersNotView()
fov = self.advancedFilters.GetFiltersOnlyView()
#3 are the views
for view in xrange(3):
ov = fov[view]
nv = fnv[view]
if not (nv or ov): continue
if view == DONE_VIEW:
buff = self._buff_doneA
elif view == SEND_VIEW:
buff = self._buff_sendA
elif view == RCV_VIEW:
buff = self._buff_recvA
#Control for the only view
for col, strOV in ov.iteritems():
if not strOV: continue
buff = filter(lambda row:
not self.__ControlStringInside(strOV, row[col], case_sensitive),
buff)
#Control for the not view
for col, strNV in nv.iteritems():
buff = filter(lambda row:
self.__ControlStringInside(strNV, row[col], case_sensitive),
buff)
if view == DONE_VIEW:
self._buff_doneA = buff
elif view == SEND_VIEW:
self._buff_sendA = buff
elif view == RCV_VIEW:
self._buff_recvA = buff
def _SetFiltersAndOrders(self):
""" Apply the the filters and the orders to the buffer
"""
if self._startingUp: return
if self._sortDone != -1:
self._buff_doneA = self.__SortData(self._buff_doneR, self._sortDone, self._orderDone)
else:
self._buff_doneA = self._buff_doneR[:]
if self._sortRcv != -1:
self._buff_recvA = self.__SortData(self._buff_recvR, self._sortRcv, self._orderRcv)
else:
self._buff_recvA = self._buff_recvR[:]
if self._sortSend != -1:
self._buff_sendA = self.__SortData(self._buff_sendR, self._sortSend, self._orderSend)
else:
self._buff_sendA = self._buff_sendR[:]
try:
self.__ChangeData()
except:
print "Error on __ChangeData"
traceback.print_exc()
if self.filterDoneActive:
self._buff_doneA = self.__FilterData(self._buff_doneA, self._filterDone, DONE_VIEW)
if self.filterRcvActive:
self._buff_recvA = self.__FilterData(self._buff_recvA, self._filterRcv, RCV_VIEW)
def _CreateDataSets(self, data):
""" Create, and return, data lines without separators
"""
if "|" in data:
#Create the right data format
d = [x.split("|") for x in data.split(LINE_SEP) if x.strip()]
else: d = list()
return d
def _SetJFormat(self, view, frmt=None):
""" Set the job format to the server
"""
if view == RCV_VIEW:
cmd = 'RCVFMT '
frmt = frmt or self._Frcv
else:
cmd = 'JOBFMT '
if view == DONE_VIEW:
frmt = frmt or self._Fdone
else:
frmt = frmt or self._Fsend
if not self._hClient.connected:
self._sendStartupParameters()
return
return self._hClient.queueStringCommand(cmd + frmt)
def _getFilterDictByView(self, view):
""" Get the filter associated by the view
"""
if view == DONE_VIEW:
return self.filterDone
elif view == RCV_VIEW:
return self.filterRcv
elif view == SEND_VIEW:
return self.filterSend
else:
raise ValueError
def _sendCommandList(self, cmdList, *args):
""" Send command list and after add them the fail deferred
"""
#Leave the user pass me strings or already tuple/list, but al least one value
if isinstance(cmdList, basestring):
cmdList = [cmdList]
for arg in args:
cmdList.append(arg)
if not self._hClient.connected:
self._sendStartupParameters()
return
deferreds = []
for cmd in cmdList:
if self._debug3: self._Debug("HP:Send command:", cmd)
deferreds.append( self._hClient.queueStringCommand(cmd) )
for deferred in deferreds:
# If something goes wrong, call fail
deferred.addErrback(self._Fail)
def _get_current_fetch(self):
""" Return the current fetch method
"""
try:
curr_view = self._lst_sequence_fetch_order[self._current_fetch_idx]
return self._lst_sequence_fetch[curr_view] [0]
except IndexError:
#create a null function that do nothing
def _f(*args, **kw):
pass
return _f
def _get_current_fetch_cb(self, sum_=True):
""" Return the current fetch method
"""
try:
curr_view = self._lst_sequence_fetch_order[self._current_fetch_idx]
if sum_:
self._current_fetch_idx += 1
return self._lst_sequence_fetch[curr_view] [1]
except:
def null_funct(*args, **kw):
pass
return null_funct
#
# Debug
#
def _init_debug(self):
""" Make the instance wide variables for debug
"""
self._debug1 = self._debug > 0
self._debug2 = self._debug > 1
self._debug3 = self._debug > 2
self._debug4 = self._debug > 3
def _SetDebug(self, value):
self._debug = value
self._init_debug()
def _GetDebug(self):
return self._debug
debug = property(_GetDebug, _SetDebug)
def _Debug(self, *args, **kw):
""" Unique debug function.
If the instancer wants the debug messages, he have to
pass me a debug function, otherwise I'll print the message.
If the debug value are more the 3, I'll print message in
any case.
"""
msg = ''
for arg in args:
arg = repr(arg)
if not arg.endswith(" "): arg += " "
msg += arg
if kw: msg += pprint.pformat(kw)
if callable(self.debugFunct):
self.debugFunct(msg)
if not callable(self.debugFunct) or self._debug4:
print msg
#
# Properties
#
def SetMode(self, newMode=None):
"""
"""
if not self._connected:return
if newMode: self._mode = newMode
if not self._hClient.connected:
self._sendStartupParameters()
return
return self._hClient.queueStringCommand("MODE %s" % self._mode)
def GetTzone(self):
"""
"""
return self._tzone
def SetTzone(self, tzone=None):
"""
"""
if tzone is not None: self._tzone = tzone
if isinstance(self._tzone, basestring) and not self._tzone.isdigit():
raise ValueError("Tzone value must be an integer 0|1")
elif isinstance(self._tzone, basestring) and self._tzone.isdigit():
self._tzone = int(self._tzone)
if not self._tzone in (0,1):
raise ValueError("Tzone value must be an integer 0|1")
if not self._connected: return
if self._tzone == 0: tzoneCmd = 'GMT'
else: tzoneCmd = 'LOCAL'
if not self._hClient.connected:
self._sendStartupParameters()
return
return self._hClient.queueStringCommand("TZONE %s" % tzoneCmd)
def GetModems(self):
""" Return modem list
"""
lstModems = list()
for strModem in self._buff_status.split(LINE_SEP):
#No modem found
if not self.mText in strModem: continue
lstM = strModem.split()
lstModems.append( lstM[lstM.index(self.mText) +1].strip() )
return lstModems
# -- Queues
def GetQStatus(self):
""" Status property
"""
return self._buff_status
def GetQRcvd(self):
"""
"""
return self._buff_recvA
def GetQSend(self):
"""
"""
return self._buff_sendA
def GetQDone(self):
"""
"""
return self._buff_doneA
def GetDateTimeRcv(self):
"""
"""
return self._dateTimeRcv
# -- CallBacks
def SetcallBackOnError(self, callBack):
"""
"""
self._callBackOnError = callBack
def SetcallBackOnUpdate(self, callBack):
"""
"""
self._callBackOnUpdate = callBack
def SetcallBackOnStatus(self, callBack):
"""
"""
self._callBackOnStatus = callBack
# -- Server info
def GetHost(self):
"""
"""
return self._host
def SetHost(self, host):
"""
"""
self._host = str(host)
self._SendStartupParameters()
def GetUser(self):
"""
"""
return self._user
def SetUser(self, user):
"""
"""
self._user = str(user)
self._SendStartupParameters()
def GetPasswd(self):
"""
"""
return self._user
def SetPasswd(self, passwd):
"""
"""
self._passwd = str(passwd)
self._SendStartupParameters()
def GetAdminPasswd(self):
"""
"""
return self._user
def SetAdminPasswd(self, adminpasswd):
"""
"""
self._adminpasswd = str(adminpasswd)
self._SendStartupParameters()
def GetPassive(self):
"""
"""
return self._passive
def SetPassive(self, passive):
"""
"""
self._passive = passive
self._hClient.passive = passive
def GetTimeUpdate(self):
""" Return the update time
"""
return self._timeUpdate
def SetTimeUpdate(self, time_sec):
""" Set the update time. If the time are < 0,
stop immediatly the timer
"""
self._timeUpdate = time_sec
if self._timeUpdate < 0 and self._delayCall and self._delayCall.active():
self._delayCall.reset(self._timeUpdate)
elif self._timeUpdate > 0:
if self._delayCall and self._delayCall.active():
#Only update the time, not need to recall
#wait for the current update and, the next time, set the new seconds
return
self._delayCall = reactor.callLater(self._timeUpdate, self._get_current_fetch())
self._delayCall.delay(10)
reactor.callLater(0, self._get_current_fetch())
# -- Filters
def GetFilterDone(self):
"""
"""
return self._filterDone
def SetFilterDone(self, filterDone):
"""
"""
self._filterDone = filterDone or {}
self._SetFiltersAndOrders()
def GetFilterRcv(self):
"""
"""
return self._filterRcv
def SetFilterRcv(self, filterRcv):
"""
"""
self._filterRcv = filterRcv or {}
self._SetFiltersAndOrders()
def GetFilterSend(self):
"""
"""
return self._filterSend
def SetFilterSend(self, filterSend):
"""
"""
self._filterSend = filterSend or {}
self._SetFiltersAndOrders()
# -- Filters
def GetFilterDoneActive(self):
"""
"""
return self._filterDoneActive
def SetFilterDoneActive(self, filterDoneActive):
"""
"""
self._filterDoneActive = filterDoneActive
self._SetFiltersAndOrders()
def GetFilterRcvActive(self):
"""
"""
return self._filterRcvActive
def SetFilterRcvActive(self, filterRcvActive):
"""
"""
self._filterRcvActive = filterRcvActive
self._SetFiltersAndOrders()
def GetFilterSendActive(self):
"""
"""
return self._filterSendActive
def SetFilterSendActive(self, filterSendActive):
"""
"""
self._filterSendActive = filterSendActive
self._SetFiltersAndOrders()
# -- Orders
def GetOrderDone(self):
""" Return the done order
"""
return self._orderDone
def SetOrderDone(self, orderDone):
""" Set the done order
"""
self._orderDone = orderDone or DONE_ORDER
self._CreateFormats(DONE_VIEW)
self._SetFiltersAndOrders()
def GetOrderSend(self):
"""
"""
return self._orderSend
def SetOrderSend(self, orderSend):
"""
"""
self._orderSend = orderSend or SEND_ORDER
self._CreateFormats(SEND_VIEW)
self._SetFiltersAndOrders()
def GetOrderRcv(self):
"""
"""
return self._orderRcv
def SetOrderRcv(self, orderRcv):
"""
"""
self._orderRcv = orderRcv or RCV_ORDER
self._CreateFormats(RCV_VIEW)
self._SetFiltersAndOrders()
# -- Sort
def GetSortDone(self):
"""
"""
return self._sortDone
def SetSortDone(self, sortDone):
"""
"""
self._sortDone= sortDone or 0
def GetSortRcv(self):
"""
"""
return self._sortRcv
def SetSortRcv(self, sortRcv):
"""
"""
self._sortRcv = sortRcv or 0
def GetSortSend(self):
"""
"""
return self._sortSend
def SetSortSend(self, sortSend):
"""
"""
self._sortSend = sortSend or 0
def GetHClient(self):
""" Return the hylafax client connection
Use it at your own risk!
"""
return self._hClient
#
# Debug functions
#
def _timePassed(self):
"""
"""
print "passed 0.2"
reactor.callLater(0.2, self._timePassed)
#
# Property
#
host = property(GetHost, SetHost)
user = property(GetUser, SetUser)
passwd = property(GetPasswd, SetPasswd)
adminpasswd = property(GetAdminPasswd, SetAdminPasswd)
passive = property(GetPassive, SetPassive)
tzone = property(GetTzone, SetTzone)
status = property(GetQStatus)
modems = property(GetModems)
timeUpdate = property(GetTimeUpdate, SetTimeUpdate)
qDone = property(GetQDone)
qRcv = property(GetQRcvd)
qSend = property(GetQSend)
dateTimeRcv = property(GetDateTimeRcv)
callBackOnUpdate = property(fset=SetcallBackOnUpdate)
callBackOnError = property(fset=SetcallBackOnError)
callBackOnStatus = property(fset=SetcallBackOnStatus)
filterSend = property(GetFilterSend, SetFilterSend)
filterDone = property(GetFilterDone, SetFilterDone)
filterRcv = property(GetFilterRcv, SetFilterRcv)
orderSend = property(GetOrderSend, SetOrderSend)
orderDone = property(GetOrderDone, SetOrderDone)
orderRcv = property(GetOrderRcv, SetOrderRcv)
sortSend = property(GetSortSend, SetSortSend)
sortDone = property(GetSortDone, SetSortDone)
sortRcv = property(GetSortRcv, SetSortRcv)
filterDoneActive = property(GetFilterDoneActive, SetFilterDoneActive)
filterRcvActive = property(GetFilterRcvActive, SetFilterRcvActive)
filterSendActive = property(GetFilterSendActive, SetFilterSendActive)
hylafax_client = property(GetHClient)
|