Commit 9105fd56 authored by Gabriel Monnerat's avatar Gabriel Monnerat

Commit initial version

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk/utils@37406 20353a03-c40f-0410-a6d1-a30d3c3de9de
parents
all: install
PYTHON=python2.6
install:
@$(PYTHON) setup.py install
0.6
===
- removed the configurationmanager.py.
0.5
===
- Removed the possibility of infinite loop in getAvailableOO.
- Corrected the problem when the document in file system isn't
found(IllegalArgumentException).
- Implemented in oohandler to when not exist OOo Instance free. If don't exist,
the requisition is locked.
0.4
===
Created class to manipulate the configuration file(ConfigurationManager).
Now, all paths are taken by this object and in configuration file is possible
set the number of OOo instance that you want. The OOFactory object was implemented
to start the max number of OOo instance and get the instance available to use.
0.3
===
Split start.py into openoffice.py and xvfb.py. The classes openoffice and xvfb have
methods to control the process(start,stop and status about the process). Created one class
and one object for it, for easier reload the configuration.
0.2
===
Extends the code to convert a document to pdf. Initially the connection with OO
was implemented in class worker, but should be removed and implemented separately.
0.1
===
Create a prototype using WSGI and XMLRPC. One class calculator was used as test.
Install Dependencies
--------------------
System Dependencies
- Xvfb
Python Dependencies
- zope.interface
- WSGIUtils
- PasteDeploy
- PasteScript
Instalation in Mandriva:
- urpmi xvfb
- urpmi python-setuptools
- easy_install-2.6 zope.interface PasteDeploy PasteScript WSGIUtils
Instalation of Openoffice
Was used for testing the package's official openoffice.Follow these steps to install:
- Download the package from the official site;
- unpack the tar.gz and Install;
The instalation is in /opt
How to use
----------
- Start the Daemon
./cloudooo start
Description of Daemon
---------------------
- XMLRPC + WSGI will be one bridge for easy access OpenOffice.org. This will implement one XMLRPC server into WSGI (Paster).
- PyUno is used to connect to OpenOffice.org stated with open socket. The features will be handled all by pyuno.
- Xvfb is used to run Openoffice.org. This is controlled by Daemon(cloudooo).
- Only a process will have access to OpenOffice.org by time.
- All clients receive the same object(proxy) when connects with XMLRPC Server.
Xvfb and OpenOffice
- configure and start Xvfb;
- Use a single Xvfb;
- the xvfb will be started with the XMLRPC Server;
- When start the Daemon(cloudooo), it configures Xvfb, next opens the openoffice(with pyuno) and start XMLRPC Server;
- control Xvfb;
- start openoffice;
- Pyuno start the openoffice processes and the communication is through sockets;
- Openoffice processes run in brackground and in virtual display;
- control openoffice;
- The socket can't lose the connection, if this occurs should kill the process and submit again the file;
XMLRPC Server - XMLRPC + WSGI
-----------------------------
- Send document to openoffice and return the document converted with metadata;
- XMLRPC receives a file and connects to a openoffice by pyuno;
- The pyuno opens a new openoffice, write, add metadata and returns the document edited or converted to xmlrpc and it return the document to the user;
- When finalize the use of openoffice, should make sure that it was finalized;
- Export to another format;
- Invite document and return metadata only;
- Edit metadata of the document;
- Problems and possible solution
- OpenOffice is stalled;
- finalize the process, start openoffice and submit the document again(without restart the cloudooo);
- Openoffice is crashed;
- finalize the process, verify if all the process was killed, start openoffice and submit the document again(without restart the cloudooo)
- OpenOffice received the document and stalled;
- if openoffice isn't responding, kill the process and start
- The document that was sent is corrupt;
- write in log the error and verify that the process aren't in memory
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import implements
from cloudooo.interfaces.application import IApplication
from cloudooo.utils import logger, socketStatus
from psutil import pid_exists, Process
class Application(object):
"""Base object to create an object that is possible manipulation a
process"""
implements(IApplication)
name = "application"
def start(self):
"""Start Application"""
logger.debug("Process Started %s, Port %s. Pid %s" % (self.name,
self.getAddress()[-1],
self.pid()))
def stop(self):
"""Stop the process"""
if hasattr(self, 'process') and self.status():
process_pid = self.process.pid
logger.debug("Stop Pid - %s" % process_pid)
try:
self.process.terminate()
finally:
if pid_exists(process_pid) or self.status():
Process(process_pid).kill()
def loadSettings(self, hostname, port, path_run_dir, display_id, **kwargs):
"""Define attributes for application instance
Keyword arguments:
hostname -- Host to start the instance.
port -- Expected a int number.
path_run_dir -- Full path to create the enviroment.
display_id -- Display to open the OpenOffice.
"""
self.hostname = hostname
self.port = port
self.path_run_dir = path_run_dir
self.display_id = display_id
self.timeout = kwargs.get('start_timeout', 20)
def restart(self):
"""Start and Stop the process"""
self.stop()
self.start()
def status(self):
"""Check by socket if the openoffice work."""
return socketStatus(self.hostname, self.port)
def getAddress(self):
"""Return port and hostname of OOo Instance."""
return self.hostname, self.port
def pid(self):
"""Returns the pid"""
if not hasattr(self, 'process') or not self.status():
return None
return self.process.pid
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from os import environ, remove
from os.path import exists, join
from subprocess import Popen, PIPE
from threading import Lock
from cloudooo.ooolib import setUpUnoEnvironment
from zope.interface import implements
from application import Application
from cloudooo.interfaces.lockable import ILockable
from cloudooo.utils import logger, waitStartDaemon, removeDirectory, waitStopDaemon
class OpenOffice(Application):
"""Object to control one OOo Instance and all features instance."""
implements(ILockable)
name = "openoffice"
def __init__(self):
"""Creates the variable to save the pid, port and hostname of the object.
The lock is a simple lock python that is used when one requisition is
using the openoffice.
"""
self._bin_soffice = 'soffice.bin'
self._lock = Lock()
self._cleanRequest()
def _testOpenOffice(self, host, port):
"""Test if OpenOffice was started correctly"""
logger.debug("Test OpenOffice %s - Pid %s" % (self.getAddress()[-1], self.pid()))
command = [self.python_path
, self.unoconverter_bin
, "--test"
, "--hostname=%s" % host
, "--port=%s" % port
, "--document_url=%s" % self.document_url
, "--unomimemapper_bin=%s" % self.unomimemapper_bin
, "--python_path=%s" % self.python_path
, "--uno_path=%s" % self.uno_path
, "--office_bin_path=%s" % self.office_bin_path]
logger.debug("Testing Openoffice Instance %s" % port)
stdout, stderr = Popen(" ".join(command), shell=True, stdout=PIPE,
stderr=PIPE).communicate()
if not stdout and stderr != "":
logger.debug(stderr)
return False
else:
url = stdout.replace("\n", "")
logger.debug("Instance %s works" % port)
remove(url)
return True
def _cleanRequest(self):
"""Define request attribute as 0"""
self.request = 0
def loadSettings(self, hostname, port, path_run_dir, display_id,
office_bin_path, uno_path, **kw):
"""Method to load the configuratio to control one OpenOffice Instance
Keyword arguments:
office_path -- Full Path of the OOo executable.
e.g office_bin_path='/opt/openoffice.org3/program'
uno_path -- Full path of the Uno Library
"""
Application.loadSettings(self, hostname, port, path_run_dir, display_id)
setUpUnoEnvironment(uno_path, office_bin_path)
self.office_bin_path = office_bin_path
self.uno_path = uno_path
self.process_name = "soffice.bin"
self.document_url = kw.get('document_url', '')
self.unoconverter_bin = kw.get("unoconverter_bin", "unoconverter")
self.python_path = kw.get('python_path', 'python')
self.unomimemapper_bin = kw.get("unomimemapper_bin")
def _start_process(self, command, env):
"""Start OpenOffice.org process"""
self.process = Popen(' '.join(command),
stdout=PIPE,
shell=True,
close_fds=True,
env=env)
waitStartDaemon(self, self.timeout)
if exists(self.document_url):
return self._testOpenOffice(self.hostname, self.port)
return True
def start(self):
"""Start Instance."""
self.path_user_installation = join(self.path_run_dir, \
"cloudooo_instance_%s" % self.port)
if exists(self.path_user_installation):
removeDirectory(self.path_user_installation)
# Create command with all parameters to start the instance
self.command = [join(self.office_bin_path, self._bin_soffice)
, '-invisible'
, '-nologo'
, '-nodefault'
, '-norestore'
, '-nofirststartwizard'
, '"-accept=socket,host=%s,port=%d;urp;StarOffice.ComponentContext"' % \
(self.hostname, self.port)
, '-env:UserInstallation=file://%s' % self.path_user_installation]
# To run the instance OOo is need a environment. So, the "DISPLAY" of Xvfb
# is passed to env and the environment customized is passed to the process
env = environ.copy()
env["DISPLAY"] = ":%s" % self.display_id
process_started = self._start_process(self.command, env)
if not process_started:
self.stop()
waitStopDaemon(self, self.timeout)
self._start_process(self.command, env)
self._cleanRequest()
Application.start(self)
def stop(self):
"""Stop the instance by pid. By the default
the signal is 15."""
Application.stop(self)
if exists(self.path_user_installation):
removeDirectory(self.path_user_installation)
self._cleanRequest()
def isLocked(self):
"""Verify if OOo instance is being used."""
return self._lock.locked()
def acquire(self):
"""Lock Instance to use."""
self.request += 1
self._lock.acquire()
def release(self):
"""Unlock Instance."""
logger.debug("OpenOffice %s, %s unlocked" % self.getAddress())
self._lock.release()
openoffice = OpenOffice()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from subprocess import Popen, PIPE
from cloudooo.utils import logger, waitStartDaemon, remove_file
from zope.interface import implements
from application import Application
from cloudooo.interfaces.application import IApplication
from os.path import exists
class Xvfb(Application):
"""Start and control Xvfb. It is used to open/run
instances OpenOffice.
"""
implements(IApplication)
name = "xvfb"
def loadSettings(self, hostname, port, path_run_dir, display_id, **kwargs):
"""Method to load the configuration to run and monitoring the Xvfb.
Keyword Arguments:
virtual_screen -- the display number
"""
Application.loadSettings(self, hostname, port, path_run_dir, display_id)
self.virtual_screen = kwargs.get('virtual_screen', '0')
self.process_name = "Xvfb"
def start(self):
"""Method to start Virtual Frame Buffer."""
self.command = ["Xvfb", "-ac", ":%s" % self.display_id, \
"-screen", self.virtual_screen, "800x600x16", \
"-fbdir", self.path_run_dir]
self.process = Popen(self.command,
stdout=PIPE,
close_fds=True)
waitStartDaemon(self, self.timeout)
Application.start(self)
logger.debug("Xvfb pid - %s" % self.pid())
def stop(self):
"""Stop Xvfb processes and remove lock file in file system"""
Application.stop(self)
lock_filepath = '/tmp/.X%s-lock' % self.display_id
if exists(lock_filepath):
remove_file(lock_filepath)
display_filepath = '/tmp/X11/X%s' % self.display_id
if exists(display_filepath):
remove_file(display_filepath)
xvfb = Xvfb()
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import sys
import jsonpickle
from types import UnicodeType, InstanceType
from os import environ
from os.path import dirname
from tempfile import mktemp
from getopt import getopt, GetoptError
from cloudooo import ooolib
from cloudooo.utils import usage
__doc__ = """
usage: unoconverter [options]
Options:
-h, --help this help screen
--test Test if the Openoffice works correctly
--hostname=STRING OpenOffice Instance address
--port=STRING OpenOffice Instance port
--document_url=STRING_URL
URL of document to load in OpenOffice
--office_bin_path=STRING_URL
Folder path were is the binary openoffice
--uno_path=STRING_URL
Folter path were is the uno library
--destination_format=STRING
extension to export the document
--mimemapper=OBJECT_SERIALIZED
Mimemapper serialized. The object is passed using
jsonpickle. IF this option is None, the object is
created
--metadata=DICT_SERIALIZED
Dictionary with metadata
"""
class UnoConverter(object):
"""A module to easily work with OpenOffice.org."""
def __init__(self, hostname, port, document_url, **kw):
""" """
self.hostname = hostname
self.port = port
self.document_url = document_url
self.document_dir_path = dirname(document_url)
self.source_format = kw.get('source_format')
self._load()
def _getPropertyToExport(self, destination_format=None):
"""Create the property according to the extension of the file."""
if destination_format and self.document_loaded:
doc_type_list = mimemapper._doc_type_list_by_extension.get(destination_format)
if self.document_type not in doc_type_list:
raise AttributeError, \
"This Document can not be converted for this format"
type = self.document_type
filter_name = mimemapper.getFilterName(destination_format, type)
property_list = []
property = ooolib.createProperty("Overwrite", True)
property_list.append(property)
property = ooolib.createProperty("FilterName", filter_name)
property_list.append(property)
return property_list
else:
return ()
def _load(self):
"""Create one document with basic properties"""
service_manager = ooolib.getServiceManager(self.hostname, self.port)
desktop = service_manager.createInstance("com.sun.star.frame.Desktop")
uno_url = ooolib.systemPathToFileUrl(self.document_url)
uno_document = desktop.loadComponentFromURL(uno_url, "_blank", 0, ())
module_manager = service_manager.createInstance("com.sun.star.frame.ModuleManager")
if not uno_document:
raise AttributeError, "This document can not be loaded or is empty"
self.document_type = module_manager.identify(uno_document)
self.document_loaded = uno_document
def convert(self, destination_format=None):
""" """
output_url = mktemp(suffix='.%s' % destination_format,
dir=self.document_dir_path)
property_list = self._getPropertyToExport(destination_format)
try:
self.document_loaded.storeToURL(ooolib.systemPathToFileUrl(output_url),
tuple(property_list))
finally:
self.document_loaded.dispose()
return output_url
def getMetadata(self):
"""Extract all metadata of the document"""
metadata = {}
document_info = self.document_loaded.getDocumentInfo()
property_list = [prop.Name for prop in document_info.getPropertyValues() \
if prop.Value]
for property_name in iter(property_list):
property = document_info.getPropertyValue(property_name)
if type(property) == UnicodeType:
metadata[property_name] = property
elif type(property) == InstanceType:
if property.typeName == 'com.sun.star.util.DateTime':
datetime = "%s/%s/%s %s:%s:%s" % (property.Day, property.Month,
property.Year, property.Hours, property.Minutes, property.Seconds)
metadata[property_name] = datetime
for number in range(document_info.getUserFieldCount()):
field_value_str = document_info.getUserFieldValue(number)
if field_value_str:
fieldname = document_info.getUserFieldName(number)
metadata[fieldname] = field_value_str
service_manager = ooolib.getServiceManager(self.hostname, self.port)
uno_file_access = service_manager.createInstance("com.sun.star.ucb.SimpleFileAccess")
doc = uno_file_access.openFileRead(ooolib.systemPathToFileUrl(self.document_url))
property_list = []
property = ooolib.createProperty("InputStream", doc)
property_list.append(property)
type_detection = service_manager.createInstance("com.sun.star.document.TypeDetection")
filter_name = type_detection.queryTypeByDescriptor(tuple(property_list), \
True)[0]
doc.closeInput()
metadata['MIMEType'] = mimemapper.getMimetypeByFilterType(filter_name)
return metadata
def setMetadata(self, metadata):
"""Returns a document with new metadata.
Keyword arguments:
metadata -- expected an dictionary with metadata.
"""
doc_info = self.document_loaded.getDocumentInfo()
prop_name_list = [prop.Name for prop in doc_info.getPropertyValues()]
user_info_counter = 0
for prop, value in metadata.iteritems():
if prop in prop_name_list:
doc_info.setPropertyValue(prop, value)
else:
max_index = doc_info.getUserFieldCount()
for index in range(max_index):
if doc_info.getUserFieldName(index).lower() == prop.lower():
doc_info.setUserFieldValue(index, value)
break
else:
doc_info.setUserFieldName(user_info_counter, prop)
doc_info.setUserFieldValue(user_info_counter, value)
user_info_counter += 1
self.document_loaded.store()
self.document_loaded.dispose()
def help():
usage(sys.stderr, __doc__)
sys.exit(1)
def main():
global mimemapper
help_msg = "\nUse --help or -h"
try:
opt_list, arg_list = getopt(sys.argv[1:], "h", ["help", "test",
"convert", "getmetadata", "setmetadata",
"uno_path=", "office_bin_path=",
"hostname=", "port=", "source_format=",
"document_url=", "destination_format=",
"mimemapper=", "metadata=",
"unomimemapper_bin=", "python_path="])
except GetoptError, msg:
msg = msg.msg + help_msg
usage(sys.stderr, msg)
sys.exit(2)
param_list = [tuple[0] for tuple in opt_list]
for opt, arg in opt_list:
if opt in ('-h', '--help'):
help()
elif opt == '--hostname':
hostname = arg
elif opt == '--port':
port = arg
elif opt == '--document_url':
document_url = arg
elif opt == '--office_bin_path':
environ['office_bin_path'] = arg
office_bin_path = arg
elif opt == '--uno_path':
environ['uno_path'] = arg
uno_path = arg
elif opt == '--destination_format':
destination_format = arg
elif opt == '--source_format':
source_format = arg
elif opt == '--metadata':
metadata = jsonpickle.decode(arg)
elif opt == '--mimemapper':
mimemapper = jsonpickle.decode(arg)
elif opt == '--unomimemapper_bin':
unomimemapper_bin = arg
elif opt == '--python_path':
python_path = arg
kw = {}
if "mimemapper" not in globals() and "--setmetadata" not in param_list:
from cloudooo.mimemapper import mimemapper
if "uno_path" in locals():
kw['uno_path'] = uno_path
if "office_bin_path" in locals():
kw['office_bin_path'] = office_bin_path
if "unomimemapper_bin" in locals():
kw['unomimemapper_bin'] = unomimemapper_bin
if "python_path" in locals():
kw['python_path'] = python_path
mimemapper.loadFilterList(hostname=hostname, port=port, **kw)
kw.clear()
if 'source_format' in locals():
kw['source_format'] = source_format
unoconverter = UnoConverter(hostname, port, document_url, **kw)
if "--test" in param_list:
output = unoconverter.convert("pdf")
if "--convert" in param_list and not '--getmetadata' in param_list \
and 'destination_format' not in locals():
output = unoconverter.convert()
elif '--convert' in param_list and 'destination_format' in locals():
output = unoconverter.convert(destination_format)
elif '--getmetadata' in param_list and not '--convert' in param_list:
output = unoconverter.getMetadata()
elif '--getmetadata' in param_list and '--convert' in param_list:
output = unoconverter.getMetadata()
output['Data'] = unoconverter.convert()
elif '--setmetadata' in param_list:
unoconverter.setMetadata(metadata)
output = document_url
print output
if "__main__" == __name__:
main()
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import sys
from os import environ
from getopt import getopt, GetoptError
from cloudooo.ooolib import getServiceManager
from cloudooo.utils import usage
from types import InstanceType
__doc__ = """
usage: unomimemapper.py [options]
Options:
-h, --help this help screen
--hostname=STRING OpenOffice Instance address
--port=STRING OpenOffice Instance port
--office_bin_path=STRING_URL
Folder path were is the binary openoffice
--uno_path=STRING_URL
Folter path were is the uno library
"""
class UnoMimemapper(object):
""" """
def __init__(self, hostname, port):
""" Receives hostname and port from openoffice and create a service manager"""
self.service_manager = getServiceManager(hostname, port)
def _getElementNameByService(self, uno_service, ignore_name_list=[]):
"""Returns an dict with elements."""
name_list = uno_service.getElementNames()
service_dict = {}
for name in iter(name_list):
element_dict = {}
element_list = uno_service.getByName(name)
for obj in iter(element_list):
if obj.Name in ignore_name_list:
continue
elif type(obj.Value) == InstanceType:
continue
element_dict[obj.Name] = obj.Value
service_dict[name] = element_dict
return service_dict
def getFilterDict(self):
"""Return all filters and your properties"""
filter_service = self.service_manager.createInstance("com.sun.star.document.FilterFactory")
filter_dict = self._getElementNameByService(filter_service, ["UINames", "UserData"])
return filter_dict
def getTypeDict(self):
"""Return all types and your properties"""
type_service = self.service_manager.createInstance("com.sun.star.document.TypeDetection")
type_dict = self._getElementNameByService(type_service, ["UINames", "URLPattern"])
return type_dict
def help():
usage(sys.stderr, __doc__)
sys.exit(1)
def main():
try:
opt_list, arg_list = getopt(sys.argv[1:], "h", ["help",
"uno_path=", "office_bin_path=",
"hostname=", "port="])
except GetoptError, msg:
msg = msg.msg + "\nUse --help or -h"
usage(sys.stderr, msg)
sys.exit(2)
if not opt_list:
help()
for opt, arg in opt_list:
if opt in ("-h", "--help"):
help()
if opt == "--uno_path":
environ['uno_path'] = arg
elif opt == "--office_bin_path":
environ['office_bin_path'] = arg
elif opt == '--hostname':
hostname = arg
elif opt == "--port":
port = arg
mimemapper = UnoMimemapper(hostname, port)
filter_dict = mimemapper.getFilterDict()
type_dict = mimemapper.getTypeDict()
print "filter_dict = %s\ntype_dict = %s" % (filter_dict, type_dict)
if "__main__" == __name__:
main()
#!/bin/sh
lsb_functions="/etc/rc.d/init.d/functions"
if test -f $lsb_functions ; then
. $lsb_functions
else
log_success_msg()
{
echo " SUCCESS! $@"
}
log_failure_msg()
{
echo " ERROR! $@"
}
fi
# complete path of the cloudooo folder
FOLDER=$(cd $(dirname $0); pwd -P)/
# get the pid of the cloudooo
CLOUDOOO_PID=$FOLDER/paster.pid
# complete path of this script
SELF=$FOLDER$(basename $0)
case "$1" in
start)
paster serve ./samples/cloudooo.conf --daemon;;
stop)
kill -1 `cat $CLOUDOOO_PID`;
paster serve ./samples/cloudooo.conf --stop-daemon;;
restart)
$SELF stop;
$SELF start;;
*)
echo "Usage: ./cloudooo {start|stop|restart}";
exit 1;;
esac
exit 0
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from signal import signal, SIGHUP
from application.openoffice import openoffice
from application.xvfb import xvfb
from wsgixmlrpcapplication import WSGIXMLRPCApplication
from utils import convertStringToBool, configureLogger, cleanDirectory
from os import path
import monitor
from mimemapper import mimemapper
import gc
def stopProcesses():
monitor.stop()
xvfb.stop()
openoffice.stop()
def application(global_config, **local_config):
"""Method to load all configuration of cloudooo and start the application.
To start the application a number of params are required:
Keyword arguments:
debug_mode -- Mode as the application prints the messages.
e.g debug_mode=logging.DEBUG
path_dir_run_cloudooo -- Full path to create the environment of the processes.
e.g path_dir_run_cloudooo='/var/run/cloudooo'
virtual_display_port -- Port to start the Xvfb.
virtual_display_id -- Sets the display.
e.g virtual_display_id='99'
application_hostname -- Sets the host to Xvfb and Openoffice.
virtual_screen -- Use to define the screen to Xvfb
e.g virtual_screen='0'
number_instances_openoffice -- Defines a number of OOo Instances.
pool_port_range_start -- Initial number to start all OOo Instances.
e.g pool_port_range_start=4060
office_bin_path -- Full Path of the OOo executable.
e.g office_bin_path='/opt/openoffice.org3/program'
uno_path -- Full path to pyuno library.
e.g uno_path='/opt/openoffice.org/program'
"""
gc.enable()
debug_mode = convertStringToBool(local_config.get('debug_mode'))
configureLogger(debug_mode=debug_mode)
document_name = local_config.get("document_name")
# path of directory to run cloudooo
path_dir_run_cloudooo = local_config.get('path_dir_run_cloudooo')
cleanDirectory(path_dir_run_cloudooo, ignore_list=["tmp", document_name])
# directory to create temporary files
cloudooo_path_tmp_dir = path.join(path_dir_run_cloudooo, 'tmp')
cleanDirectory(cloudooo_path_tmp_dir)
# The Xvfb will run in the same local of the OpenOffice
application_hostname = local_config.get('application_hostname')
openoffice_port = int(local_config.get('openoffice_port'))
# Before start Xvfb, first loads the configuration
xvfb.loadSettings(application_hostname,
int(local_config.get('virtual_display_port')),
path_dir_run_cloudooo,
local_config.get('virtual_display_id'),
virtual_screen=local_config.get('virtual_screen'),
start_timeout=local_config.get('start_timeout'))
xvfb.start()
document_url = path.join(path.dirname(__file__),
"tests/data/%s" % document_name)
# Loading Configuration to start OOo Instance and control it
openoffice.loadSettings(application_hostname,
openoffice_port,
path_dir_run_cloudooo,
local_config.get('virtual_display_id'),
local_config.get('office_bin_path'),
local_config.get('uno_path'),
document_url=document_url,
unoconverter_bin=local_config.get('unoconverter_bin'),
python_path=local_config.get('python_path'),
unomimemapper_bin=local_config.get('unomimemapper_bin'))
openoffice.start()
monitor.load(local_config)
# Signal to stop all processes
signal(SIGHUP, lambda x,y: stopProcesses())
# Load all filters
openoffice.acquire()
mimemapper.loadFilterList(application_hostname,
openoffice_port,
unomimemapper_bin=local_config.get('unomimemapper_bin'),
python_path=local_config.get('python_path'))
openoffice.release()
from manager import Manager
timeout_response = int(local_config.get('timeout_response'))
kw = dict(timeout=timeout_response,
unoconverter_bin=local_config.get('unoconverter_bin'),
python_path=local_config.get('python_path'))
cloudooo_manager = Manager(cloudooo_path_tmp_dir, **kw)
return WSGIXMLRPCApplication(instance=cloudooo_manager)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import mimetypes
from os.path import join, exists, curdir, abspath
from os import listdir, remove, chdir
from zope.interface import implements
from interfaces.document import IDocument
from zipfile import ZipFile, is_zipfile
from shutil import rmtree
import tempfile
class FileSystemDocument(object):
"""Filesystem Document is used to manipulate one temporary document
stored into the filesystem.
"""
implements(IDocument)
def __init__(self, base_folder_url, data, source_format):
"""Create an document into file system and store the URL.
Keyword arguments:
base_folder_url -- Full path to create a temporary folder
data -- Content of the document
"""
self.base_folder_url = base_folder_url
self.directory_name = self._createDirectory()
self.original_data = data
self.source_format = source_format
self.url = self.load()
def _createDirectory(self):
return tempfile.mkdtemp(dir=self.base_folder_url)
def load(self):
"""Creates one Temporary Document and write data into it.
Return the url for the document.
"""
# creates only the url of the file.
file_path = tempfile.mktemp(suffix=".%s" % self.source_format,
dir=self.directory_name)
# stores the data in temporary file
open(file_path, 'wb').write(self.original_data)
# If is a zipfile is need extract all files from whitin the compressed file
if is_zipfile(file_path):
zipfile = ZipFile(file_path)
if 'mimetype' not in zipfile.namelist() and \
'[Content_Types].xml' not in zipfile.namelist():
zipfile.extractall(path=self.directory_name)
zipfile.close()
remove(file_path)
file_list = listdir(self.directory_name)
if 'index.html' in file_list:
file_path = join(self.directory_name, 'index.html')
else:
extension_list = ['text/html', 'application/xhtml+xml']
for file in file_list:
if mimetypes.guess_type(file)[0] in extension_list:
file_path = join(self.directory_name, file)
break
return file_path
def getContent(self, zip=False):
"""Open the file and returns the content.
Keyword Arguments:
zip -- Boolean attribute. If True"""
if zip:
current_dir_url = abspath(curdir)
chdir(self.directory_name)
zip_path = tempfile.mktemp(suffix=".zip", dir=self.directory_name)
file_list = listdir(self.directory_name)
zipfile = ZipFile(zip_path, 'w')
try:
for file in iter(file_list):
zipfile.write(file)
finally:
zipfile.close()
opened_zip = open(zip_path, 'r').read()
remove(zip_path)
chdir(current_dir_url)
return opened_zip
else:
return open(self.url, 'r').read()
def getUrl(self):
"""Returns full path."""
return self.url
def restoreOriginal(self):
"""Remake the document with the original document"""
self.trash()
self.directory_name = self._createDirectory()
self.url = self.load()
def reload(self, url=None):
"""If the first document is temporary and another document is created. Use
reload to load the new document.
Keyword Arguments:
url -- Full path of the new document(default None)
"""
if self.url != url and url is not None:
remove(self.url)
self.url = url
def trash(self):
"""Remove the file in file system."""
if exists(self.directory_name):
rmtree(self.directory_name)
self.url = None
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import implements
from interfaces.filter import IFilter
class Filter(object):
"""Filter of OOo."""
implements(IFilter)
def __init__(self, extension, filter, mimetype, document_service, **kwargs):
"""Receives extension, filter and mimetype of filter and saves in object.
"""
self._extension = extension
self._filter = filter
self._mimetype = mimetype
self._document_service = document_service
self._preferred = kwargs.get('preferred')
self._sort_index = kwargs.get('sort_index')
self._label = kwargs.get("label")
def getLabel(self):
"""Returns label."""
return self._label
def getSortIndex(self):
"""Returns sort index."""
return self._sort_index
def isPreferred(self):
"""Check if this filter is preferred."""
return self._preferred
def getName(self):
"""Returns name of filter."""
return self._filter
def getDocumentService(self):
"""Returns the type of document that can use this filter."""
return self._document_service
def getExtension(self):
"""Returns extension as string."""
return self._extension
def getMimetype(self):
"""Returns mimetype."""
return self._mimetype
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
# Rafael Monnerat <rafael@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import implements
from cloudooo.interfaces.handler import IHandler
from cloudooo.document import FileSystemDocument
class FFMpegHandler:
"""
For each Document inputed is created on instance of this class to manipulate
the document. This Document must be able to create and remove a temporary
document at FS, load, export and export.
"""
implements(IHandler)
def __init__(self, base_folder_url, data, **kw):
"""Creates document in file system and loads it in OOo."""
self.document = FileSystemDocument(base_folder_url, data)
def _getCommand(self, *args, **kw):
"""Transforms all parameters passed in a command"""
return ""
def convert(self, source_format=None, destination_format=None, **kw):
"""Executes a procedure in accordance with parameters passed."""
return ""
def getMetadata(self, converted_data=False):
"""Returns a dictionary with all metadata of document.
Keywords Arguments:
converted_data -- Boolean variable. if true, the document is also returned
along with the metadata."""
return {}
def setMetadata(self, metadata):
"""Returns a document with new metadata.
Keyword arguments:
metadata -- expected an dictionary with metadata.
"""
# Is it supportable?
return ""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
# Rafael Monnerat <rafael@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import implements
from cloudooo.interfaces.handler import IHandler
from cloudooo.document import FileSystemDocument
class ImageMagickHandler(FileSystemDocument):
"""
For each Document inputed is created on instance of this class to manipulate
the document. This Document must be able to create and remove a temporary
document at FS, load, export and export.
"""
implements(IHandler)
def __init__(self, base_folder_url, data, **kw):
"""Creates document in file system and loads it in OOo."""
self.document = FileSystemDocument(base_folder_url, data)
def _getCommand(self, *args, **kw):
"""Transforms all parameters passed in a command"""
return ""
def convert(self, source_format=None, destination_format=None, **kw):
"""Executes a procedure in accordance with parameters passed."""
return ""
def getMetadata(self, converted_data=False):
"""Returns a dictionary with all metadata of document.
Keywords Arguments:
converted_data -- Boolean variable. if true, the document is also returned
along with the metadata."""
return {}
def setMetadata(self, metadata):
"""Returns a document with new metadata.
Keyword arguments:
metadata -- expected an dictionary with metadata.
"""
# Is it supportable?
return ""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import jsonpickle
from os import environ
from subprocess import Popen, PIPE
from cloudooo.application.openoffice import openoffice
from zope.interface import implements
from cloudooo.interfaces.handler import IHandler
from cloudooo.mimemapper import mimemapper
from cloudooo.document import FileSystemDocument
from cloudooo.monitor.timeout import MonitorTimeout
from cloudooo.utils import logger
from psutil import pid_exists
class OOHandler:
"""OOHandler is used to access the one Document and OpenOffice.
For each Document inputed is created on instance of this class to manipulate
the document. This Document must be able to create and remove a temporary
document at FS, load, export and export.
"""
implements(IHandler)
def __init__(self, base_folder_url, data, source_format, **kw):
"""Creates document in file system and loads it in OOo."""
self.document = FileSystemDocument(base_folder_url,
data,
source_format)
self.zip = kw.get('zip', False)
self.uno_path = kw.get("uno_path", None)
self.office_bin_path = kw.get("office_bin_path", None)
self.timeout = kw.get("timeout", 600)
self.unoconverter_bin = kw.get('unoconverter_bin', "unoconverter")
self.source_format = source_format
self.python_path = kw.get('python_path', 'python')
if not self.uno_path:
self.uno_path = environ.get("uno_path")
if not self.office_bin_path:
self.office_bin_path = environ.get("office_bin_path")
def _getCommand(self, *args, **kw):
"""Transforms all parameters passed in a command"""
hostname, port = openoffice.getAddress()
kw['hostname'] = hostname
kw['port'] = port
command_list = [self.python_path
, self.unoconverter_bin
, "--uno_path='%s'" % self.uno_path
, "--office_bin_path='%s'" % self.office_bin_path
, "--document_url='%s'" % self.document.getUrl()]
for arg in args:
command_list.insert(2, "--%s" % arg)
for k, v in kw.iteritems():
command_list.append("--%s='%s'" % (k,v))
return ' '.join(command_list)
def _startTimeout(self):
"""start the Monitor"""
self.monitor = MonitorTimeout(openoffice, self.timeout)
self.monitor.start()
return
def _stopTimeout(self):
"""stop the Monitor"""
self.monitor.terminate()
return
def _subprocess(self, command):
"""Run one procedure"""
try:
self._startTimeout()
process = Popen(command,
shell=True,
stdout=PIPE,
stderr=PIPE,
close_fds=True)
stdout, stderr = process.communicate()
finally:
self._stopTimeout()
if pid_exists(process.pid):
process.terminate()
return stdout, stderr
def _callUnoConverter(self, *feature_list, **kw):
""" """
if not openoffice.status():
openoffice.start()
command = self._getCommand(*feature_list, **kw)
stdout, stderr = self._subprocess(command)
if not stdout and stderr != '':
logger.debug(stderr)
if "NoConnectException" in stderr or \
"RuntimeException" in stderr or \
"DisposedException" in stderr:
self.document.restoreOriginal()
openoffice.restart()
kw['document_url'] = self.document.getUrl()
command = self._getCommand(*feature_list, **kw)
stdout, stderr = self._subprocess(command)
elif "ErrorCodeIOException" in stderr:
raise Exception, stderr + \
"This document can not be converted to this format"
else:
raise Exception, stderr
return stdout, stderr
def convert(self, destination_format=None, **kw):
"""Convert a document to another format supported by the OpenOffice
Keyword Arguments:
destination_format -- extension of document as String
"""
logger.debug("Convert: %s > %s" % (self.source_format, destination_format))
openoffice.acquire()
kw['source_format'] = self.source_format
if destination_format:
kw['destination_format'] = destination_format
kw['mimemapper'] = jsonpickle.encode(mimemapper)
try:
stdout, stderr = self._callUnoConverter(*['convert'], **kw)
finally:
openoffice.release()
if self.monitor.is_alive():
self._stopTimeout()
url = stdout.replace('\n', '')
self.document.reload(url)
content = self.document.getContent(self.zip)
self.document.trash()
return content
def getMetadata(self, base_document=False):
"""Returns a dictionary with all metadata of document.
Keywords Arguments:
base_document -- Boolean variable. if true, the document is also returned
along with the metadata."""
openoffice.acquire()
mimemapper_pickled = jsonpickle.encode(mimemapper)
logger.debug("getMetadata")
kw = dict(mimemapper=mimemapper_pickled)
if base_document:
feature_list = ['getmetadata', 'convert']
else:
feature_list = ['getmetadata']
try:
stdout, stderr = self._callUnoConverter(*feature_list, **kw)
finally:
openoffice.release()
if self.monitor.is_alive():
self._stopTimeout()
metadata={}
exec("metadata=%s" % stdout)
if metadata.get("Data"):
self.document.reload(metadata['Data'])
metadata['Data'] = self.document.getContent()
else:
metadata['Data'] = ''
self.document.trash()
return metadata
def setMetadata(self, metadata):
"""Returns a document with new metadata.
Keyword arguments:
metadata -- expected an dictionary with metadata.
"""
openoffice.acquire()
metadata_pickled = jsonpickle.encode(metadata)
logger.debug("setMetadata")
kw = dict(metadata=metadata_pickled)
try:
stdout, stderr = self._callUnoConverter(*['setmetadata'], **kw)
finally:
openoffice.release()
if self.monitor.is_alive():
self._stopTimeout()
doc_loaded = self.document.getContent()
self.document.trash()
return doc_loaded
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
class IApplication(Interface):
"""Controls and monitors an application"""
def start():
"""Start the application"""
def stop():
"""Stop the application"""
def pid():
"""Get the process pid"""
def loadSettings(hostname,
port,
virtual_display_id,
path_dir_run_cloudooo,
office_bin_path):
"""Load configuration to control OOo Instances"""
def status():
"""Checks if the application works correctly"""
def restart():
"""Restarts the application with the same settings"""
def getAddress():
"""Returns the hostname and port in tuple"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
from zope.interface import Attribute
class IDocument(Interface):
"""Manipulates documents in file system"""
base_folder_url = Attribute("Url of folder that is used to create temporary files")
directory_name = Attribute("String of directory name")
source_format = Attribute("Document Extension")
url = Attribute("Complete path of document in file system")
original_data = Attribute("Original data")
def load():
"""From the data creates one archive into file system using original data"""
def reload(url):
"""In the case of another file with the same content be created, pass the
url to load the new file"""
def getContent(zip):
"""Returns data of document"""
def getUrl():
"""Get the url of temporary file in file system"""
def trash():
"""Remove the file in file system"""
def restoreOriginal():
"""Restore the document with the original document"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
class IFilter(Interface):
"""This is a type filter"""
def getName():
"""Returns filter as string"""
def getDocumentService():
"""Returns document_service"""
def getSortIndex():
"""Returns the calculated value"""
def isPreferred():
"""Returns boolean variable"""
def getLabel():
"""Returns label of filter"""
def getMimetype():
"""Returns mimetype as string"""
def getExtension():
"""Returns extension as string"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
class IHandler(Interface):
"""Handles connections with the openoffice by socket"""
def convert(destination_format):
"""Convert document to ODF"""
def getMetadata(converted_data):
"""Returns a dictionary with all metadata of document. If converted_data is
True, the document is added in dictionary."""
def setMetadata(metadata_dict):
"""Returns a document with the new metadata"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
from zope.interface import Attribute
class ILockable(Interface):
"""Controls concurrency control"""
_lock = Attribute("A primitive lock is in one of two states, 'locked' or \
'unlocked'.")
def acquire():
"""Acquire the lock."""
def release():
"""Release the lock. If this thread doesn't currently have the lock, an
assertion error is raised."""
def isLocked():
"""Boolean method that checks if the object is acquired(locked) or not"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
class IManager(Interface):
"""Provides method to manipulate documents and metadatas using OOo"""
def convertFile(file, source_format, destination_format, zip):
"""Returns the converted file in the given format.
zip parameter can be specified to return the result of conversion
in the form of a zip archive (which may contain multiple parts).
This can be useful to convert a single ODF file to HTML
and png images.
"""
def getFileMetadataItemList(file, source_format, base_document):
"""Returns a list key, value pairs representing the
metadata values for the document. The structure of this
list is "unpredictable" and follows the convention of each file.
"""
def updateFileMetadata(file, source_format, metadata_dict):
"""Updates the file in the given source_format with provided metadata and
return the resulting new file."""
def getAllowedExtensionList(request_dict):
"""Returns a list extension which can be generated from given extension or
document type."""
class IERP5Compatibility(Interface):
""" Provides compatibility interface with ERP5 Project.
This methods provide same API as OpenOffice.org project.
XXX Unfinished Docstring.
"""
def run_convert(filename, file):
"""Returns the metadata and the ODF in dictionary"""
return (200 or 402, dict(), '')
def run_setmetadata(filename, file, metadata_dict):
"""Adds metadata in ODF and returns a new ODF with metadata in
dictionary"""
return (200 or 402, dict(), '')
def run_getmetadata(self, filename, file):
"""Extracts metadata from ODF and returns in dictionary"""
return (200 or 402, dict(), '')
def run_generate(filename, file, something, format, orig_format):
"""It exports a ODF to given format"""
return (200 or 402, dict(), '')
def getAllowedTargetItemList(self, content_type):
"""List types which can be generated from given content type"""
return (200 or 402, dict(), '')
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
class IMimemapper(Interface):
"""Provide methods to manipulate filters of OOo."""
def isLoaded():
"""Returns if the filters were loaded."""
def getDocumentTypeDict():
"""Returns document type dict."""
def getFilterName(extension, document_type):
"""Returns the name of filter according to parematers passed."""
def loadFilterList(**kwargs):
"""Load all filters of openoffice."""
def getFilterList(extension, **kwargs):
"""Returns a filter list according to extension or other parameters passed.
"""
def getAllowedExtensionList(document_type, **kwargs):
"""Returns a list with extensions which can be used to export according to
document type passed."""
def getMimetypeByFilterType(filter_type):
"""Returns mimetype according to the filter type passed"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import Interface
class IMonitor(Interface):
"""Provides features to monitor processes or python objects"""
def run():
"""Start to monitor"""
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from mimetypes import guess_all_extensions
from base64 import encodestring, decodestring
from zope.interface import implements
from interfaces.manager import IManager, IERP5Compatibility
from handler.oohandler import OOHandler
from mimemapper import mimemapper
from utils import logger
class Manager(object):
"""Manipulates requisitons of client and temporary files in file system."""
implements(IManager, IERP5Compatibility)
def __init__(self, path_tmp_dir, **kw):
"""Need pass the path where the temporary document will be created."""
self._path_tmp_dir = path_tmp_dir
self.kw = kw
def convertFile(self, file, source_format, destination_format, zip=False):
"""Returns the converted file in the given format.
Keywords arguments:
file -- File as string in base64
source_format -- Format of original file as string
destination_format -- Target format as string
zip -- Boolean Attribute. If true, returns the file in the form of a
zip archive
"""
if not mimemapper.getFilterList(destination_format):
raise ValueError, "This format is not supported or is invalid"
self.kw['zip'] = zip
document = OOHandler(self._path_tmp_dir,
decodestring(file),
source_format,
**self.kw)
decode_data = document.convert(destination_format)
return encodestring(decode_data)
def updateFileMetadata(self, file, source_format, metadata_dict):
"""Receives the string of document and a dict with metadatas. The metadata
is added in document.
e.g
self.updateFileMetadata(data = encodestring(data), metadata = \
{"title":"abc","description":...})
return encodestring(document_with_metadata)
"""
document = OOHandler(self._path_tmp_dir,
decodestring(file),
source_format,
**self.kw)
metadata_dict = dict([(key.capitalize(), value) \
for key, value in metadata_dict.iteritems()])
decode_data = document.setMetadata(metadata_dict)
return encodestring(decode_data)
def getFileMetadataItemList(self, file, source_format, base_document=False):
"""Receives the string of document as encodestring and returns a dict with
metadatas.
e.g.
self.getFileMetadataItemList(data = encodestring(data))
return {'Title': 'abc','Description': 'comments', 'Data': None}
If converted_data is True, the ODF of data is added in dictionary.
e.g
self.getFileMetadataItemList(data = encodestring(data), True)
return {'Title': 'abc','Description': 'comments', 'Data': string_ODF}
Note that all keys of the dictionary have the first word in uppercase.
"""
document = OOHandler(self._path_tmp_dir,
decodestring(file),
source_format,
**self.kw)
metadata_dict = document.getMetadata(base_document)
metadata_dict['Data'] = encodestring(metadata_dict['Data'])
return metadata_dict
def getAllowedExtensionList(self, request_dict={}):
"""List types which can be generated from given type
Type can be given as:
- filename extension
- document type ('text', 'spreadsheet', 'presentation' or 'drawing')
e.g
self.getAllowedMimetypeList(dict(document_type="text"))
return extension_list
"""
mimetype = request_dict.get('mimetype')
extension = request_dict.get('extension')
document_type = request_dict.get('document_type')
if mimetype:
allowed_extension_list = []
for ext in guess_all_extensions(mimetype):
ext = ext.replace('.', '')
extension_list = mimemapper.getAllowedExtensionList(extension=ext,
document_type=document_type)
for extension in extension_list:
if extension not in allowed_extension_list:
allowed_extension_list.append(extension)
return allowed_extension_list
elif extension:
extension = extension.replace('.', '')
return mimemapper.getAllowedExtensionList(extension=extension,
document_type=document_type)
elif document_type:
return mimemapper.getAllowedExtensionList(document_type=document_type)
else:
return [('','')]
def run_convert(self, filename, file):
"""Method to support the old API. Wrapper getFileMetadataItemList but
returns a dict.
This is a Backwards compatibility provided for ERP5 Project, in order to
keep compatibility with OpenOffice.org Daemon.
"""
orig_format = filename.split('.')[-1]
try:
response_dict = {}
response_dict['meta'] = self.getFileMetadataItemList(file, orig_format, True)
response_dict['data'] = response_dict['meta']['Data']
del response_dict['meta']['Data']
return (200, response_dict, "")
except Exception, e:
logger.error(e)
return (402, {}, e.args[0])
def run_setmetadata(self, filename, file, meta):
"""Wrapper updateFileMetadata but returns a dict.
This is a Backwards compatibility provided for ERP5 Project, in order to
keep compatibility with OpenOffice.org Daemon.
"""
orig_format = filename.split('.')[-1]
response_dict = {}
try:
response_dict['data'] = self.updateFileMetadata(file, orig_format, meta)
return (200, response_dict, '')
except Exception, e:
logger.error(e)
return (402, {}, e.args[0])
def run_getmetadata(self, filename, file):
"""Wrapper for getFileMetadataItemList.
This is a Backwards compatibility provided for ERP5 Project, in order to
keep compatibility with OpenOffice.org Daemon.
"""
orig_format = filename.split('.')[-1]
response_dict = {}
try:
response_dict['meta'] = self.getFileMetadataItemList(file, orig_format)
return (200, response_dict, '')
except Exception, e:
logger.error(e)
return (402, {}, e.args[0])
def run_generate(self, filename, file, something, format, orig_format):
"""Wrapper convertFile but returns a dict which includes mimetype.
This is a Backwards compatibility provided for ERP5 Project, in order to keep
compatibility with OpenOffice.org Daemon.
"""
orig_format = orig_format.split('.')[-1]
if "htm" in format:
zip = True
else:
zip = False
try:
response_dict = {}
response_dict['data'] = self.convertFile(file, orig_format, format, zip)
response_dict['mime'] = self.getFileMetadataItemList(response_dict['data'],
format)['MIMEType']
return (200, response_dict, "")
except Exception, e:
logger.error(e)
return (402, {}, e.args[0])
def getAllowedTargetItemList(self, content_type):
"""Wrapper getAllowedExtensionList but returns a dict.
This is a Backwards compatibility provided for ERP5 Project, in order to
keep compatibility with OpenOffice.org Daemon.
"""
response_dict = {}
try:
extension_list = self.getAllowedExtensionList({"mimetype": content_type})
response_dict['response_data'] = extension_list
return (200, response_dict, '')
except Exception, e:
logger.error(e)
return (402, {}, e.args[0])
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from re import findall
from subprocess import Popen, PIPE
from zope.interface import implements
from filter import Filter
from os import environ
from interfaces.mimemapper import IMimemapper
from types import InstanceType
class MimeMapper(object):
"""Load all filters from OOo. You can get the of the filter you want or all
filters of the specific extension.
"""
implements(IMimemapper)
def __init__(self):
"""When it is instantiated, it creates a structure to store filters.
And lists to store the tags xml.
"""
self._loaded = False
self._filter_by_extension_dict = {}
self._extension_list_by_type = {}
self._doc_type_list_by_extension = {}
# List of extensions that are ODF
self._odf_extension_list = []
self._mimetype_by_filter_type = {}
self._document_type_dict = {}
def _addFilter(self, filter):
"""Add filter in mimemapper catalog."""
extension = filter.getExtension()
if not self._filter_by_extension_dict.has_key(extension):
self._filter_by_extension_dict[extension] = []
self._filter_by_extension_dict.get(extension).append(filter)
def _typeToDocumentService(self, document_type):
"""Returns the document service according to document type."""
for k, v in self._document_type_dict.iteritems():
if k.startswith(document_type):
return v
def _getElementNameByService(self, uno_service, ignore_name_list=[]):
"""Returns an dict with elements."""
name_list = uno_service.getElementNames()
service_dict = {}
for name in iter(name_list):
element_dict = {}
element_list = uno_service.getByName(name)
for obj in iter(element_list):
if obj.Name in ignore_name_list:
continue
elif type(obj.Value) == InstanceType:
continue
element_dict[obj.Name] = obj.Value
service_dict[name] = element_dict
return service_dict
def isLoaded(self):
"""Verify if filters were loaded"""
return self._loaded
def loadFilterList(self, hostname, port, **kw):
"""Load all filters of openoffice.
Keyword arguments:
hostname -- host of OpenOffice
port -- port to connects by socket
**kw:
uno_path -- full path to uno library
office_bin_path -- full path to openoffice binary
"""
# Filters that has flag in bad_flag_list is ignored.
# XXX - Is not good way to remove unnecessary filters
# XXX - try find a good way to remove filters that are not used for export
bad_flag_list = [65, 94217, 536641, 1572929, 268959937, 524373, 85, 524353]
self.python_path = kw.get("python_path", "python")
self.unomimemapper_bin = kw.get("unomimemapper_bin",
"/usr/bin/unomimemapper.py")
uno_path = kw.get("uno_path", environ.get('uno_path'))
office_bin_path = kw.get("office_bin_path", environ.get('office_bin_path'))
command = [self.python_path
, self.unomimemapper_bin
, "--uno_path=%s" % uno_path
, "--office_bin_path=%s" % office_bin_path
, "--hostname=%s" % hostname, "--port=%s" % port]
stdout, stderr = Popen(' '.join(command),
stdout=PIPE,
close_fds=True,
shell=True).communicate()
exec(stdout)
for key, value in filter_dict.iteritems():
filter_name = key
flag = value.get("Flags")
if flag in bad_flag_list:
continue
ui_name = value.get('UIName')
filter_type = value.get('Type')
filter_type_dict = type_dict.get(filter_type)
if not ui_name:
ui_name = filter_type_dict.get("UIName")
extension_list = filter_type_dict.get("Extensions")
mimetype = filter_type_dict.get("MediaType")
if not (extension_list and mimetype):
continue
preferred = filter_type_dict.get("Preferred")
document_service_str = value.get('DocumentService')
sort_index = flag
doc_type = document_service_str.split('.')[-1]
split_type_list = findall(r'[A-Z][a-z]+', doc_type)
if len(split_type_list) > 2:
doc_type = ''.join(split_type_list[:2]).lower()
else:
doc_type = split_type_list[0].lower()
if doc_type not in self._document_type_dict:
self._document_type_dict[doc_type] = document_service_str
if not self._mimetype_by_filter_type.has_key(filter_type):
self._mimetype_by_filter_type[filter_type] = mimetype
# Create key with empty list by document_type in dictonary
# e.g: {'com.sun.star.text.TextDocument': [] }
if not self._extension_list_by_type.has_key(document_service_str):
self._extension_list_by_type[document_service_str] = []
for ext in iter(extension_list):
# All mimetypes that starts with "application/vnd.oasis.opendocument" are
# ODF.
if mimetype.startswith("application/vnd.oasis.opendocument"):
if not ext in self._odf_extension_list:
self._odf_extension_list.append(ext)
# Create key with empty list by extension in dictonary
# e.g: {'pdf': [] }
if not self._doc_type_list_by_extension.has_key(ext):
self._doc_type_list_by_extension[ext] = []
# Adds a tuple with extension and ui_name in document_type key. If
# tuple exists in list is not added.
# e.g {'com.sun.star.text.TextDocument': [('txt', 'Text'),]}
if not (ext, ui_name) in self._extension_list_by_type[document_service_str]:
self._extension_list_by_type[document_service_str].append((ext, ui_name))
# Adds a document type in extension key. If document type exists in
# list is not added.
# e.g {'doc': ['com.sun.star.text.TextDocument']}
if not document_service_str in self._doc_type_list_by_extension[ext]:
self._doc_type_list_by_extension[ext].append(document_service_str)
# Creates a object Filter with all attributes
filter = Filter(ext, filter_name, mimetype, document_service_str,
preferred=preferred, sort_index=sort_index, label=ui_name)
# Adds the object in filter_by_extension_dict
self._addFilter(filter)
self._loaded = True
def getMimetypeByFilterType(self, filter_type):
"""Get Mimetype according to the filter type
Keyword arguments:
filter_type -- string of OOo filter
e.g
>>> mimemapper.getMimetypeByFilterType("writer8")
'application/vnd.oasis.opendocument.text'
"""
return self._mimetype_by_filter_type.get(filter_type, u'')
def getFilterName(self, extension, document_service):
"""Get filter name according to the parameters passed.
Keyword arguments:
extension -- expected a string of the file format.
document_type -- expected a string of the document type.
e.g
>>> mimemapper.getFilterName("sdw", "com.sun.star.text.TextDocument")
'StarWriter 3.0'
"""
filter_list = [filter for filter in iter(self.getFilterList(extension)) \
if filter.getDocumentService() == document_service]
if len(filter_list) > 1:
for filter in iter(filter_list):
if filter.isPreferred():
return filter.getName()
else:
sort_index_list = [filter.getSortIndex() \
for filter in iter(filter_list)]
num_max_int = max(sort_index_list)
for filter in iter(filter_list):
if filter.getName().endswith("Export"):
return filter.getName()
for filter in iter(filter_list):
if filter.getSortIndex() == num_max_int:
return filter.getName()
else:
return filter_list[0].getName()
def getFilterList(self, extension):
"""Search filter by extension, and return the filter as string.
Keyword arguments:
extension -- expected a string of the file format.
e.g
>>> mimemapper.getFilterList("doc")
[<filter.Filter object at 0x9a2602c>,
<filter.Filter object at 0x9a21d6c>,
<filter.Filter object at 0x9a261ec>]
"""
return self._filter_by_extension_dict.get(extension, [])
def getAllowedExtensionList(self, extension=None, document_type=None):
"""Returns a list with extensions which can be used to export according to
document type passed.
e.g
>>> mimemapper.getAllowedExtensionList({"extension":"doc"})
or
>>> mimemapper.getAllowedExtensionList({"document_type":"text"})
(('rtf', 'Rich Text Format'),
('htm', 'HTML Document'),)
If both params are passed, document_type is discarded.
"""
allowed_extension_list = []
document_type_list = []
if extension:
document_type_list.extend(self._doc_type_list_by_extension.get(extension, []))
elif document_type:
document_type = self._typeToDocumentService(document_type)
allowed_extension_list.extend(self._extension_list_by_type.get(document_type, []))
# gets list of extensions of each document type if document_type_list isn't
# empty.
for type in iter(document_type_list):
# gets list of extensions with key document type
extension_list = self._extension_list_by_type.get(type)
for ext in iter(extension_list):
if not ext in allowed_extension_list:
allowed_extension_list.append(ext)
return tuple(allowed_extension_list)
mimemapper = MimeMapper()
from request import MonitorRequest
from memory import MonitorMemory
from cloudooo.application.openoffice import openoffice
monitor_request = None
monitor_memory = None
def load(local_config):
"""Start the monitors"""
global monitor_request
monitor_request = MonitorRequest(openoffice,
int(local_config.get('monitor_interval')),
int(local_config.get('limit_number_request')))
monitor_request.start()
if bool(local_config.get('enable_memory_monitor')):
global monitor_memory
monitor_memory = MonitorMemory(openoffice,
int(local_config.get('monitor_interval')),
int(local_config.get('limit_memory_used')))
monitor_memory.start()
return
def stop():
"""Stop all monitors"""
if monitor_request:
monitor_request.terminate()
if monitor_memory:
monitor_memory.terminate()
clear()
def clear():
global monitor_request, monitor_memory
monitor_request = None
monitor_memory = None
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from cloudooo.monitor.monitor import Monitor
from threading import Thread
from psutil import Process
from cloudooo.utils import logger
from time import sleep
class MonitorMemory(Monitor, Thread):
"""Usefull to control the memory and does not allow use it unnecessarily"""
def __init__(self, openoffice, interval, limit_memory_usage):
"""Expects to receive an object that implements the interfaces IApplication
and ILockable, the limit of memory usage that the openoffice can use and the
interval to check the object."""
Monitor.__init__(self, openoffice, interval)
Thread.__init__(self)
self.limit = limit_memory_usage
def create_process(self):
self.process = Process(int(self.openoffice.pid()))
def get_memory_usage(self):
try:
if not hasattr(self, 'process'):
self.create_process()
elif self.process.pid != int(self.openoffice.pid()):
self.create_process()
except TypeError:
logger.debug("OpenOffice is stopped")
return 0
# convert bytes to GB
return sum(self.process.get_memory_info())/(1024*1024)
def run(self):
"""Is called by start function. this function is responsible for
controlling the amount of memory used, and if the process exceeds the limit
it is stopped forcibly
"""
self.status_flag = True
logger.debug("Start MonitorMemory")
while self.status_flag:
if self.get_memory_usage() > self.limit:
logger.debug("Stopping OpenOffice")
self.openoffice.stop()
sleep(self.interval)
logger.debug("Stop MonitorMemory")
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from zope.interface import implements
from cloudooo.interfaces.monitor import IMonitor
class Monitor(object):
""" """
implements(IMonitor)
def __init__(self, openoffice, interval):
"""Expects an openoffice object and the interval"""
self.status_flag = False
self.openoffice = openoffice
self.interval = interval
def terminate(self):
"""Set False in monitor flag"""
self.status_flag = False
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from cloudooo.monitor.monitor import Monitor
from threading import Thread
from cloudooo.utils import logger
from time import sleep
class MonitorRequest(Monitor, Thread):
"""Usefull to control the number of request in Object"""
def __init__(self, openoffice, interval, request_limit):
"""Expects to receive an object that implements the interfaces IApplication
and ILockable, the limit of request that the openoffice can receive and the
interval to check the object."""
Monitor.__init__(self, openoffice, interval)
Thread.__init__(self)
self.request_limit = request_limit
def run(self):
"""Is called by start function"""
self.status_flag = True
logger.debug("Start MonitorRequest")
while self.status_flag:
if self.openoffice.request > self.request_limit:
self.openoffice.acquire()
logger.debug("Openoffice: %s, %s will be restarted" % \
self.openoffice.getAddress())
self.openoffice.restart()
self.openoffice.release()
sleep(self.interval)
logger.debug("Stop MonitorRequest ")
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from cloudooo.monitor.monitor import Monitor
from multiprocessing import Process
from time import sleep
from cloudooo.utils import logger
class MonitorTimeout(Monitor, Process):
"""Monitors and controls the time of use of an object"""
def __init__(self, openoffice, interval):
"""Expects to receive an object that implements the interfaces IApplication
and ILockable. And the interval to check the object."""
Monitor.__init__(self, openoffice, interval)
Process.__init__(self)
def run(self):
"""Start the process"""
port = self.openoffice.getAddress()[-1]
pid = self.openoffice.pid()
logger.debug("Monitoring OpenOffice: Port %s, Pid: %s" % (port, pid))
self.status_flag = True
sleep(self.interval)
if self.openoffice.isLocked():
logger.debug("Stop OpenOffice - Port %s - Pid %s" % (port, pid))
self.openoffice.stop()
def terminate(self):
"""Stop the process"""
Monitor.terminate(self)
Process.terminate(self)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from os import environ, putenv
from sys import path
from os.path import exists
def setUpUnoEnvironment(uno_path=None, office_bin_path=None):
"""Set up the environment to use the uno library and connect with the
openoffice by socket"""
if uno_path is not None:
environ['uno_path'] = uno_path
else:
uno_path = environ.get('uno_path')
if office_bin_path is not None:
environ['office_bin_path'] = office_bin_path
else:
office_bin_path = environ.get('office_bin_path')
# Add in sys.path the path of pyuno
if uno_path not in path:
path.append(uno_path)
fundamentalrc_file = '%s/fundamentalrc' % office_bin_path
if exists(fundamentalrc_file) and \
not environ.has_key('URE_BOOTSTRAP'):
putenv('URE_BOOTSTRAP','vnd.sun.star.pathname:%s' % fundamentalrc_file)
def createProperty(name, value):
"""Create property"""
setUpUnoEnvironment()
from com.sun.star.beans import PropertyValue
property = PropertyValue()
property.Name = name
property.Value = value
return property
def getServiceManager(host, port):
"""Get the ServiceManager from the running OpenOffice.org."""
setUpUnoEnvironment()
import uno
# Get the uno component context from the PyUNO runtime
uno_context = uno.getComponentContext()
# Create the UnoUrlResolver on the Python side.
url_resolver = "com.sun.star.bridge.UnoUrlResolver"
resolver = uno_context.ServiceManager.createInstanceWithContext(url_resolver,
uno_context)
# Connect to the running OpenOffice.org and get its
# context.
uno_connection = resolver.resolve("uno:socket,host=%s,port=%s;urp;StarOffice.ComponentContext" % (host, port))
# Get the ServiceManager object
return uno_connection.ServiceManager
def systemPathToFileUrl(path):
"""Returns a path in uno library patterns"""
setUpUnoEnvironment()
from unohelper import systemPathToFileUrl
return systemPathToFileUrl(path)
[app:main]
use = egg:cloudooo
#
## System config
#
debug_mode = True
# Folder where all cloudooo core files are installed
cloudooo_home = /usr/share/cloudooo
# Folder where pid files, lock files and virtual frame buffer mappings
# are stored. In this folder is necessary create a folder tmp, because this
# folder is used to create all temporary documents.
path_dir_run_cloudooo = /var/run/cloudooo
# Folder where OpenOffice Uno interpreter is installed
uno_path = /opt/openoffice.org3/basis-link/program
# Folder where OpenOffice Binarie is installed
office_bin_path = /opt/openoffice.org3/program
#
## Monitor Settings
#
# Limit to use the Openoffice Instance. if pass of the limit, the instance is
# stopped and another is started.
limit_number_request = 100
# Interval to check the factory
monitor_interval = 10
timeout_response = 180
enable_memory_monitor = True
# Set the limit in MB
# e.g 1000 = 1 GB, 100 = 100 MB
limit_memory_used = 1000
# Filename that will be used to test the connection with openoffice.
document_name = test.odt
#
## OOFactory Settings
#
# The pool consist of several OpenOffice.org instances
openoffice_hostname = localhost
# OpenOffice Port
openoffice_port = 4062
#
## Xvfb Settings
#
# Default port to xvfb
virtual_display_port = 6097
# ID of the virtual display where OOo instances are launched
virtual_display_id = 97
virtual_screen = 0
# Put where is the conversors-bin
unoconverter_bin = /usr/bin/unoconverter.py
unomimemapper_bin = /usr/bin/unomimemapper.py
# Python path e.g /usr/local/bin/python
python_path = /usr/bin/python
[server:main]
use = egg:PasteScript#wsgiutils
host = 0.0.0.0
port = 8011
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SARL and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
require 'xmlrpc/client'
input_filename = '../tests/data/test.doc'
output_filename = '../tests/output/ruby_test.odt'
in_data = File.read input_filename
enc_data = XMLRPC::Base64.encode in_data
server = XMLRPC::Client.new2 'http://localhost:8008'
result = server.call('convert', enc_data)
out_data = XMLRPC::Base64.decode result
out_file = File.open(output_filename, 'w')
out_file.print out_data
out_file.close
data_length = out_data.length
if data_length == 8124 || data_length == 8101
puts data_length
puts "OK"
end
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import sys
from multiprocessing import Process
from os import listdir
from cloudooo.utils import usage
from xmlrpclib import ServerProxy
from os.path import join
from getopt import getopt, GetoptError
from time import ctime, time
from base64 import encodestring
__doc__ = """
usage: python HighTestLoad.py [options]
Options:
-h, --help this help screen
-f folder Full url of folder with documents
-c concurrency Number of clients
-n requests Number of requests
-s server Server Address e.g http://ip:port/
-l filter log Folder to store logs
"""
class Log(object):
"""Object to manipulate the log file"""
def __init__(self, log_path, mode='a'):
"""Open the log file in filesystem"""
self._log = open(log_path, mode)
def msg(self, message):
"""Write the message in file"""
self._log.write(message)
def close(self):
"""Close the file"""
self._log.close()
def flush(self):
"""Flush the internal I/O buffer."""
self._log.flush()
class Client(Process):
"""Represents a client that sends requests to the server. The log file by
default is created in current path, but can be created in another path.
In log are stored:
- Date and time that the client initiated the test;
- Duration of each request;
- Total time of all requets;
- Data and time that the client finished the test;
"""
def __init__(self, address, number_request, folder, **kw):
"""Client constructor
address -- Complete address as string
e.g http://localhost:8008
number_request -- number of request that client will send to the server
folder -- Full path to folder.
e.g '/home/user/documents'
"""
Process.__init__(self)
self.address = address
self.number_of_request = number_request
self.folder = folder
log_filename = kw['log_filename'] or "%s.log" % self.name
log_folder_path = kw.get("log_folder_url", '')
log_path = join(log_folder_path, log_filename)
self.log = Log(log_path, 'w')
def run(self):
""" """
time_list = []
self.log.msg("Test Start: %s\n" % ctime())
proxy = ServerProxy(self.address)
while self.number_of_request:
folder_list = listdir(self.folder)[:self.number_of_request]
for filename in folder_list:
file_path = join(self.folder, filename)
data = encodestring(open(file_path).read())
self.log.msg("Input: %s\n" % file_path)
try:
now = time()
proxy.convertFile(data, 'odt', 'doc')
response_duration = time() - now
self.log.msg("Duration: %s\n" % response_duration)
time_list.append(response_duration)
self.log.flush()
except Exception, err:
self.log.msg("%s\n" % str(err))
self.number_of_request -= 1
self.log.msg("Test Stop: %s\n" % ctime())
self.log.msg("Total Duration: %s" % sum(time_list))
self.log.close()
def main():
help_msg = "\nUse --help or -h"
try:
opt_list, arg_list = getopt(sys.argv[1:], "hc:n:f:s:l:", ["help"])
except GetoptError, msg:
msg = msg.msg + help_msg
usage(sys.stderr, msg)
sys.exit(2)
kw = {}
for opt, arg in opt_list:
if opt in ('-h', '--help'):
usage(sys.stdout, __doc__)
sys.exit(2)
elif opt == '-c':
number_client = int(arg)
elif opt == '-n':
number_request = int(arg)
elif opt == '-f':
document_folder = arg
elif opt == '-s':
server_address = arg
elif opt == '-l':
kw['log_folder_url'] = arg
client_list = []
for num in range(number_client):
kw['log_filename'] = "client%s.log" % num
client = Client(server_address, number_request, document_folder, **kw)
client_list.append(client)
client.start()
for client in client_list:
client.join()
if __name__ == "__main__":
main()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from sys import path
from ConfigParser import ConfigParser
from os.path import join, exists, dirname
from os import environ, putenv
from cloudooo.application.xvfb import xvfb
from cloudooo.application.openoffice import openoffice
from cloudooo.utils import waitStartDaemon
from cloudooo.mimemapper import mimemapper
config = ConfigParser()
testcase_path = dirname(__file__)
def make_suite(test_case):
"""Function is used to run all tests together"""
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(test_case))
return suite
def loadConfig(path):
config.read(path)
def startFakeEnvironment(start_openoffice=True, conf_path=None):
"""Create a fake environment"""
server_conf_path = conf_path or join(testcase_path, "..", "samples/cloudooo.conf")
loadConfig(server_conf_path)
uno_path = config.get("app:main", "uno_path")
path_dir_run_cloudooo = config.get("app:main", "path_dir_run_cloudooo")
virtual_display_id = int(config.get("app:main", "virtual_display_id"))
virtual_display_port_int = int(config.get("app:main", "virtual_display_port"))
hostname = config.get("server:main", "host")
openoffice_port = int(config.get("app:main", "openoffice_port"))
office_bin_path = config.get("app:main", "office_bin_path")
if not environ.get('uno_path'):
environ['uno_path'] = uno_path
office_bin_path = config.get("app:main", "office_bin_path")
if not environ.get('office_bin_path'):
environ['office_bin_path'] = office_bin_path
if not uno_path in path:
path.append(uno_path)
fundamentalrc_file = '%s/fundamentalrc' % office_bin_path
if exists(fundamentalrc_file) and \
not environ.has_key('URE_BOOTSTRAP'):
putenv('URE_BOOTSTRAP','vnd.sun.star.pathname:%s' % fundamentalrc_file)
xvfb.loadSettings(hostname,
virtual_display_port_int,
path_dir_run_cloudooo,
virtual_display_id,
virtual_screen='1')
xvfb.start()
waitStartDaemon(xvfb, 10)
if start_openoffice:
openoffice.loadSettings(hostname,
openoffice_port,
path_dir_run_cloudooo,
virtual_display_id,
office_bin_path,
uno_path)
openoffice.start()
openoffice.acquire()
hostname, port = openoffice.getAddress()
kw = dict(python_path=config.get("app:main", "python_path"),
unomimemapper_bin=config.get("app:main", "unomimemapper_bin"),
uno_path=config.get("app:main", "uno_path"),
office_bin_path=config.get("app:main", "office_bin_path"))
if not mimemapper.isLoaded():
mimemapper.loadFilterList(hostname, port, **kw)
openoffice.release()
return openoffice, xvfb
return xvfb
def stopFakeEnvironment(stop_openoffice=True):
"""Stop Openoffice and Xvfb """
if stop_openoffice:
openoffice.stop()
xvfb.stop()
return True
class cloudoooTestCase(unittest.TestCase):
"""Test Case to load cloudooo conf."""
def setUp(self):
"""Creates a environment to run the tests. Is called always before the
tests."""
self.hostname = config.get("server:main", "host")
self.cloudooo_port = config.get("server:main", "port")
self.openoffice_port = config.get("app:main", "openoffice_port")
self.office_bin_path = config.get("app:main", "office_bin_path")
self.path_dir_run_cloudooo = config.get("app:main", "path_dir_run_cloudooo")
self.tmp_url = join(self.path_dir_run_cloudooo, "tmp")
self.uno_path = config.get("app:main", "uno_path")
self.unomimemapper_bin = config.get("app:main", "unomimemapper_bin")
self.unoconverter_bin = config.get("app:main", "unoconverter_bin")
self.python_path = config.get("app:main", "python_path")
self.virtual_display_id = config.get("app:main", "virtual_display_id")
self.virtual_display_port_int = config.get("app:main", "virtual_display_port")
self.afterSetUp()
def afterSetUp(self):
""" """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>File input (or "upload") in HTML forms</title>
<link rel="stylesheet" title="Yucca's style" href="test_arquivos/basic.css">
<style type="text/css">
body { font-family: Cambria; font-size-adjust: 0.465; }
h1, h2, h3, h4 { font-family: Candara, sans-serif; }
* { line-height: 1.2; }
</style>
</head><body>
<h1>
<a name="start">File input (or &#8220;upload&#8221;) in HTML forms</a>
</h1>
<p class="summary">A
form
in an HTML document (Web page) can contain an <code>input</code> element
with <code>type="file"</code>. This may let the user include one or
more files into the form submission.
The form is often processed so that such files are stored onto
the disk of the Web server; this is why file input (or file submission)
is often called &#8220;file upload.&#8221;
File input opens interesting possibilities, but browser support is
still limited and generally of poor quality even in newest versions.
Moreover, users are often puzzled with it, since most people
use file input rather
rarely.</p>
<p>This document presents
</p><ul>
<li> <a href="#basics">the basics</a>, including
references to <a href="#server">server-side techniques needed</a>,
a simple
<a href="#example">example</a> to test and
<a href="#how">notes on how it was intended to work</a>
</li><li> <a href="#support">notes on browser support</a>:
<a href="#ie">IE</a>, <a href="#netscape">Netscape</a>,
<a href="#moz">Mozilla</a>,
<a href="#opera">Opera</a>,
<a href="#safari">Safari</a>;
<a href="#warn">users&#8217; problems with file input</a>;
<a href="#present">appearance of the Browse button and the filename box</a>
</li><li><a href="#acc">accessibility problems: file input is a challenge to many users</a>
</li><li> <a href="#alt">suggestions for allowing
alternative file submission methods</a>
</li><li> <a href="#ref">some references</a>
</li><li> <a href="#js">notes on client-side scripting issues</a>
</li><li> <a href="#tech">some technical notes</a> on the
specifications and implementations:
<a href="#enctype">The <code>enctype</code> attribute</a>;&nbsp;
<a href="#multi">Submitting several files?</a>;&nbsp;
<a href="#value">Setting the default filename</a>;&nbsp;
<a href="#name">Getting the original name</a>;&nbsp;
<a href="#size">The <code>size</code> attribute</a>;&nbsp;
<a href="#restr">Setting restrictions on the file size</a>;&nbsp;
<a href="#filter">Filtering (through a file type filter)</a>;&nbsp;
<a href="#rfc">The status of RFC 1867</a>.
</li></ul>
<h2><a name="basics">The basics</a></h2>
<p>The idea behind file input in HTML
forms is to let users include entire files from their system
into a form submission. The files could be text files, image files,
or other data. For text files, file input would
allow more convenient mechanisms than typing (or cutting&nbsp;&amp; pasting)
large pieces of text. For binary data, such as images, file input
would be not just more convenient but usually the only practical way.
For more information on the design principles of file input,
see
<a href="http://www.faqs.org/rfcs/rfc1867.html">RFC 1867</a>,
<cite>Form-based File Upload in HTML</cite>.</p>
<p>Writing an HTML form with a file input field is rather simple.
The difficult thing is actually to find or write a server-side script
which can <em>do</em> something useful when it receives data in such a format.
And the <em>really</em> difficult thing is to make such processing
robust and controlled so that
all data is processed properly and so that
someone won&#8217;t e.g. fill your server&#8217;s
disk space with gigabytes of junk, by ignorance or by malevolence.</p>
<p>You need to know the general basics of
writing HTML forms; if you need
links to tutorials and references on forms, consult
<cite><a href="http://www.cs.tut.fi/%7Ejkorpela/forms/index.html">How to write HTML forms</a></cite>.
Then, what
you need to do <strong>in HTML</strong> is to
write a form so that
</p><ul>
<li> the <code>action</code> attribute refers to a server-side
script which is capable of handling submissions containing forms or,
technically speaking, being in <code>multipart/form-data</code>
format; as explained below,
don&#8217;t even dream about using
<code>mailto:</code> URLs in <code>action</code> attributes,
in this context or otherwise!
</li><li> it has the attribute
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/methods.html" title="Methods GET and POST in HTML forms&#8212;what&#8217;s the difference?">
<code>method="post"</code></a>
</li><li> it has the attribute
<code><a href="#enctype">enctype</a>="multipart/form-data"</code>
</li><li> it contains a field
<br>
<code>&lt;input type="file" name="<var>somename</var>" size="<var>chars</var>"&gt;</code>
<br>
where
<ul class="emb">
<li> <var>somename</var> is a name you assign to the field as you like; the
form data set will contain the content of the file &#8220;under that
name&#8221;, and that name has nothing to do with the filename
</li><li> <var>chars</var> is an integer specifying the desired width,
as a number of characters,
of the filename box to be displayed; the <code>size</code> attribute
is optional, but setting
it to some relatively
large value (say <code>40</code>) probably helps the user,
since the default width of the
box in current browsers is rather narrow for typical filenames.
(See <a href="#size">notes on the <code>size</code> attribute</a>.)
</li></ul>
</li></ul>
<p><small>Minimally, the form needs to contain a
<a title="Description of INPUT TYPE=SUBMIT in WDG&#8217;s HTML 4.0 reference" href="http://www.htmlhelp.com/reference/html40/forms/input.html#submit">
a submit element</a> too. It may also contain any other fields you like,
and explanatory texts, images, etc.</small></p>
<p class="warning"><a name="goofs">A common problem with file input
in forms</a> is that form data gets sent but only the <em>name</em>
of the file is included. The reason is typically that the <code>form</code>
element does not contain the attributes mentioned above.</p>
<p>Since <a href="#support">browser support</a> to file input
is still problematic, consider
<a href="#alt"><strong>providing alternative methods</strong></a>
of submitting data, too.</p>
<p><small>It is hopefully evident that what happens in file
input is the submission of <em>a copy of the file content</em>.
The file on the user&#8217;s disk remains intact, and the server-side
script cannot change <em>it</em>, only the copy of the data.
</small></p>
<h2><a name="server">Setting up a server-side script</a></h2>
<p>As mentioned above, the server-side script
(form handler) is the difficult part in creating a possibility
for submitting files.
There are useful brief notes on
that in the <a href="#faq" title="&quot;How can I allow file uploads to my web site?&quot; in Web Authoring FAQ by WDG">FAQ entry</a>, but it <em>is</em> a difficult programming issue,
and outside the scope of this document of mine.
I&nbsp;just wish to emphasize&#8212;in addition to
<a href="#restr">security issues</a> discussed below&nbsp;-
that
<strong>what happens to the data after submission is at the hands
of the server-side script</strong>. It could &#8220;upload&#8221; it, i.e.
save onto the server&#8217;s disk under some name, but it might just as
well process the data only by extracting some information from it,
or send the data by E-mail somewhere, or even send it to a printer.
For example, the
<a href="http://www.htmlhelp.org/tools/validator/">WDG HTML Validator</a>
provides, as one alternative,
<a href="http://www.htmlhelp.org/tools/validator/upload.html">a page containing a form for submitting a file</a> to validation.
</p>
<p>There are <strong>different server-side techniques</strong> for
processing forms, so you need to consult documentation applicable to
the technique you use, which is usually dictated by the characteristics
of the server software. In particular, if you use
<strong><a href="http://www.webthing.com/tutorials/cgifaq.html" title="CGI Programming FAQ">CGI</a></strong>, it can be useful
to check section
<a href="http://cgi.resourceindex.com/Programs_and_Scripts/Perl/File_Uploading/"><cite>Programs and Scripts: Perl: File Uploading</cite></a> in
<a href="http://www.cgi-resources.com/"><cite>CGI Resource Index</cite></a>.
(See also the links under &#8220;Related Categories&#8221;
for scripts in other languages.)
You might find a script suitable for your purposes, or at least ideas for
writing your own script.
In your own coding using Perl with CGI,
you&#8217;ll probably benefit from using the
<code><a href="http://theoryx5.uwinnipeg.ca/CPAN/data/CGI.pm/CGI.html">CGI.pm</a></code> module; see especially
section
<cite><a href="http://theoryx5.uwinnipeg.ca/CPAN/data/CGI.pm/CGI.html#CREATING_A_FILE_UPLOAD_FIELD">Creating a file upload field</a></cite>
in its documentation, and my
<cite><a href="http://www.cs.tut.fi/%7Ejkorpela/perl/cgi.html">Fool&#8217;s Guide to CGI.pm</a></cite>.
As an another example, if
<a href="http://www.php.net/">PHP</a> is what you use, see
section
<cite><a href="http://www.php.net/manual/features.file-upload.php3">Handling file uploads</a></cite>
in
<cite><a href="http://www.php.net/manual/">PHP Manual</a></cite>. For
ASP, see e.g. <cite><a href="http://www.asp101.com/articles/jacob/scriptupload.asp">Pure ASP File Upload</a></cite>
by Jacob Gilley.</p>
<h2><a name="example">Example</a></h2>
<p>The example below uses
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/sendback.pl" type="text/plain" title="A Perl script for echoing back form data, formatted as a table">my simple sendback script</a> discussed in
my
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/testing.html" title="How to test HTML forms using simple remotely-accessible scripts">document on testing HTML forms</a>.
It simply echoes back the data it gets, but presented so that your
browser will display it nicely; for a file field, only
40 first octets (byes) are shown.</p>
<p>The HTML markup is:
</p><pre><code class="html">&lt;form action="http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi"
enctype="multipart/form-data" method="post"&gt;
&lt;p&gt;
Type some text (if you like):&lt;br&gt;
&lt;input type="text" name="textline" size="30"&gt;
&lt;/p&gt;
&lt;p&gt;
Please specify a file, or a set of files:&lt;br&gt;
&lt;input type="file" name="datafile" size="40"&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;input type="submit" value="Send"&gt;
&lt;/div&gt;
&lt;/form&gt;</code></pre>
<p>And on your browser, with its current settings, and
as possibly affected by
<a href="http://www.cs.tut.fi/%7Ejkorpela/basic.css" type="text/css">my stylesheet</a>,
this is what the form looks like
</p><form action="http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi" enctype="multipart/form-data" method="post">
<p>
Type some text (optionally):<br>
<input id="txt" name="textline" size="30" type="text">
</p>
<p>
Please specify a file, or a set of files:<br>
<input name="datafile" size="40" type="file">
</p>
<div><input value="Send" type="submit"></div>
</form>
<h2><a name="how">How it was intended to work</a></h2>
<p><a href="http://www.faqs.org/rfcs/rfc1867.html" title="Form-based File Upload in HTML">RFC 1867</a> describes, in section
<cite>3&nbsp;Suggested implementation</cite>, how
file input was intended to take place in a typical situation:
</p><blockquote>
<h3>3.1 Display of <code>FILE</code> widget</h3>
<p> When a[n] <code>INPUT</code>
tag of type <code>FILE</code> is encountered, the browser might show
a display of (previously selected) file names, and a &#8220;Browse&#8221; button
or selection method. Selecting the &#8220;Browse&#8221; button would cause the
browser to enter into a file selection mode appropriate for the
platform. Window-based browsers might pop up a file selection window,
for example. In such a file selection dialog, the user would have the
option of replacing a current selection, adding a new file selection,
etc. Browser implementors might choose let the list of file names be
manually edited.</p>
<p> If an <code>ACCEPT</code> attribute is present, the browser might constrain the
file patterns prompted for to match those with the corresponding
appropriate file extensions for the platform.</p>
</blockquote>
<p>Upon form submit, the contents of the files would then
be included into the data set sent, as defined by the specification
of the
<a href="#enctype"><code>multipart/form-data</code></a>
data type (data format, data encoding).</p>
<h2><a name="support">Browser support to file input</a></h2>
<p class="important">Although most browsers have
supported file input for a long time, the <em>quality</em>
of implementations is poor. Therefore users easily get confused with
file input.</p>
<p>The following notes on browser support are mostly
historical and based on fairly old
observations of mine (on Win95, Win98, and WinNT).
These notes are followed by more interesting notes
<a href="#warn">users&#8217; problems</a>
especially caused by the poor quality of support on modern browsers.</p>
<h3><a name="ie">Internet Explorer</a></h3>
<p><strong>IE 3.0</strong>
displays an input box and
lets the user type a filename there&#8212;and it sends the
<em>name</em> as part of the form data!
Generally, any browser without any code which tries to support
<code>input type="file"</code> can be <em>expected</em> to
behave that way. (A browser which does not recognize <code>"file"</code>
as a possible value for the <code>type</code> attribute can be expected
to ignore that attribute, which means that the default value will be
used, as if
<a title="Description of INPUT TYPE=TEXT in WDG&#8217;s HTML 4.0 reference" href="http://www.htmlhelp.com/reference/html40/forms/input.html#text"><code>type="text"</code></a> had been specified.)
</p>
<!--
<p><small>It has been reported that IE 3.0 from minor
version 3.02 onwards supports file input, if an add-on
is installed.</small></p>
-->
<p><strong>IE 4</strong> has an input box
and a &#8220;Browse&#8221; capability,
and it actually sends the file content,
but it still allows <em>one</em> file
only to be selected.
The &#8220;Browse&#8221; function display is <a href="#filter" title="Filtering (through a file type filter)">unfiltered</a>, i.e. all files
which are normally visible are selectable.
There does not seem to be any improvement in this respect in IE 5,
or IE&nbsp;6, or IE&nbsp;7.</p>
<h3><a name="netscape">Netscape</a></h3>
<p>According to
<a href="http://devedge.netscape.com/library/manuals/1998/htmlguide/tags10.html#1312487">Netscape&#8217;s documentation on file input</a>,
support to it exists already in
<strong>Netscape&nbsp;2</strong>. <!--I haven&#8217;t tested that, so I will
mainly comment on version&nbsp;4 only.--></p>
<p><strong>Netscape 4</strong> support to file input has
a &#8220;Browse&#8221; capability, too, but the browsing has by default
a
<a href="#filter" title="Filtering (through a file type filter)">filter</a>
which limits selectability to &#8220;HTML files&#8221;.
The user can manually change this, though it is questionable
how familiar users are with such things.
Only one file can be specified.
There does not seem to be any improvement in this respect in Netscape 4.5.
Here is an example of the user interface:
</p><blockquote><p>
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/file.gif">
<img src="test_arquivos/file.gif" title="Screen shot of Netscape 4&#8217;s user interface for file input" alt="(A popup window, titled &quot;File Upload&quot;, with a typical
Windows-style set of directory and file icons, and basic
functionality for navigation in the directory hierarchy.
There is a field (initially empty) for File name, and a pulldown
menu named Files of type, initially set to HTML Files.)" border="1" height="332" width="551"></a></p></blockquote>
<h3 id="moz">Mozilla</h3>
<p>The above-mentioned
strange feature of Netscape has been fixed in
<a href="http://www.mozilla.org/">Mozilla</a>, which
uses no filter (i.e. displays all files);
on the other hand it (at least in several versions) gives
no user option to switch to a filtered view!</p>
<p>Otherwise, Mozilla browsers follow the IE and Netscape tradition
in implementing file input.</p>
<h3><a name="opera">Opera</a></h3>
<p><a href="http://www.operasoftware.com/" title="Opera software home page"><strong>Opera</strong></a>
supports file input rather well.
<!--(This applies to 3.60 official release;
there were serious problems in beta releases.)-->
It provides a &#8220;Browse&#8221; menu,
though the button for activating it carries the label &#8220;...&#8221;,
which might be somewhat confusing.
It lets the user specify several files from the menu:
</p><ul class="emb">
<li> Normally when you click on a file, the selection is changed, but...
</li><li> if you keep the Ctrl key pressed down while clicking on a
file, Opera <em>adds</em> it to the selection, and
</li><li> if you keep the Shift key pressed down while clicking on a
file, Opera discards the current selection and replaces it with a <em>range</em>
of files, from the file you clicked on to the file you last clicked before that,
inclusively.
</li></ul>
<p>It isn&#8217;t perfect though. The Browse window is rather small, and it is
impossible to pick up several ranges, i.e. you must click on the files individually
unless you want to select just one contiguous range.
And the box for file names is quite small too, and its size is not
affected by the <code>size</code> attribute.
See also <a href="#value">notes on setting the default filename</a>.
</p><p>When several files are specified (for one file input field),
Opera puts them into a <code>multipart</code> message inside
a <code>multipart</code> message.
</p>
<h3 id="safari">Safari</h3>
<p>The Safari browser is popular in the Mac environment and is now
available for Windows as well, as a beta version.</p>
<p>I have been told that on Safari, the file input widget has just a
browse button, labeled
&#8220;Choose file&#8230;,&#8221;
with no filename field.</p>
<h3><a name="warn">Users&#8217; problems with file input</a></h3>
<p>On the browsers discussed above,
if the user <strong class="warning">types a filename directly</strong> into
the input box, it must be the <em>full pathname</em> and it must
be typed exactly. If the input is not a name
of an existing file
(e.g. due to a typo),
then the form will be sent
as if an empty file had been specified
(though with the name given by the user),
and <em>no
warning</em> is given. People who encounter file input for the
first time might be expected to get very confused, since
the filename box appears first and looks like an area
where the user should type something. </p>
<p>The user probably often wishes to <strong>view</strong> the
contents of files in the dialog, since it is difficult to
select the file on the basis of its <em>name</em> only.
On Windows systems, the browsers discussed here seem to use
widgets where normal clicking on a file icon selects it, and to
open it (in some program) one needs to use <em>right click</em> and
select a suitable action. I guess most users won&#8217;t find that out
without being helped. The following screen capture presents
the dialogue on IE&nbsp;4 (on WinNT) in a situation where the user
has right clicked on an icon and an action menu has popped up and
the user is about to select the <u>O</u>pen action (which would,
in this case, probably open the <code>.jpg</code> file in a graphics
program or in a new browser window.</p>
<img alt="(A Choose file dialog, with a popup menu on top of it,
with Select as the first and hightlighted alternative.
The Open alternative has been focused on. There are
other alternatives below it, e.g. Add to Zip, Send To,
and Properties." src="test_arquivos/select.gif" height="358" width="436">
<p><small>There&#8217;s little you can do as the author of a form
to help users in getting acquainted with such issues.
If you think it&#8217;s useful to refer to instructions for some
particular browsing environments, make it clear what situations
(browsers, operating systems)
the instructions apply to.</small></p>
<p>The technical problems discussed here are one reason why
authors should consider providing <em>alternatives</em> to file input.
There&#8217;s a section on <a href="#acc" title="File input is a challenge to many users">accessibility problems</a> below,
discussing some additional reasons.</p>
<h3><a name="present">The appearance of the Browse button and the filename box</a></h3>
<table align="right" border="0" cellpadding="8" cellspacing="0" width="40%">
<tbody><tr><td>
<form action="http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi" enctype="multipart/form-data" method="post">
<div><input name="datafile" type="file">
<input type="submit"></div>
</form>
</td></tr></tbody></table>
<p>All the browsers mentioned above use essentially
similar appearance for the
<a href="http://foldoc.doc.ic.ac.uk/foldoc/foldoc.cgi?query=widget" title="Definition of &quot;widget&quot;">widget</a> used to implement a file input element:
a text input box for the filename looks similar to
normal text input elements (<code>&lt;input type="text"&gt;</code>), and
the Browse button
resembles submit buttons
(thus, is often grey), and it has
the text &#8220;Browse&#8221; or
its equivalent in another language.
</p>
<p><small>That text is under the control of the browser, not the author.
It has however
<a href="http://www.deja.com/msgid.xp?MID=%3C38119FEE.DFF60EBA@sector27.de%3E">been reported</a> that on Netscape, the text could be changed using a
<!--"http://developer.netscape.com/docs/manuals/communicator/jsguide/scripts.htm"-->
signed script.</small></p>
<p>This is somewhat problematic, since it does not make
the essential difference between submit and browse buttons
visually obvious. Cf. to similar
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/imagereset.html#why">problems with reset buttons</a>.</p>
<p>There is no way to <em>guarantee</em> that Browse buttons
&#8220;look different&#8221;
or otherwise force any particular <em>appearance</em>
such as font face or size. See
the document
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/present.html" title="The visual appearance of input fields in forms on Web
pages, such as the width of a text input field or the color of a
submit button, can be affected using presentational markup
or style sheets or both. This document discusses both ways
and their practical effect on present-day browsers."><cite>Affecting the presentation
of form fields on Web pages</cite></a>
for an overview and examples. The Browse button is particularly
&#8220;immune&#8221; to any presentational suggestions; it&#8217;s typically a
&#8220;hard-wired&#8221; part of the browser&#8217;s user interface.
In particular, on IE, declaring a background color and a text color
for <code>input</code> elements in a style sheet affects
submit buttons (<code>input type="submit"</code>) but not
Browse buttons (<code>input type="file"</code>).
</p>
<p>If you think that &#8220;looking different&#8221; is important, you might thus try
suggesting presentational features for <em>submit</em>
buttons rather than Browse buttons (i.e., for <code>input type="file"</code>
elements). However, this would mean that
Browse buttons look like (the default appearance of) submit buttons
whereas real submit buttons don&#8217;t!
So it seems that it&#8217;s
<strong>best to let browsers present Browse and submit
buttons their way</strong>.
</p>
<p>The <strong>input box</strong> for the filename, on the other hand,
seems to be affected by similar factors as normal text input boxes.
You can apply various CSS properties to the <code>input</code>
element, though it is far from obvious what they should mean for
a file input widget or what they actually cause in each browser.</p>
<p><small>Historical note:
Since <code>input</code> elements are inline (text-level) elements,
you can put text level markup
like <code>font</code> around them in HTML.
However, such markup is often ignored when rendering form fields
For example,
<code>&lt;font size="4" face="Courier"&gt;&lt;input type="file" ...&gt;&lt;/font&gt;</code>
<em>might</em>
increase the font size and set the font to Courier. Specifically,
this happened on Netscape&nbsp;4 but not on most other browsers.
(As a side effect, on Netscape&nbsp;4, such
a font size change affected the dimensions of the Browse
button but not the font size of the the text &#8220;Browse&#8221;.
Note that
if you included a <code>color</code> attribute there,
Netscape&nbsp; ignored it.)</small></p>
<p>You could suggest presentational properties
in a style sheet too, e.g.<br>
<code>&lt;input type="file" ... style="color:#f00; background:#ccc"&gt;</code><br>
and these in turn would be ignored e.g. by Netscape&nbsp;4
but applied, to some extent at least, by most other graphic browsers.
It is difficult to say how CSS rules <em>should</em>
affect the widget, since it is an open question whether e.g.
the text of the Browse button (which is not part of the
textual content of the HTML document)
should be formatted according to the font properties of the
<code>input</code> element. (For example, IE&nbsp;4 and Mozilla seem to
apply the font-size property but not the font-family property when
rendering the button text. IE&nbsp;6 applies font-family too.</p>
<p>The following example demonstrates how your browser treats a file
input element where we suggest presentational properties both in HTML
and in CSS:</p>
<form action="http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi" enctype="multipart/form-data" method="post">
<div>
<b><tt><big><input name="foo" style="background: rgb(255, 255, 204) none repeat scroll 0% 0%; color: rgb(102, 51, 0); -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; font-size: 160%; font-family: Courier,monospace; font-weight: bold;" type="file"></big></tt></b>
</div>
</form>
<p>The example has the HTML markup<br>
<code>&lt;b&gt;&lt;tt&gt;&lt;big&gt;&lt;input type="file" ...&gt;&lt;/big&gt;&lt;/tt&gt;&lt;/b&gt;</code><br>
and the following CSS declarations applied to that <code>input</code> element:<br>
<code>color:#630; background:#ffc none; font-size:160%; font-family:Courier,monospace; font-weight:bold</code><br>
Such suggestions might help in making it clearer to users that
there is a <em>special</em> input box. But try to avoid making it look
<em>too</em> special, since there is then the risk of not getting
intuitively recognized as an input box at all.</p>
<p>At Quirksmode.org, there is a longish article that discusses
fairly complex CSS techniques for changing the appearance of
file input elements, in a sense:
<cite><a href="http://www.quirksmode.org/dom/inputfile.html">
Styling an input type="file"</a></cite>. I&nbsp;would however advice
against any substantial changes in the appearance. Any esthetic
improvement over browser defaults (in addition to being a matter
of taste) has a price: it makes even the experienced user uncertain
of what the widget is.</p>
<h2><a name="acc">File input is a challenge to many users</a></h2>
<p>This section discusses some specific <em>accessibility</em>
problems in file input. For an overview of what accessibility is and
why it is important, please refer to the
<cite><a href="http://web.archive.org/web/20030605114512/http://www.diffuse.org/accessibility.html">Guide to Web Accessibility and Design for All</a></cite>.</p>
<p>It has been reported that some
special-purpose browsing software,
such as some versions of the
<a href="http://www.freedomscientific.com/fs_products/software_jaws.asp">JAWS</a> screen reader, have serious difficulties in file input.
This is understandable, since the common implementation in browsers
is oriented towards visual interaction.</p>
<p>Even the &#8220;normal&#8221; browsers have serious difficulties in file
input without using a mouse. (There are different reasons, including
physiological and neurological problems, why the user may need to work
without a mouse or other pointing devide.)
In Internet Explorer&nbsp;6, you can select the
Browse button by tabbing, but if you try to use
the keyboard to activate it, hitting the Enter key, the browser
<em>submits the form</em> instead!
You would need to know that hitting the <em>space bar</em>
(when focused on the Browse button)
activates
the file selection dialogue.
Netscape&nbsp;7 skips over the browse button
entirely when tabbing&#8212;it cannot be selected without a mouse.</p>
<p><small>Not surprisingly, on Opera things work reasonably.
The user can
select the Browse button using the tab key and activate it
by pressing the enter key, then select a file for upload from the
file system; you would use the arrow keys move around in the file
selection.</small></p>
<p>On the <a href="http://lynx.browser.org/">Lynx</a>
text browser, at least on Lynx&nbsp;2.8.4 on Unix,
there is no Browse button, and there is no dialogue
for accessing the computer&#8217;s
file system. Thus, the user needs to know the exact path name and syntax to
type in the file name for upload, as is apparently also the case for
IE and Netscape.</p>
<p>There is also the
<em>usability problem</em>
that the browsing
may start from a part of the file system in a manner which is
not so natural to the user. The initial selection might be
e.g. that of the directory where the Web browser itself resides!
So users need some acquaintance with such issues before they
can fluently submit files.</p>
<p>More generally, since file input is relatively rare,
users are often <strong>not familiar</strong> with it.
They might not recognize the Browse button, and might have difficulties
in understanding what&#8217;s going on when they click on it (or fail to
click on it).</p>
<p class="important">Thus, authors should normally
include some short explanation about the presence of a file
input field before the field itself. This can usually me done
in a natural way, explaining simultaneously what kind and type of file
should be submitted.</p>
<p>For example, the explanation could say: &#8220;Please specify, if possible,
an image
file containing your photo in JPEG format.&#8221;
Such a note may not help much when a user
encounters such a field for the first time in his life,
but it helps him to associate the eventual problems with a concept
of file input and to explain his problems when seeking for help.
And if he has tried to use file input before,
it tells him to stay tuned to something special, and
perhaps at this point, before entering the file input field, to access the
file system outside the browser and find the exact path name of the file
he wants to submit.</p>
<h2><a name="alt">How to provide alternatives</a></h2>
<p>There are several possible ways to let people submit their
files even when their browsers do not
<a href="#support" title="Browser support to file input">support</a> file fields in forms
(or the support is of so poor quality that they don&#8217;t want to use it).</p>
<p>You could include a
<strong><a title="Description of TEXTAREA in WDG&#8217;s HTML 4.0 reference" href="http://www.htmlhelp.com/reference/html40/forms/textarea.html"><code>TEXTAREA</code></a></strong> element into the form.
This would work especially for <em>text</em> files in the sense that a user
could open his file in an editor and cut &amp; paste the data
into the textarea. Naturally, this becomes awkward for large files,
but it might still be a good idea to have a textarea along with
a file input field. Your server side script would need some more
code to handle both.</p>
<p>You could simply include an <strong>E-mail address</strong>
and encourage people to send their files to that address as
attachments. You would need to have some processing for such
submissions, but it <em>could</em> be automated using some
software like <a href="http://www.iki.fi/era/procmail/mini-faq.html" title="Procmail FAQ">Procmail</a>. On the other hand, you might decide that such
submissions will be rare, and process them &#8220;by hand.&#8221;
Make sure the address is <strong>visible</strong> on the page
itself. You could make it a <code>mailto:</code> link too,
but don&#8217;t risk the functionality
by some misguided attempt to
<a href="http://www.htmlhelp.com/faq/html/links.html#mailto-subject">include a fixed <code>Subject</code> header</a>!
Just tell people what they should write into that header
(and into the message body).
</p>
<p>Sometimes you might consider setting up an
<strong>FTP server</strong>, or
using one, so that it has a free upload area.
You would then just specify the server and the area, and
people could
use their favorite FTP clients. Note that for the submission of
a large number of files, FTP would be more comfortable than
using a form with a file input field.
</p>
<p>Especially for local users, you could just give a
<strong>physical address</strong> to which people can bring or
send their files e.g. on diskettes or CD roms. Make it clear to them
<em>in advance</em> which media and formats you can handle that way.
</p>
<h2><a name="ref">References</a></h2>
<ul>
<li> <a href="http://www.htmlhelp.org/reference/html40/forms/input.html#file">The part which describes <code>input type="file"</code></a> in
<a href="http://www.htmlhelp.org/reference/html40/forms/input.html">the description of the <code>input</code> element</a> in
<a href="http://www.htmlhelp.org/reference/html40/"><cite>HTML
4.0 Reference</cite></a> by
<a href="http://www.htmlhelp.org/" title="Web Design Group">WDG</a>.
That document is also available e.g. as
<a href="http://htmlhelp.inet.tele.dk/reference/html40/forms/input.html">a mirror copy in Denmark</a>. Note that the document contains,
under <cite>More information</cite>,
references to the definition of the <code>input</code>
element in HTML specifications.
</li><li> <a href="http://www.htmlhelp.com/faq/html/forms.html#file-upload" name="faq">Answer to the question
<cite>How can I allow file uploads to my web site?</cite></a>
in <a href="http://www.htmlhelp.com/" title="Web Design Group">WDG</a>&#8217;s
<a href="http://www.htmlhelp.com/faq/html/" title="Web Authoring FAQ (index page)"><cite>Web Authoring FAQ</cite></a>.
That document too has
<a href="http://htmlhelp.inet.tele.dk/faq/html/forms.html#file-upload">a mirror copy in Denmark</a>.
</li></ul>
<p>See also <a href="#rfc">notes on RFC 1867</a>.</p>
<h2><a name="js">Notes on client-side scripting issues</a></h2>
<p>In <a href="http://www.cs.tut.fi/%7Ejkorpela/forms/javascript.html#scripting-gen">client-side
scripting</a>, there are some special problems when handling
file input fields. The <a href="http://www.irt.org/script/form.htm"><cite>JavaScript Form FAQ</cite></a> contains answers to
such questions:
</p><ul>
<li><a href="http://www.irt.org/script/1154.htm">FAQ 1154</a> How can I set the value of a fileupload form field?</li><!--JavaScript Form-->
<li><a href="http://www.irt.org/script/561.htm">FAQ 561</a> How can I extract just the file name from a forms file upload field?</li><!--JavaScript Form-->
<li><a href="http://www.irt.org/script/780.htm">FAQ 780</a> Can you simulate a click on an &lt;input type="file"&gt; button?</li><!--JavaScript Form-->
</ul>
<p>See also
<a href="#filter">notes on filtering</a> above as regards to support
to event attributes for file input.</p>
<h2><a name="tech">Technical notes</a></h2>
<p>
The <cite><a href="http://www.w3.org/TR/html4/">HTML 4.01 specification</a></cite> discusses, in
<a href="http://www.w3.org/TR/html4/interact/forms.html">section <cite>Forms</cite></a>,
issues related to file input fields along with other types of fields.
The notes below hopefully help in locating
and interpreting
the relevant portions.</p>
<h3><a name="enctype">The <code>enctype</code> attribute</a></h3>
<p>
The HTML 4.01 specification
<a href="http://www.w3.org/TR/html4/interact/forms.html">defines an <code>enctype</code> attribute</a> for the
<code>form</code> element.
Its value is generically defined <!--(though only in
<a href="http://www.w3.org/TR/REC-html40/sgml/dtd.html#ContentType"
>a DTD comment</a>!)--> as being a &#8220;media type&#8221;, referring to
<a href="http://www.faqs.org/rfcs/rfc2045.html" title="Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies">RFC 2045</a>. (That
<a href="http://www.cs.tut.fi/%7Ejkorpela/rfc.var" title="What RFCs are&#8212;General info on RFCs">RFC</a> is actually just one part of a large set of documents which
what media types are. In particular, the general description of
the media type concept is in
<a href="http://www.faqs.org/rfcs/rfc2046.html" title="Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types">RFC&nbsp;2046</a>.)
</p><p>
<a name="mediatype">A <dfn>media type</dfn></a>,
also known as <dfn>content type</dfn>, <dfn>Internet media type</dfn>, or
<dfn>MIME type</dfn>, defines a <em>data format</em> such as
plain text (<code>text/plain</code>), GIF image (<code>image/gif</code>)
or binary data with unspecified internal structure
(<code>application/octet-stream</code>).
</p><p>
But in the context of form submission, the use of a media
type as the value of the <code>enctype</code> attribute is meaningful
only if there is a definition of the <strong>conversion</strong> to be
done. This means the exact way of <em>encoding</em> the form data,
which is essentially a set of <var>name</var>/<var>value</var> pairs,
into a particular data format. The definition must be rigorous, since
otherwise it is impossible to process the data in a useful, robust way
by computer programs.
</p><p>The HTML specification
<a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4">defines two possible values for <code>enctype</code></a>:
</p><dl>
<dt> <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1"><code>enctype="application/x-www-form-urlencoded"</code></a>
(the default)
</dt><dd> This implies a simple encoding which presents the fields as
<var>name</var><code>=</code><var>value</var> strings separated by
ampersands (<code>&amp;</code>) and uses some special
<a href="http://www.cs.tut.fi/%7Ejkorpela/chars.html#esc">&#8220;escape&#8221; mechanisms
for characters</a>, such as <code>%28</code> for the &#8220;(&#8221; character.
It&#8217;s confusing if people try to read it&#8212;it was meant to be
processed by programs, not directly read by humans!
</dd><dt> <a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2"><code>enctype="multipart/form-data"</code></a>
</dt><dd> This implies that the form data set is encoded so that
each form field (more exactly, each &#8220;control&#8221;) is presented
in a format suitable for that field, and the data set as a whole
is a <code>multipart</code> message containing those
presentations as its components. This is wasteful for &#8220;normal&#8221;
forms but appropriate, even the only feasible way, for forms
containing file fields. The <code>multipart</code> structure
means that each file comes in a nice &#8220;package&#8221; inside a larger
package, with a suitable &#8220;label&#8221; (content type information)
on the inner &#8220;package.&#8221;
This type was originally defined in
<a href="http://www.faqs.org/rfcs/rfc1867.html" title="Form-based File Upload in HTML">RFC&nbsp;1867</a> but it is also discussed in
<a href="http://www.faqs.org/rfcs/rfc2388.html" title="Returning Values from Forms: multipart/form-data">RFC&nbsp;2388</a>
(see <a href="#rfc">notes on the RFCs</a> later).
</dd></dl>
<p><small>Browsers
may support other values too, but are not required to, and it is
generally unsafe to use them.
Sometimes people use <code>enctype="text/plain"</code>,
and <code>text/plain</code> is <i>per se</i> a well-defined media type;
but there is no specification of the exact method of encoding
a form data set into such a format, and browsers are not required to
support such an attribute&#8212;so <em>anything</em> may happen if
you use it.</small></p>
<p class="important">Normally you should not try
to re-invent the wheel by
writing code which interprets (decodes) the encoded form data.
Instead, call a suitable routine in a subroutine <strong>library</strong>
for the programming language you use. It typically decodes the data
into a convenient format for you to process in your own code.
</p>
<p>It seems that the HTML 4.01 specification contains no explicit
requirement that <code>enctype="multipart/form-data"</code> be
used if the form contains a file input field
(although it explicitly
<a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2">recommends</a> that).
But e.g. IE 4 and Netscape 4 handle form submissions incorrectly
if the <code>enctype</code> is defaulted in such a case:
they send the <em>name</em> of the file instead of its content!</p>
<h3><a name="multi">Submitting several files?</a></h3>
<p>
The HTML 4.01 specification uses the term
<dfn>file select</dfn> for the &#8220;control&#8221; (i.e. form field)
created by an <code>input type="file"</code> element. It
<a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.2.1">specifies file select</a> so
that this control type allows the user to select files
so that their contents may be submitted with a form. Note the
plural &#8220;files&#8221;&#8212;the idea is clearly that one such field
should allow the inclusion of several files.
</p><p>
Note that there is nothing an author needs to do, and nothing he
<em>can</em> do, to make a browser allow the selection of several
files per input field. It depends on the browser whether that is
possible.
</p><p>
However, as described above,
the <a href="#support">current browser support</a> is
poor: only some versions of Opera support multi-selection,
and these do not include the newest versions.
And in fact, even if a browser allows users to pick up several files
for one
<code>input type="file"</code> field, users might not know how
they can do that, or <em>how</em> they can
do that!</p>
<p>Thus,
an author might,
as a <strong>workaround</strong>,
include several
<code>input type="file"</code> fields if it is desirable that users
can include several files into one form submission.
<a href="http://lists.w3.org/Archives/Public/www-html/2000Jul/0077.html" title="Re: Form-based Multiple File Upload in HTML
(a message in the www-html list, 2000-07-21)">Andrew Clover has suggested some interesting techniques</a>
for making the appearance of the fields dynamic
(in JavaScript or in a server-based way)
so that
&#8220;the user isn&#8217;t immediately confronted with two dozen empty file upload boxes.&#8221;
</p><p>Alternatively, or additionally, an author might encourage users
to use suitable software like
<a href="http://www.winzip.com/">WinZip</a>
or <a href="ftp://ftp.freesoftware.com/pub/infozip/WiZ.html">WiZ</a>
to &#8220;zip&#8221; several files together. Naturally the server-side script
must then be somehow prepared to handle zipped files.
</p><p>
</p><h3><a name="value">Setting the default filename</a></h3>
<p>The HTML 4.01 specification
<a href="http://www.w3.org/TR/html4/interact/forms.html#h-17.4.1">describes
the <code>value</code> attribute</a>
for a file input field by saying that browsers (user agents)
&#8220;may use the value of the <code>value</code>
attribute as the initial file name.&#8221; This however is
<strong>usually not supported by browsers</strong>. The usual
explanation is &#8220;security reasons.&#8221;
And indeed it would be a security risk if files from the
user&#8217;s disk were submitted without the user&#8217;s content.
It might be all too
easy to lure some users into submitting some password files! But in fact
<a href="http://www.faqs.org/rfcs/rfc1867.html" title="Form-based File Upload in HTML">RFC&nbsp;1867</a> duly notifies this problem; in section
<cite>8&nbsp;Security Considerations</cite> it says:</p>
<blockquote>
It is important that a user agent not send any file that the user has
not explicitly asked to be sent. Thus, HTML interpreting agents are
expected to confirm any default file names that might be suggested
with <code>&lt;INPUT TYPE=file VALUE="yyyy"&gt;</code>.
</blockquote>
<p><small>It also mentions (in section 3.4) that the use of <code>value</code>
&#8220;is probably platform dependent&#8221; but then goes on:
&#8220;It might
be useful, however, in sequences of more than one transaction, e.g.,
to avoid having the user prompted for the same file name over and
over again.&#8221; This isn&#8217;t particularly logical, since how would the
name be passed from one submission to another? (The mechanism for
<a href="#name">getting the original file name</a> would be quite unreliable for such
purposes.)
A more useful application could be this: Assume that your form is for
reporting a problem with a particular program, say Emacs, and
that program uses a configuration file with some specific name, say
<code>.emacs</code>, so that you would very much like to get the user&#8217;s
config file for problem analysis. Setting the default name, if supported
by the browser, might be an extra convenience to the user.</small></p>
<p>Thus, they <em>just failed to implement it</em>, for no good
reason. This isn&#8217;t a very important flaw, however. The situations
where it would make sense to suggest a default file name are rare.</p>
<p><small>
Netscape&#8217;s old <!--a href=
"http://developer.netscape.com/docs/manuals/htmlguid/index.htm"-->
<cite>HTML Tag Reference</cite> says, in
<a href="http://devedge.netscape.com/library/manuals/1998/htmlguide/tags10.html#1312487">the description of <code>input type="file"</code></a>,
that &#8220;<code>VALUE=</code><var>filename</var>
specifies the initial value of the input element,&#8221; but
no actual support to this in Netscape browsers has been reported.
Similar considerations apply to the
<a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/INPUT_file.asp" title="input type=file in Microsoft&#8217;s HTML reference">corresponding item</a>
in Microsoft&#8217;s
<a href="http://msdn.microsoft.com/workshop/author/html/reference/elements.asp"><cite>HTML Elements</cite></a> reference.
It additionally messes things up by describing the <em>intended</em>
meaning wrong: &#8220;Sets or retrieves the value of the
<code>&lt;INPUT type=file&gt;</code>.&#8221; The description links to
<a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/value_1.asp">a description of the <code>value</code> attribute</a> which says:
&#8220;The value, a file name, typed by the user into the control.
Unlike other controls, this value is read-only.&#8221; This probably
relates to using the <code>value</code> <em>property</em> in
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/javascript.html#scripting-gen">client-side scripting</a>.
And in fact, one can read the value in JavaScript
(and get the filename entered by the user)
but setting it is unsuccessful (without an error message); the same applies
to Netscape (but on Opera, even an attempt to read the value seems
to confuse the browser).
Note that the <em>examples</em> in the above-mentioned documentation
do not contain an <code>input type="file"</code> element with
a <code>value</code> attribute.</small></p>
<p>
However,
<a href="#opera">support to file input in several versions of Opera</a>
handles the <code>value</code>
attribute in the following way:
</p><ul class="emb">
<li> the value is displayed in the box for file name input
</li><li> that value can be edited by the user (as an <em>alternative</em>
to using the Browse menu, which changes the content of that box)
</li><li> however if the user submits the form so that the initial value
has not been changed by the user, there will be a security alert and
the user is requested to confirm the submission.
</li><li> there does not seem to be any working
way to specify a <em>set</em> of files
in the <code>value</code> attribute.
</li></ul>
<p>Such support, however, is absent in Opera 7.54, for some reason.</p><p>
</p><p>The following form contains a file input field with
<code>value="C:\.emacs"</code>. Your browser probably just ignores
that attribute, but some browsers may use it to set the initial
file name:</p>
<form action="http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi" enctype="multipart/form-data" method="post">
<div><input name="datafile" value="C:\.emacs" type="file"></div>
<div><input value="Send" type="submit"></div>
</form>
<p>An example of Opera&#8217;s security alert in the situation discussed above:
<br>
<img src="test_arquivos/opalert.gif" title="Screenshot" alt="! The files listed below have been selected, without your
intervention, to be sent to another computer. Do you want to
send these files?
Destination&nbsp;&nbsp;http://yucca.hut.fi/cgi-bin/sendback.pl
Form URL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http://www.hut.fi/u/jkorpela/forms/filedemo.html
C:\emacs
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OK&nbsp;&nbsp;&nbsp;&nbsp;Cancel&nbsp;&nbsp;&nbsp;&nbsp;Help" height="263" width="391">
</p><p><small>There was a short-time bug in Opera 6 that created
a security hole, which would have let authors grab users&#8217; files
without their knowing, i.e. bypassing the dialogue
described above.</small></p>
<h3><a name="name">Getting the original name</a></h3>
<p>
<a href="#rfc" title="The status of RFC 1867">RFC 1867</a> says:
</p><blockquote cite="http://www.faqs.org/rfcs/rfc1867.html">
<p>The original local file name may be supplied as well, either as a
&#8216;filename&#8217; parameter either of the &#8216;content-disposition: form-data&#8217;
header or in the case of multiple files in a &#8216;content-disposition:
file&#8217; header of the subpart. The client application should make best
effort to supply the file name; if the file name of the client&#8217;s
operating system is not in US-ASCII, the file name might be
approximated or encoded using the method of
<a href="http://www.faqs.org/rfcs/rfc1522.html" title="MIME (Multipurpose Internet Mail Extensions) Part Two: Message Header Extensions for Non-ASCII Text">RFC&nbsp;1522</a>. This is a
convenience for those cases where, for example, the uploaded files
might contain references to each other, e.g., a TeX file and its .sty
auxiliary style description.</p>
</blockquote>
<p>But note that this appears in
subsection 3.3 of section <cite>3.&nbsp;Suggested
Implementatation</cite>.
Thus, it is <strong>only a recommendation</strong> related
to one <strong>possible</strong> implementation.
You shouldn&#8217;t count on having a
<code>filename</code>
included.</p>
<p>It seems that Netscape, IE, and Opera actually
include the <code>filename</code> parameter.
However, only Opera uses the format which
seems to be the <em>intended</em> one,
as deduced from the examples in
<a href="http://www.faqs.org/rfcs/rfc1867.html" title="Form-based File Upload in HTML">RFC&nbsp;1867</a>
(section&nbsp;6),
namely a <em>relative</em> name like
<code>foo.txt</code>, not a full pathname like
<code>C:\mydocs\foo.txt</code>.
Internet Explorer&nbsp;7 beta preview behaves similarly,
and this has been explained as a security improvement.</p>
<p><small>Is the Netscape and IE behavior really incorrect? Well,
since most computers have some sort of path name system for file names,
one would expect to see path names in examples if the intent had been
that path names are sent.
This is consistent with the fact that in order to actually
<em>use</em> the file names for some meaningful purpose
(like the one mentioned in
<a href="http://www.faqs.org/rfcs/rfc1867.html" title="Form-based File Upload in HTML">RFC&nbsp;1867</a>:
&#8220;the
uploaded file might contain references to each other, e.g.,
a TeX file and its .sty auxiliary style description,&#8221;
which clearly calls for <em>relative</em> file names).
When path names are sent, things get much more complicated,
since their specific syntax (and interpretation)
is strongly system-specific, and there is even no provision for
telling the server what the browser&#8217;s file system is.
Sending relative names only is also consistent with elementary
security considerations: avoid sending information about the
user&#8217;s file system structure.
Note that the security section of
<a href="http://www.faqs.org/rfcs/rfc1867.html" title="Form-based File Upload in HTML">RFC&nbsp;1867</a>
does not mention any problems that might arise from that;
this more or less proves that browsers were
<em>not</em> expected to send path names.</small></p>
<p>
The idea of including a <code>filename</code> attribute
makes sense of course, and would apply e.g. to a file
submission containing a set of HTML documents referring to each other
with relative URLs.
However, it&#8217;s clear that the processing script
would need to strip off the path part of the names (which is in
principle risky since
<code>C:\mydocs\foo.txt</code>
could be a relative filename
on many systems!). Moreover, since the <a href="#multi">submission of several files</a> is
currently clumsy at best, the idea would be of limited usefulness even
when it works. (Collections of files that refer to each other by names
would be best handled as packaged into formats such as
<code>application/zip</code>, leaving the file name issue to be handled
by zipping and unzipping programs, which can preserve relative names as
well as relative directory structures.)</p>
<h3><a name="size">The <code>size</code> attribute</a></h3>
<p>Although the user is not expected to type the filename(s) into
a filename box but use the Browse function,
the size of the box matters. When the user selects a file by
clicking on it, the browser puts the filename into the filename box,
and the name is a full pathname which can be quite long.
It may confuse users if they see the name badly truncated.</p>
<p><a href="http://www.w3.org/TR/REC-html32#input">Definition of
<code>input type="file"</code> in the HTML 3.2 specification</a>
said:</p>
<blockquote>
<div>Just like [for] <code>type=text</code> you can use the
<code>size</code> attribute to set the visible width of this field
in average character widths.</div>
</blockquote>
<p>
And most browsers seem to treat the <code>size</code> attribute
that way. <!--Opera 3.60 ignores the attribute.-->
</p><p>
But the HTML 4.01 specification
<a href="http://www.w3.org/html4/interact/forms.html#adef-size-INPUT">defines the <code>size</code> attribute for an <code>input</code>
element</a> as follows:
</p><blockquote>
This attribute tells the user agent the initial width of the control. The width is given in pixels except when
<code>type</code> attribute has the value
<code>"text"</code>
or
<code>"password"</code>.
In that case, its value refers to the (integer) number of characters.
</blockquote>
<p>This logically implies that for <code>input type="file"</code>,
the <code>size</code> attribute specifies the width in pixels,
not characters.
This is probably an oversight, and
the risk of a browser acting literally according it
is ignorable.</p>
<p>On the other hand, you could
<a href="http://www.cs.tut.fi/%7Ejkorpela/styles/howto.html" title="How to use style sheets (suggested material and procedures)">use style sheets</a> in addition to the <code>size</code> attribute.
Using e.g. the attribute
<code>style="<a href="http://www.htmlhelp.org/reference/css/box/width.html" title="Description of the width property in CSS1">width</a>:25em"</code>
could override the <code>size</code> attribute; this currently seems to happen
on IE 4 and newer only, but it should do no harm on browsers which don&#8217;t
support it.
However note that although it might seem attractive to use
<code>style="width:100%"</code>, asking the browser use as wide a box as
possible, there&#8217;s the problem that at least IE 4 puts the Browse button
on the same line as the box. Thus you would in effect force horizontal
scrolling! Something like <code>style="width:80%"</code>
would be better, though it is just a guess that the box and the button
will then usually fit.
</p>
<h3><a name="restr">Setting restrictions on the file size</a></h3>
<p>Especially if &#8220;file upload&#8221; means storing the file on the server&#8217;s
disk, it is necessary to consider imposing various restrictions.
It would be nasty if some user filled the disk with gigabytes of junk,
by ignorance, or by misclicking, or by malevolence.
See section
<cite><a href="http://cpan.uwinnipeg.ca/htdocs/CGI.pm/CGI.html#avoiding_denial_of_service_attacks">Avoiding Denial of Service Attacks</a></cite> in the
<a href="http://theoryx5.uwinnipeg.ca/CPAN/data/CGI.pm/CGI.html">documentation of CGI.pm</a>; even if it isn&#8217;t directly applicable
to you since you use other techniques than CGI and Perl, it
gives some food for thought in general.</p>
<p>The server-side form handler can be coded to do whatever the
programmer wants, and imposing <em>some</em> upper limit is clearly a must.
(That is, the code should check for the input size, and discard, or
otherwise process in a special way, submissions exceeding a reasonable limit.)
</p><p>
Any client-side restrictions, i.e.
checks done by a browser prior to form submission,
are unreliable and should be considered
as extra comfort to <em>users</em> only&#8212;so that they get a rejection
message earlier.
</p><p>
<a name="maxlength" href="#rfc" title="The status of RFC 1867">RFC 1867</a> says:
</p><blockquote>
If the <code>INPUT</code> tag includes the attribute <code>MAXLENGTH</code>, the
user agent should consider its value to represent the maximum
<code>Content-Length</code> (in bytes) which the server will accept for transferred
files.
</blockquote>
<p>It appears that no browser has even tried to implement
that, and there&#8217;s no statement about such a feature in HTML
specifications. On the contrary, the
<a href="http://www.w3.org/TR/REC-html32#input">HTML 3.2 specification says</a> something quite different:
</p><blockquote>
You can set an upper limit to the length of file names using the
<code>maxlength</code> attribute.
</blockquote>
<p>Thus, it is better not to use the <code>maxlength</code> attribute,
because it currently does nothing and, worse still,
in the future it might be interpreted in two incompatible ways.
The HTML&nbsp;4 specification takes no position on this: it describes
<code>maxlength</code> as defined for <code>input type="text"</code>
and <code>input type="password"</code> only.</p>
<h3><a name="filter">Filtering (through a file type filter)</a></h3>
<p><a href="http://www.w3.org/TR/html4/interact/forms.html#adef-accept">The HTML 4.01 specification defines an <code>accept</code> attribute</a>
for use with <code>input type="file"</code> as follows:
</p><blockquote>
<div>This attribute specifies a comma-separated list of content types
that a server processing this form will handle correctly. User agents
may use this information to filter out non-conforming files when
prompting a user to select files to be sent to the server.
</div>
</blockquote>
<p>Thus you could specify, for example,
<code>accept="image/gif,image/jpeg"</code>, if you are willing to get
image files in GIF or JPEG format only.
Browsers <em>might</em> use this information to set up the Browse menu
so that only such files are selectable, at least initially.
And
<a href="http://www.w3.org/TR/REC-html32#input">the HTML 3.2 specification even claims</a>:
&#8220;Some user agents support the ability to restrict the kinds of files
to those matching a comma separated list of MIME content types given
with the <code>ACCEPT</code> attribute[;]
e.g. <code>accept="image/*"</code> restricts files to images.&#8221;
(Note that <code>"image/*"</code> is not a MIME content type. Obviously
the intent is that some
<a href="http://foldoc.doc.ic.ac.uk/foldoc/foldoc.cgi?query=wildcard" title="A generic loose definition of &quot;wild card&quot;">&#8220;wildcarding&#8221;</a> could be applied, but there doesn&#8217;t seem
to be any definition about that.)
</p><p>
But it seems that browser support is currently nonexistent.
No filtering is applied, except <a href="#netscape">on Netscape&nbsp;4</a>
which initially
sets
a filter which restricts selectability to HTML documents, no matter
what there is in an <code>accept</code> attribute!
And even if there were support, you of course couldn&#8217;t <em>rely</em> on
such filtering, for many reasons.
If it worked, it would be basically for user comfort, not for setting
effective restrictions (which must be imposed by the form handler).</p>
<p>Using
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/javascript.html#scripting-gen">client-side scripting</a>,
you might help some users so that they won&#8217;t submit data of
wrong type.
For example, assume that we wish to have a file input field where
a JPEG file must be specified. And we might take the simplistic
view that this means a file name which ends with <code>jpg</code>,
and check, in a client-side script, that the value of the field
matches that.
Note that the value is the filename, not the file content.
However one must be <strong class="warning">extra careful</strong> here.
Although the
<a href="http://www.irt.org/articles/js058/" title="Events and Event Handlers">event attributes</a>
<code>onfocus</code>, <code>onchange</code> and <code>onblur</code>
for <code>input type="file"</code>
are supported even in earliest JavaScript implementations
(from version 1.0), there are limitations and problems.
In particular, <code>onblur</code> seems to be treated strangely,
and the obvious idea&#8212;associate checking code with
<code>onblur</code>&#8212;seems to make Netscape run in an eternal
loop. Thus, it is probably best to
<strong>associate the checks with file submission only</strong>.
This means using the <code>onsubmit</code> attribute in the
<code>form</code> tag.
<a href="http://www.cs.tut.fi/%7Ejkorpela/forms/filecheck.html" title="The example as a separate document">Example</a>:
</p><pre><code class="html">&lt;script type="text/javascript" language="JavaScript"&gt;
function check() {
var ext = document.f.pic.value;
ext = ext.substring(ext.length-3,ext.length);
ext = ext.toLowerCase();
if(ext != 'jpg') {
alert('You selected a .'+ext+
' file; please select a .jpg file instead!');
return false; }
else
return true; }
&lt;/script&gt;
&lt;form method="post" name=f
enctype="multipart/form-data"
onsubmit="return check();"
action="http://www.cs.tut.fi/cgi-bin/run/~jkorpela/echo.cgi"&gt;
&lt;p&gt;
Please select a JPEG (.jpg) file to be sent:
&lt;br&gt;
&lt;input type="file" name="pic" size="40"
accept="image/jpeg"&gt;
&lt;p&gt;
Please include a short explanation:&lt;br&gt;
&lt;textarea name="expl" rows="3" cols="40"
onfocus="check();"&gt;
&lt;/textarea&gt;
&lt;p&gt;
&lt;input type="submit" value="Send"&gt;
&lt;/form&gt;</code></pre>
<h3><a name="rfc">The status of RFC 1867</a></h3>
<p>
The status of the original description of
<code>input type="file"</code>, namely
<a href="http://www.faqs.org/rfcs/rfc1867.html">RFC 1867</a>,
<cite>Form-based File Upload in HTML</cite>, is vague.
The <a href="http://www.w3.org/TR/html4">HTML 4.01 specification</a>
makes only an
<a href="http://www.w3.org/TR/html4/references.html#h-1.2"><em>informative</em> reference</a>
to it, and mentions a &#8220;work in progress&#8221; in this area:<br>
<small><code>ftp://ftp.ietf.org/internet-drafts/draft-masinter-form-data-01.txt</code></small>
<br>
This is however outdated information; the URL does not work, and
the draft has expired.
There does not seem to be anything else even at the level of
<a href="http://sunsite.cnlab-switch.ch/cgi-bin/search/standard/nph-findstd" title="Internet Standards Archive [incl. Internet-Drafts]">Internet-Drafts</a> to replace RFC 1867.
There is however
<a href="http://www.faqs.org/rfcs/rfc2388.html">RFC&nbsp;2388</a>,
<cite>Returning Values from Forms: multipart/form-data</cite>
which might be related to the process. However it is not
specified to obsolete RFC 1867.
</p>
<p>
In the
<a href="http://www.w3.org/TR/html401/">
<cite>HTML 4.01 Specification</cite></a>,
the <a href="http://www.w3.org/TR/html40/references.html#h-1.2">informative references</a> have been updated so that a reference
is made to RFC&nbsp;2388, with a note &#8220;Refer also to RFC&nbsp;1867.&#8221;
</p>
<p>In June 2000, <a href="ftp://ftp.isi.edu/in-notes/rfc2854.txt">RFC 2854</a>, <cite>The 'text/html' Media Type</cite>, was issued.
It&#8217;s basic purpose was to &#8220;to remove HTML from IETF Standards Track&#8221;
officially, i.e. to make it explicit that work on HTML specifications
has been moved from IETF to W3C. It explicitly obsoletes RFC 1867,
together with some other HTML related RFCs. But note that there is
very little in HTML specifications by the W3C that defines
what file input really is; they refer to RFC 1867 instead.</p>
<p>RFC 1867 contains much more <strong>detailed</strong> information about
&#8220;file upload&#8221; than HTML specifications. It explains the original
idea and how it might be implemented. However, its normative
status is vague,
and the implementations are still wanting,
so you should generally <em>not</em> expect browsers to support
the idea very well.</p>
<hr title="Information about this document">
<div class="footer">
<div><a title="ISO 8601, the date and time representation standard" href="http://www.cs.tut.fi/%7Ejkorpela/iso8601.html">
Date</a> of creation: 1999 (?). Last revision: 2004-11-13.
Last modification: 2008-03-21.</div>
<div>This page belongs to division
<cite><a href="http://www.cs.tut.fi/%7Ejkorpela/www.html">Web authoring and surfing</a></cite>,
subdivision
<cite><a href="http://www.cs.tut.fi/%7Ejkorpela/forms/index.html" title="How to write HTML forms; links to tutorials and references and documents on special topics related to forms">Forms</a></cite> in
the free information site
<cite><a href="http://www.cs.tut.fi/%7Ejkorpela/indexen.html">IT and communication</a></cite>
by
<a href="http://www.cs.tut.fi/%7Ejkorpela/personal.html" title="Jukka K. Korpela, an IT generalist and specialist (personal home page)"><span lang="fi">Jukka</span> &#8220;Yucca&#8221; <span lang="fi">Korpela</span></a>.
</div></div>
</body></html>
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import os
import unittest
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
TestRunner = unittest.TextTestRunner
suite = unittest.TestSuite()
tests = os.listdir(os.curdir)
tests = [n[:-3] for n in tests if n.startswith('test') and n.endswith('.py')]
for test in tests:
m = __import__(test)
if hasattr(m, 'test_suite'):
suite.addTest(m.test_suite())
if __name__ == '__main__':
startFakeEnvironment()
TestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from xmlrpclib import ServerProxy
from subprocess import Popen, PIPE
from base64 import encodestring, decodestring
from cloudoooTestCase import cloudoooTestCase, make_suite
class TestAllFormats(cloudoooTestCase):
"""Test XmlRpc Server. Needs cloudooo server started"""
def afterSetUp(self):
"""Create connection with cloudooo server"""
self.proxy = ServerProxy("http://%s:%s/RPC2" % (self.hostname,
self.cloudooo_port))
def testTextFormats(self):
"""Test all text formats"""
self.runTestForType('odt', 'text', 'data/test.odt')
def testPresentationFormats(self):
"""Test all presentation formats"""
self.runTestForType('odp', 'presentation', 'data/test.odp')
def testDrawingFormats(self):
"""Test all drawing formats"""
self.runTestForType('odg', 'drawing', 'data/test.odg')
def testSpreadSheetFormats(self):
"""Test all spreadsheet formats"""
self.runTestForType('ods', 'spreadsheet', 'data/test.ods')
def testWebFormats(self):
"""Test all web formats"""
self.runTestForType('html', 'web', 'data/test.html')
def testGlobalFormats(self):
"""Test all global formats"""
self.runTestForType('sxg', 'global', 'data/test.sxg')
def runTestForType(self, source_format, document_type, filename):
"""Generic test"""
data = open(filename,'r').read()
request = {'document_type': document_type}
extension_list = self.proxy.getAllowedExtensionList(request)
for extension in extension_list:
data_output = self.proxy.convertFile(encodestring(data),
source_format,
extension[0])
output_file_url = 'output/test_%s.%s' % (document_type, extension[0])
open(output_file_url, 'w').write(decodestring(data_output))
stdout, stderr = Popen("file %s" % output_file_url,
shell=True,
stdout=PIPE,
stderr=PIPE).communicate()
self.assertEquals(stdout.endswith(": empty"), False, stdout)
def test_suite():
return make_suite(TestAllFormats)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestAllFormats)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudooo.application.application import Application
from os.path import exists
from cloudoooTestCase import make_suite
class TestApplication(unittest.TestCase):
def setUp(self):
"""Instantiate one application object and load settings on object"""
self.application = Application()
self.application.loadSettings('localhost', 9999, '/tmp/', '99')
def testLoadSettings(self):
"""Test if settings are defined correctly"""
self.assertEquals(self.application.hostname, 'localhost')
self.assertEquals(self.application.port, 9999)
self.assertEquals(self.application.path_run_dir, '/tmp/')
self.assertEquals(self.application.display_id, '99')
self.assertEquals(self.application.pid_filepath, "/tmp/application.pid")
def testStartTimeout(self):
"""Test if the attribute timeout is defined correctly"""
self.assertEquals(self.application.timeout, 20)
application = Application()
application.loadSettings('localhost', 9999, '/', '99', start_timeout=25)
self.assertEquals(application.timeout, 25)
def testgetAddress(self):
"""Test if getAddress() returns tuple with address correctly """
self.assertEquals(self.application.getAddress(), ('localhost', 9999))
def testPid(self):
"""As the application do not have the pid() should return None"""
self.assertEquals(self.application.pid(), None)
def testStart(self):
""" """
self.application.start()
self.assertEquals(exists(self.application.pid_filepath), True)
self.application.stop()
self.assertEquals(exists(self.application.pid_filepath), False)
def test_suite():
return make_suite(TestApplication)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestApplication)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from subprocess import Popen, PIPE
from base64 import decodestring
from os.path import exists, join
from os import remove
from zipfile import ZipFile, is_zipfile
from cloudooo.document import FileSystemDocument
from cloudoooTestCase import make_suite
class TestFileSystemDocument(unittest.TestCase):
"""Test to class FileSystemDocument"""
def setUp(self):
"""Create data to tests and instantiated a FileSystemDocument"""
self.tmp_url = '/tmp'
self.data = decodestring("cloudooo Test")
self.fsdocument = FileSystemDocument(self.tmp_url, self.data, 'txt')
def tearDown(self):
"""Remove the file in file system"""
if self.fsdocument.getUrl() is not None:
self.fsdocument.trash()
def testRestoreOriginal(self):
"""Test if changing the document and call remake, the document back to
original state"""
old_document_url = self.fsdocument.getUrl()
document_filename = "document"
document_test_url = join(self.fsdocument.directory_name, document_filename)
open(document_test_url,'wb').write(decodestring("Test Document"))
self.fsdocument.reload(document_test_url)
self.assertEquals(exists(old_document_url), False)
self.assertNotEquals(self.fsdocument.original_data,
self.fsdocument.getContent())
old_document_url = self.fsdocument.getUrl()
self.fsdocument.restoreOriginal()
self.assertEquals(exists(old_document_url), False)
self.assertNotEquals(old_document_url, self.fsdocument.getUrl())
self.assertEquals(exists(self.fsdocument.getUrl()), True)
self.assertEquals(self.fsdocument.getContent(), self.data)
def testgetContent(self):
"""Test if returns the data correctly"""
self.assertEquals(self.fsdocument.getContent(), self.data)
def testgetUrl(self):
"""Check if the url is correct"""
url = self.fsdocument.getUrl()
self.assertEquals(exists(url), True)
def testLoadDocumentFile(self):
"""Test if the document is created correctly"""
url = self.fsdocument.getUrl()
tmp_document = open(url,'r').read()
self.assertEquals(self.data, tmp_document)
self.fsdocument.trash()
self.assertEquals(exists(url), False)
def testReload(self):
"""Change url and check if occurs correctly"""
old_document_url = self.fsdocument.getUrl()
document_filename = "document"
document_test_url = join(self.fsdocument.directory_name, document_filename)
open(document_test_url,'wb').write(self.data)
self.fsdocument.reload(document_test_url)
url = self.fsdocument.getUrl()
self.assertEquals(exists(old_document_url), False)
self.assertEquals(self.fsdocument.getContent(), self.data)
self.fsdocument.trash()
self.assertEquals(exists(url), False)
def testZipDocumentList(self):
"""Tests if the zip file is returned correctly"""
zip_output_url = 'output/ziptest.zip'
open(join(self.fsdocument.directory_name, 'document2'), 'w').write('test')
zip_file = self.fsdocument.getContent(True)
open(zip_output_url, 'w').write(zip_file)
stdout, stderr = Popen("file %s" % zip_output_url,
shell=True, stdout=PIPE).communicate()
self.assertEquals(stdout, 'output/ziptest.zip: Zip archive data, at least v2.0 to extract\n')
ziptest = ZipFile(zip_output_url, 'r')
self.assertEquals(len(ziptest.filelist), 2)
for file in ziptest.filelist:
if file.filename.endswith("document2"):
self.assertEquals(file.file_size, 4)
else:
self.assertEquals(file.file_size, 9)
remove(zip_output_url)
def testSendZipFile(self):
"""Tests if the htm is extrated from zipfile"""
zip_input_url = 'data/test.zip'
zip_output_url = 'output/zipdocument.zip'
data = open(zip_input_url).read()
zipdocument = FileSystemDocument(self.tmp_url, data, 'zip')
open(zip_output_url, 'w').write(zipdocument.getContent(True))
self.assertEquals(is_zipfile(zip_output_url), True)
zipfile = ZipFile(zip_output_url)
self.assertEquals(sorted(zipfile.namelist()),
sorted(['logo.gif','test.htm']))
def test_suite():
return make_suite(TestFileSystemDocument)
if "__main__" == __name__:
suite = unittest.TestLoader().loadTestsFromTestCase(TestFileSystemDocument)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudooo.filter import Filter
from cloudoooTestCase import make_suite
class TestFilter(unittest.TestCase):
"""Test filter and your interface"""
def setUp(self):
"""Instatiated Filter with properties"""
extension = 'pdf'
filter = 'writer_pdf_Export'
mimetype = 'application/pdf'
document_type = "text"
preferred = True
sort_index = 1000
self.filter = Filter(extension, filter, mimetype, document_type,
preferred=preferred, sort_index=sort_index)
def testFilter(self):
"""Tests filter gets"""
self.assertEquals(self.filter.getExtension(), 'pdf')
self.assertEquals(self.filter.getName(), 'writer_pdf_Export')
self.assertEquals(self.filter.getMimetype(), 'application/pdf')
self.assertEquals(self.filter.getSortIndex(), 1000)
self.assertEquals(self.filter.isPreferred(), True)
def test_suite():
return make_suite(TestFilter)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestFilter)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
import os
import subprocess
from xmlrpclib import ServerProxy
from base64 import encodestring, decodestring
from multiprocessing import Process
from cloudoooTestCase import cloudoooTestCase, make_suite
class TestHighLoad(cloudoooTestCase):
"""Test with many simultaneous connection"""
def afterSetUp(self):
"""Creates connection with cloudooo Server"""
self.output_folder = "output"
self.proxy = ServerProxy("http://%s:%s" % (self.hostname, self.cloudooo_port))
def basicTestToGenerate(self, id, data, source_format, destination_format):
"""Test to use method generate of server"""
document = self.proxy.convertFile(data, source_format, destination_format)
document_output_url = os.path.join(self.output_folder,"%s.%s" % (id, destination_format))
open(document_output_url,'wb').write(decodestring(document))
stdout, stderr = subprocess.Popen("file %s" % document_output_url,
shell=True, stdout=subprocess.PIPE).communicate()
self.assertEquals(stdout, 'output/%s.pdf: PDF document, version 1.4\n' % id)
self.assertEquals(stderr, None)
os.remove(document_output_url)
self.assertEquals(os.path.exists(document_output_url), False)
def testGenerateHighLoad(self):
"""Sends many request to Server. Calling generate method"""
process_list = []
data = open("data/test.doc",'r').read()
for id in range(50):
process = Process(target=self.basicTestToGenerate, args=(id,
encodestring(data), 'doc', 'pdf'))
process.start()
process_list.append(process)
for proc in process_list[:]:
proc.join()
del proc
def test_suite():
return make_suite(TestHighLoad)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestHighLoad)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudooo.document import FileSystemDocument
from cloudooo.handler.oohandler import OOHandler
from cloudooo.application.openoffice import OpenOffice
from cloudooo.manager import Manager
from cloudooo.mimemapper import MimeMapper
from cloudooo.filter import Filter
from cloudooo.application.xvfb import Xvfb
from cloudooo.monitor.request import MonitorRequest
from cloudooo.interfaces.document import IDocument
from cloudooo.interfaces.lockable import ILockable
from cloudooo.interfaces.manager import IManager
from cloudooo.interfaces.application import IApplication
from cloudooo.interfaces.filter import IFilter
from cloudooo.interfaces.mimemapper import IMimemapper
from cloudooo.interfaces.handler import IHandler
from cloudooo.interfaces.monitor import IMonitor
from cloudoooTestCase import make_suite
class TestInterface(unittest.TestCase):
"""Test All Interfaces"""
def testIDocument(self):
"""Test if FileSystemDocument implements IDocument"""
self.assertEquals(IDocument.implementedBy(FileSystemDocument), True)
def testIFilter(self):
"""Test if Filter implements IDocument"""
self.assertEquals(IFilter.implementedBy(Filter),True)
self.assertEquals(IFilter.names(), ['getLabel', 'getName', 'getSortIndex',
'isPreferred', 'getDocumentService', 'getExtension', 'getMimetype'])
def testIManager(self):
"""Test if Manager implements IManager"""
self.assertEquals(IManager.implementedBy(Manager), True)
method_list = ['convertFile',
'getFileMetadataItemList',
'updateFileMetadata',
'getAllowedExtensionList']
for method in method_list:
self.assertEquals(method in IManager.names(), True)
self.assertEquals(len(method_list), len(IManager.names()))
self.assertEquals(IManager.get('convertFile').required, ('file',
'source_format', 'destination_format', 'zip'))
self.assertEquals(IManager.get('getAllowedExtensionList').required,
('request_dict',))
self.assertEquals(IManager.get('getFileMetadataItemList').required,
('file','source_format', 'base_document'))
self.assertEquals(IManager.get('updateFileMetadata').required,
('file','source_format', 'metadata_dict'))
def testIMimeMapper(self):
"""Test if Mimemapper implements IMimemapper."""
method_list = ['getDocumentTypeDict', 'getFilterName', 'loadFilterList',
'getFilterList', 'getAllowedExtensionList', 'getMimetypeByFilterType',
'isLoaded']
for method in method_list:
self.assertEquals(method in IMimemapper.names(), True)
self.assertEquals(IMimemapper.implementedBy(MimeMapper),True)
self.assertEquals(len(method_list),len(IMimemapper.names()))
self.assertEquals(IMimemapper.get('getFilterName').required, ('extension',
'document_type'))
self.assertEquals(IMimemapper.get('loadFilterList').required, ())
self.assertEquals(IMimemapper.get('getFilterList').required, ('extension',))
self.assertEquals(IMimemapper.get('getDocumentTypeDict').required, ())
self.assertEquals(IMimemapper.get('getAllowedExtensionList').required,
("document_type",))
self.assertEquals(IMimemapper.get('getMimetypeByFilterType').required,
('filter_type',))
def testIMonitor(self):
"""Test if Monitors implements IMonitor"""
self.assertEquals(IMonitor.implementedBy(MonitorRequest), True)
self.assertEquals(IMonitor.names(), ["run"])
def testIHandler(self):
"""Test if Handlers implements IHandler"""
self.assertEquals(IHandler.implementedBy(OOHandler), True)
method_list = ['convert', 'getMetadata', 'setMetadata']
for method in method_list:
self.assertEquals(method in IHandler.names(), True,
"Method %s is not declared" % method)
self.assertEquals(len(method_list), len(IHandler.names()))
self.assertEquals(IHandler.get('convert').required, ('destination_format',))
self.assertEquals(IHandler.get('getMetadata').required,
('converted_data',))
self.assertEquals(IHandler.get('setMetadata').required,
('metadata_dict',))
def testIApplication(self):
"""Test if OpenOffice implements IApplication"""
self.assertEquals(IApplication.implementedBy(OpenOffice), True)
self.assertEquals(IApplication.implementedBy(Xvfb), True)
application_method_list = ["start", "stop", "pid",
"status", "restart",
"loadSettings", "getAddress"]
self.assertEquals(sorted(IApplication.names()),
sorted(application_method_list))
def testILockable(self):
"""Test if Openoffice implements ILockable"""
self.assertEquals(ILockable.implementedBy(OpenOffice), True)
lockable_method_list = ["_lock", "acquire", "release", "isLocked"]
self.assertEquals(sorted(ILockable.names()), sorted(lockable_method_list))
def test_suite():
return make_suite(TestInterface)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestInterface)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudoooTestCase import cloudoooTestCase
from cloudooo.application.openoffice import openoffice
from cloudooo.mimemapper import MimeMapper
from cloudoooTestCase import make_suite
text_expected_tuple = (('doc', 'Microsoft Word 6.0'),
('doc', 'Microsoft Word 95'),
('doc', 'Microsoft Word 97/2000/XP'),
('htm', 'HTML Document (OpenOffice.org Writer)'),
('html', 'HTML Document (OpenOffice.org Writer)'),
('html', 'XHTML'), ('odt', 'ODF Text Document'),
('ott', 'ODF Text Document Template'),
('pdf', 'PDF - Portable Document Format'),
('rtf', 'Rich Text Format'), ('sdw', 'StarWriter 3.0'),
('sdw', 'StarWriter 4.0'), ('sdw', 'StarWriter 5.0'),
('sxw', 'OpenOffice.org 1.0 Text Document'),
('txt', 'Text'), ('txt', 'Text Encoded'),
('xhtml', 'XHTML'),
(u'pdb', u'AportisDoc (Palm)'),
(u'psw', u'Pocket Word'))
global_expected_tuple = (('sdw', 'StarWriter 3.0'),
('sdw', 'StarWriter 4.0'),
('sgl', 'StarWriter 4.0 Master Document'),
('sdw', 'StarWriter 5.0'),
('sgl', 'StarWriter 5.0 Master Document'),
('txt', 'Text Encoded (OpenOffice.org Master Document)'),
('sxw', 'OpenOffice.org 1.0 Text Document'),
('sxg', 'OpenOffice.org 1.0 Master Document'),
('pdf', 'PDF - Portable Document Format'),
('odm', 'ODF Master Document'),
('odt', 'ODF Text Document'),
('html', 'HTML (Writer/Global)'),
('htm', 'HTML (Writer/Global)'))
drawing_expected_tuple = (('bmp', 'BMP - Windows Bitmap'),
('emf', 'EMF - Enhanced Metafile'),
('eps', 'EPS - Encapsulated PostScript'),
('gif', 'GIF - Graphics Interchange Format'),
('htm', 'HTML Document (OpenOffice.org Draw)'),
('html', 'HTML Document (OpenOffice.org Draw)'),
('html', 'XHTML'),
('jfif', 'JPEG - Joint Photographic Experts Group'),
('jif', 'JPEG - Joint Photographic Experts Group'),
('jpe', 'JPEG - Joint Photographic Experts Group'),
('jpeg', 'JPEG - Joint Photographic Experts Group'),
('jpg', 'JPEG - Joint Photographic Experts Group'),
('met', 'MET - OS/2 Metafile'), ('odg', 'ODF Drawing'),
('otg', 'ODF Drawing Template'), ('pbm', 'PBM - Portable Bitmap'),
('pct', 'PCT - Mac Pict'),
('pdf', 'PDF - Portable Document Format'),
('pgm', 'PGM - Portable Graymap'),
('pict', 'PCT - Mac Pict'),
('png', 'PNG - Portable Network Graphic'),
('ppm', 'PPM - Portable Pixelmap'),
('ras', 'RAS - Sun Raster Image'), ('sda', 'StarDraw 5.0'),
('sdd', 'StarDraw 3.0'),
('svg', 'SVG - Scalable Vector Graphics'),
('svm', 'SVM - StarView Metafile'),
('sxd', 'OpenOffice.org 1.0 Drawing'),
('tif', 'TIFF - Tagged Image File Format'),
('tiff', 'TIFF - Tagged Image File Format'),
('wmf', 'WMF - Windows Metafile'),
('xhtml', 'XHTML'), ('xpm', 'XPM - X PixMap'))
web_expected_tuple = (('html', 'HTML Document'),
('htm', 'HTML Document'),
('txt', 'Text (OpenOffice.org Writer/Web)'),
('txt', 'Text Encoded (OpenOffice.org Writer/Web)'),
('sxw', 'OpenOffice.org 1.0 Text Document (OpenOffice.org Writer/Web)'),
('pdf', 'PDF - Portable Document Format'),
('odt', 'OpenOffice.org Text (OpenOffice.org Writer/Web)'),
('sdw', 'StarWriter 3.0 (OpenOffice.org Writer/Web)'),
('sdw', 'StarWriter 4.0 (OpenOffice.org Writer/Web)'),
('sdw', 'StarWriter 5.0 (OpenOffice.org Writer/Web)'))
presentation_expected_tuple = (('bmp', 'BMP - Windows Bitmap'),
('emf', 'EMF - Enhanced Metafile'),
('eps', 'EPS - Encapsulated PostScript'),
('gif', 'GIF - Graphics Interchange Format'),
('htm', 'HTML Document (OpenOffice.org Impress)'),
('html', 'HTML Document (OpenOffice.org Impress)'),
('html', 'XHTML'), ('jfif', 'JPEG - Joint Photographic Experts Group'),
('jif', 'JPEG - Joint Photographic Experts Group'),
('jpe', 'JPEG - Joint Photographic Experts Group'),
('jpeg', 'JPEG - Joint Photographic Experts Group'),
('jpg', 'JPEG - Joint Photographic Experts Group'),
('met', 'MET - OS/2 Metafile'), ('odg', 'ODF Drawing (Impress)'),
('odp', 'ODF Presentation'), ('otp', 'ODF Presentation Template'),
('pbm', 'PBM - Portable Bitmap'), ('pct', 'PCT - Mac Pict'),
('pdf', 'PDF - Portable Document Format'),
('pgm', 'PGM - Portable Graymap'),
('pict', 'PCT - Mac Pict'),
('png', 'PNG - Portable Network Graphic'),
('pot', 'Microsoft PowerPoint 97/2000/XP Template'),
('ppm', 'PPM - Portable Pixelmap'),
('pps', 'Microsoft PowerPoint 97/2000/XP'),
('ppt', 'Microsoft PowerPoint 97/2000/XP'),
('ras', 'RAS - Sun Raster Image'),
('sda', 'StarDraw 5.0 (OpenOffice.org Impress)'),
('sdd', 'StarDraw 3.0 (OpenOffice.org Impress)'),
('sdd', 'StarImpress 4.0'), ('sdd', 'StarImpress 5.0'),
('svg', 'SVG - Scalable Vector Graphics'),
('svm', 'SVM - StarView Metafile'),
('sxd', 'OpenOffice.org 1.0 Drawing (OpenOffice.org Impress)'),
('sxi', 'OpenOffice.org 1.0 Presentation'),
('tif', 'TIFF - Tagged Image File Format'),
('tiff', 'TIFF - Tagged Image File Format'),
('wmf', 'WMF - Windows Metafile'),
('xhtml', 'XHTML'), ('xpm', 'XPM - X PixMap'))
spreadsheet_expected_tuple = (('csv', 'Text CSV'),
('htm', 'HTML Document (OpenOffice.org Calc)'),
('html', 'HTML Document (OpenOffice.org Calc)'),
('html', 'XHTML'), ('ods', 'ODF Spreadsheet'),
('ots', 'ODF Spreadsheet Template'),
('pdf', 'PDF - Portable Document Format'),
('sdc', 'StarCalc 3.0'), ('sdc', 'StarCalc 4.0'),
('sdc', 'StarCalc 5.0'), ('sxc', 'OpenOffice.org 1.0 Spreadsheet'),
('txt', 'Text CSV'), ('xhtml', 'XHTML'),
('xlc', 'Microsoft Excel 4.0'), ('xlc', 'Microsoft Excel 5.0'),
('xlc', 'Microsoft Excel 95'), ('xlc', 'Microsoft Excel 97/2000/XP'),
('xlm', 'Microsoft Excel 4.0'), ('xlm', 'Microsoft Excel 5.0'),
('xlm', 'Microsoft Excel 95'), ('xlm', 'Microsoft Excel 97/2000/XP'),
('xls', 'Microsoft Excel 4.0'), ('xls', 'Microsoft Excel 5.0'),
('xls', 'Microsoft Excel 95'), ('xls', 'Microsoft Excel 97/2000/XP'),
('xls', 'Text CSV'), ('xlt', 'Microsoft Excel 5.0 Template'),
('xlt', 'Microsoft Excel 95 Template'),
('xlt', 'Microsoft Excel 97/2000/XP Template'),
('xlw', 'Microsoft Excel 4.0'), ('xlw', 'Microsoft Excel 5.0'),
('xlw', 'Microsoft Excel 95'), ('xlw', 'Microsoft Excel 97/2000/XP'))
math_expected_tuple = (('smf', 'StarMath 4.0'), ('mml', 'MathML 1.01'),
('pdf', 'PDF - Portable Document Format'), ('smf', 'StarMath 5.0'),
('sxm', 'OpenOffice.org 1.0 Formula'), ('odf', 'ODF Formula'),
('smf', 'StarMath 3.0'))
chart_expected_tuple = (('sds', 'StarChart 3.0'),
('sds', 'StarChart 4.0'),
('sds', 'StarChart 5.0'),
('sxs', 'OpenOffice.org 1.0 Chart'),
('odc', 'ODF Chart'))
class TestMimeMapper(cloudoooTestCase):
"""Test if object load filters correctly of OOo."""
def afterSetUp(self):
"""Mimemapper is created and load uno path."""
self.mimemapper = MimeMapper()
openoffice.acquire()
hostname, port = openoffice.getAddress()
self.mimemapper.loadFilterList(hostname,
port,
unomimemapper_bin=self.unomimemapper_bin,
python_path=self.python_path)
openoffice.release()
def testUnoMimemapperPath(self):
"""Test if the unomimemapper has the path to script"""
mimemapper = MimeMapper()
openoffice.acquire()
hostname, port = openoffice.getAddress()
try:
mimemapper.loadFilterList(hostname, port)
except:
self.assertEquals(mimemapper.unomimemapper_bin, "/usr/bin/unomimemapper.py")
finally:
openoffice.release()
openoffice.acquire()
try:
mimemapper.loadFilterList(hostname, port, unomimemapper_bin="/x/y/unomimemapper")
except NameError:
self.assertEquals(mimemapper.unomimemapper_bin, "/x/y/unomimemapper")
finally:
openoffice.release()
def testGetFilterWhenExtensionNotExist(self):
"""Test the case that the user passes extension which does not exist."""
empty_list = self.mimemapper.getFilterList('xxx')
self.assertEquals(empty_list, [])
def testIfThereIsDuplicateData(self):
"""Test if there is duplicate data."""
extension_list = self.mimemapper._doc_type_list_by_extension.keys()
self.assertEquals(len(extension_list), len(set(extension_list)))
for type_list in self.mimemapper._doc_type_list_by_extension.values():
self.assertEquals(len(type_list), len(set(type_list)))
document_type_list = self.mimemapper._document_type_dict.keys()
self.assertEquals(len(document_type_list), len(set(document_type_list)))
document_service_list = self.mimemapper._document_type_dict.values()
self.assertEquals(len(document_service_list), len(set(document_service_list)))
document_service_list = self.mimemapper._extension_list_by_type.keys()
self.assertEquals(len(document_service_list), len(set(document_service_list)))
extension_list = self.mimemapper._extension_list_by_type.values()
for extension in extension_list:
self.assertEquals(len(extension), len(set(extension)),
"extension_list_by_type has duplicate data")
def testGetFilterByExt(self):
"""Test if passing the extension the filter returns corretcly."""
pdf_filter_list = self.mimemapper.getFilterList('pdf')
self.assertEquals(len(pdf_filter_list),7)
xls_filter_list = self.mimemapper.getFilterList('xls')
self.assertEquals(len(xls_filter_list),5)
doc_filter_list = self.mimemapper.getFilterList('doc')
self.assertEquals(len(doc_filter_list),3)
def testGetDocumentTypeDict(self):
"""Test if dictonary document type returns type correctly."""
document_type_dict = self.mimemapper._document_type_dict
type = document_type_dict.get("text")
self.assertEquals(type, 'com.sun.star.text.TextDocument')
type = document_type_dict.get("chart")
self.assertEquals(type, 'com.sun.star.chart2.ChartDocument')
type = document_type_dict.get("drawing")
self.assertEquals(type, 'com.sun.star.drawing.DrawingDocument')
type = document_type_dict.get("presentation")
self.assertEquals(type, 'com.sun.star.presentation.PresentationDocument')
type = document_type_dict.get("spreadsheet")
self.assertEquals(type, 'com.sun.star.sheet.SpreadsheetDocument')
type = document_type_dict.get("web")
self.assertEquals(type, 'com.sun.star.text.WebDocument')
def testGetAllowedExtensionListByExtension(self):
"""Test if function getAllowedExtensionList returns correctly a list with
extensions that can generate with extension passed."""
doc_got_list = list(self.mimemapper.getAllowedExtensionList('doc'))
doc_got_list.sort()
text_expected_list = list(text_expected_tuple)
text_expected_list.sort()
self.assertEquals(doc_got_list, text_expected_list)
jpeg_got_list = list(self.mimemapper.getAllowedExtensionList('jpeg'))
jpeg_got_list.sort()
jpeg_expected_list = list(set(presentation_expected_tuple +
drawing_expected_tuple))
jpeg_expected_list.sort()
self.assertEquals(jpeg_got_list, jpeg_expected_list)
pdf_got_list = list(self.mimemapper.getAllowedExtensionList('pdf'))
pdf_got_list.sort()
pdf_expected_list = list(set(presentation_expected_tuple +
drawing_expected_tuple + web_expected_tuple + global_expected_tuple +
math_expected_tuple + text_expected_tuple + spreadsheet_expected_tuple))
pdf_expected_list.sort()
self.assertEquals(pdf_got_list, pdf_expected_list)
def testGetAllowedExtensionListForText(self):
"""Passing document_type equal to 'text', the return must be equal
to text_expected_tuple."""
got_list = list(self.mimemapper.getAllowedExtensionList(document_type='text'))
got_list.sort()
text_expected_list = list(text_expected_tuple)
text_expected_list.sort()
self.assertEquals(got_list, text_expected_list)
def testGetAllowedExtensionListForGlobal(self):
"""Passing document_type equal to 'global', the return must be equal
to global_expected_tuple."""
got_list = list(self.mimemapper.getAllowedExtensionList(document_type='global'))
got_list.sort()
global_expected_list = list(global_expected_tuple)
global_expected_list.sort()
self.assertEquals(got_list, global_expected_list)
def testGetAllAllowedExtensionListForDrawing(self):
"""Passing document_type equal to 'drawing', the return must be equal
to drawing_expected_tuple."""
got_list = list(self.mimemapper.getAllowedExtensionList(document_type='drawing'))
got_list.sort()
drawing_expected_list = list(drawing_expected_tuple)
drawing_expected_list.sort()
self.assertEquals(got_list, drawing_expected_list)
def testGetAllAllowedExtensionListForWeb(self):
"""Passing document_type equal to 'web', the return must be equal
to web_expected_tuple."""
got_tuple = list(self.mimemapper.getAllowedExtensionList(document_type='web'))
got_tuple.sort()
web_expected_list = list(web_expected_tuple)
web_expected_list.sort()
self.assertEquals(got_tuple, web_expected_list)
def testGetAllAllowedExtensionListForPresentation(self):
"""Passing document_type equal to 'presentation', the return must be equal
to presentation_expected_tuple."""
got_list = \
list(self.mimemapper.getAllowedExtensionList(document_type='presentation'))
got_list.sort()
presentation_expected_list = list(presentation_expected_tuple)
presentation_expected_list.sort()
self.assertEquals(got_list, presentation_expected_list)
def testGetAllAllowedExtensionListForSpreadsheet(self):
"""Passing document_type equal to 'spreadsheet', the return must be equal
to spreadsheet_expected_tuple."""
got_tuple = self.mimemapper.getAllowedExtensionList(document_type='spreadsheet')
self.assertEquals(sorted(got_tuple), sorted(spreadsheet_expected_tuple))
def testGetAllAllowedExtensionListForChart(self):
"""Passing document_type equal to 'chart', the return must be equal
to chart_expected_tuple."""
got_list = list(self.mimemapper.getAllowedExtensionList(document_type='chart'))
got_list.sort()
chart_expected_list = list(chart_expected_tuple)
chart_expected_list.sort()
self.assertEquals(got_list, chart_expected_list)
def testGetFilterName(self):
"""Test if passing extension and document_type, the filter is correct."""
filtername = self.mimemapper.getFilterName("pdf", 'com.sun.star.text.TextDocument')
self.assertEquals(filtername, "writer_pdf_Export")
filtername = self.mimemapper.getFilterName('ppt', 'com.sun.star.presentation.PresentationDocument')
self.assertEquals(filtername,"MS PowerPoint 97")
filtername = self.mimemapper.getFilterName("html", 'com.sun.star.presentation.PresentationDocument')
self.assertEquals(filtername, "impress_html_Export")
def testGetMimetype(self):
"""Test get mimetype according to the filter type"""
self.assertEquals(self.mimemapper.getMimetypeByFilterType("writer8"),\
"application/vnd.oasis.opendocument.text")
self.assertEquals(self.mimemapper.getMimetypeByFilterType("math8"),\
"application/vnd.oasis.opendocument.formula")
self.assertEquals(self.mimemapper.getMimetypeByFilterType("writer_MS_Word_97"),\
'application/msword')
def test_suite():
return make_suite(TestMimeMapper)
if '__main__' == __name__:
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestMimeMapper)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
import cloudooo.monitor
from cloudoooTestCase import cloudoooTestCase, make_suite
from cloudooo.monitor.request import MonitorRequest
from cloudooo.monitor.memory import MonitorMemory
class TestMonitorInit(cloudoooTestCase):
"""Test Case to test if the monitors are controlled correctly"""
def afterSetUp(self):
"""Create one fake file configuration"""
self.load_config = {}
self.load_config['monitor_interval'] = 1
self.load_config['limit_number_request'] = 100
self.load_config['limit_memory_used'] = 500
def tearDown(self):
"""stop all monitors"""
cloudooo.monitor.stop()
def testMonitorInitGlobalAttributes(self):
"""Test the global attributes"""
self.assertEquals(cloudooo.monitor.monitor_request, None)
self.assertEquals(cloudooo.monitor.monitor_memory, None)
def testMonitorLoadOnlyMonitorRequest(self):
"""Check if the monitors are started"""
cloudooo.monitor.load(self.load_config)
self.assertEquals(isinstance(cloudooo.monitor.monitor_request, MonitorRequest),
True)
self.assertEquals(cloudooo.monitor.monitor_memory, None)
def testMonitorLoadMonitorMemory(self):
"""Check if the MemoryMemory is started"""
self.load_config['enable_memory_monitor'] = True
cloudooo.monitor.load(self.load_config)
self.assertEquals(isinstance(cloudooo.monitor.monitor_request, MonitorRequest),
True)
self.assertEquals(isinstance(cloudooo.monitor.monitor_memory, MonitorMemory),
True)
def test_suite():
return make_suite(TestMonitorInit)
if "__main__" == __name__:
suite = unittest.TestLoader().loadTestsFromTestCase(TestMonitorInit)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from time import sleep
from cloudooo.application.openoffice import openoffice
from cloudooo.monitor.memory import MonitorMemory
from psutil import Process
from types import IntType
from cloudoooTestCase import make_suite
class TestMonitorMemory(unittest.TestCase):
"""Test case to see if the MonitorMemory is properly managing the
openoffice."""
def setUp(self):
if not openoffice.status():
openoffice.start()
def tearDown(self):
"""Restart the openoffice in cases that the openoffice is stopped"""
if not openoffice.status():
openoffice.acquire()
openoffice.restart()
openoffice.release()
if self.monitor.is_alive():
self.monitor.terminate()
def testMonitorWithHugeMemoryLimit(self):
"""Test the monitor with limit of 1Gb to the OpenOffice process"""
try:
self.monitor = MonitorMemory(openoffice, 1, 1000)
self.monitor.start()
sleep(2)
self.assertEquals(openoffice.status(), True)
finally:
self.monitor.terminate()
def testMonitorWithLittleMemoryLimit(self):
"""Test the monitor with limit of 10Mb. In This case the openoffice will be
stopped"""
try:
self.monitor = MonitorMemory(openoffice, 2, 10)
self.monitor.start()
sleep(3)
self.assertEquals(openoffice.status(), False)
finally:
self.monitor.terminate()
def testMonitorWithOpenOfficeStopped(self):
"""Tests if the monitor continues to run even with openoffice stopped"""
openoffice.stop()
self.monitor = MonitorMemory(openoffice, 2, 1000)
try:
self.monitor.start()
sleep(3)
self.assertEquals(self.monitor.is_alive(), True)
finally:
self.monitor.terminate()
def testCreateProcess(self):
"""Test if the psutil.Process is create correctly"""
self.monitor = MonitorMemory(openoffice, 2, 1000)
self.monitor.create_process()
self.assertEquals(hasattr(self.monitor, 'process'), True)
self.assertEquals(type(self.monitor.process), Process)
def testGetMemoryUsage(self):
"""Test memory usage"""
self.monitor = MonitorMemory(openoffice, 2, 1000)
openoffice.stop()
memory_usage_int = self.monitor.get_memory_usage()
self.assertEquals(memory_usage_int, 0)
if not openoffice.status():
openoffice.start()
memory_usage_int = self.monitor.get_memory_usage()
self.assertEquals(type(memory_usage_int), IntType)
def test_suite():
return make_suite(TestMonitorMemory)
if "__main__" == __name__:
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestMonitorMemory)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from time import sleep
from cloudooo.monitor.request import MonitorRequest
from cloudoooTestCase import cloudoooTestCase, make_suite
from cloudooo.application.openoffice import openoffice
class TestMonitorRequest(cloudoooTestCase):
"""Test all features of a monitor following the interface"""
def testMonitorTerminate(self):
"""Test terminate function to stop the thread"""
monitor_request = MonitorRequest(openoffice, 0, 2)
monitor_request.start()
self.assertEquals(monitor_request.is_alive(), True)
monitor_request.terminate()
sleep(4)
try:
self.assertEquals(monitor_request.is_alive(), False)
finally:
monitor_request.terminate()
def testMonitorRequest(self):
"""Test if openoffice is monitored correclty"""
openoffice.request = 3
self.assertEquals(openoffice.request, 3)
monitor_request = MonitorRequest(openoffice, 1, 2)
monitor_request.start()
sleep(4)
self.assertEquals(openoffice.request, 0)
monitor_request.terminate()
def test_suite():
return make_suite(TestMonitorRequest)
if "__main__" == __name__:
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestMonitorRequest)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from time import sleep
from cloudooo.application.openoffice import openoffice
from cloudooo.monitor.timeout import MonitorTimeout
from cloudoooTestCase import make_suite
class TestMonitorTimeout(unittest.TestCase):
"""Test all features of a monitor following the interface"""
def _createMonitor(self, interval):
"""Returns an MonitorTimeout instance"""
return MonitorTimeout(openoffice, interval)
def testMonitorTimeoutStatus(self):
"""Test if the monitor of memory works"""
monitor_timeout = self._createMonitor(10)
monitor_timeout.start()
self.assertEquals(monitor_timeout.is_alive(), True)
monitor_timeout.terminate()
monitor_timeout = self._createMonitor(1)
monitor_timeout.start()
sleep(2)
self.assertEquals(monitor_timeout.is_alive(), False)
monitor_timeout.terminate()
def testStopOpenOffice(self):
"""Test if the openoffice stop by the monitor"""
openoffice.acquire()
try:
monitor_timeout = self._createMonitor(1)
monitor_timeout.start()
sleep(2)
self.assertEquals(openoffice.status(), False)
openoffice.restart()
self.assertEquals(openoffice.status(), True)
finally:
monitor_timeout.terminate()
openoffice.release()
def testStopOpenOfficeTwice(self):
"""Test the cases that is necessary start the monitors twice"""
openoffice.acquire()
try:
monitor_timeout = self._createMonitor(1)
monitor_timeout.start()
sleep(2)
self.assertEquals(openoffice.status(), False)
monitor_timeout.terminate()
openoffice.restart()
self.assertEquals(openoffice.status(), True)
monitor_timeout = self._createMonitor(1)
monitor_timeout.start()
sleep(2)
self.assertEquals(openoffice.status(), False)
monitor_timeout.terminate()
sleep(1)
self.assertEquals(monitor_timeout.is_alive(), False)
finally:
monitor_timeout.terminate()
openoffice.release()
def test_suite():
return make_suite(TestMonitorTimeout)
if "__main__" == __name__:
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestMonitorTimeout)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from subprocess import Popen, PIPE
from base64 import encodestring, decodestring
from cloudoooTestCase import cloudoooTestCase
from cloudooo.handler.oohandler import OOHandler
from cloudooo.application.openoffice import openoffice
from cloudoooTestCase import make_suite
class TestOOHandler(cloudoooTestCase):
"""Test OOHandler and manipulation of OOo Instance"""
def _save_document(self, document_output_url, data):
"""Create document in file system"""
open(document_output_url, "w").write(data)
def _assert_document_output(self, document_output_url, msg):
"""Check if the document was created correctly"""
stdout, stderr = Popen("file %s" % document_output_url,
shell=True,
stdout=PIPE).communicate()
self.assertEquals(msg in stdout,
True,
"\nStdout: %sMsg: %s" % (stdout, msg))
def afterSetUp(self):
""" """
self.kw = dict(unoconverter_bin=self.unoconverter_bin,
python_path=self.python_path)
def testConvertOdtToDoc(self):
"""Test convert ODT to DOC"""
data = encodestring(open("data/test.odt").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'odt',
**self.kw)
doc_exported = handler.convert("doc")
document_output_url = "output/testExport.doc"
self._save_document(document_output_url, doc_exported)
msg = 'Microsoft Office Document'
self._assert_document_output(document_output_url, msg)
def testConvertDocToOdt(self):
"""Test convert DOC to ODT"""
data = encodestring(open("data/test.doc").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'doc',
**self.kw)
doc_exported = handler.convert("odt")
document_output_url = "output/testConvert.odt"
self._save_document(document_output_url, doc_exported)
msg = 'output/testConvert.odt: OpenDocument Text\n'
self._assert_document_output(document_output_url, msg)
def testGetMetadata(self):
"""Test getMetadata"""
data = encodestring(open("data/test.odt").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'odt',
**self.kw)
metadata = handler.getMetadata()
self.assertEquals(metadata.get('Data'), '')
self.assertEquals(metadata.has_key('Data'), True)
self.assertEquals(metadata.get('MIMEType'),
'application/vnd.oasis.opendocument.text')
handler.document.restoreOriginal()
metadata = handler.getMetadata(True)
self.assertNotEquals(metadata.get('Data'), '')
def testSetMetadata(self):
"""Test setMetadata"""
data = encodestring(open("data/test.odt").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'odt',
**self.kw)
new_data = handler.setMetadata({"Title": "cloudooo Test -"})
new_handler = OOHandler(self.tmp_url,
new_data,
'odt',
**self.kw)
metadata = new_handler.getMetadata()
self.assertEquals(metadata.get('Title'), "cloudooo Test -")
def testConvertWithOpenOfficeStopped(self):
"""Test convert with openoffice stopped"""
openoffice.stop()
data = encodestring(open("data/test.doc").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'doc',
**self.kw)
doc_exported = handler.convert("odt")
document_output_url = "output/testConvert.odt"
self._save_document(document_output_url, doc_exported)
msg = 'output/testConvert.odt: OpenDocument Text\n'
self._assert_document_output(document_output_url, msg)
def testGetMetadataWithOpenOfficeStopped(self):
"""Test getMetadata with openoffice stopped"""
openoffice.stop()
data = encodestring(open("data/test.odt").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'odt',
**self.kw)
metadata = handler.getMetadata()
self.assertEquals(metadata.get('Title'), 'title')
self.assertEquals(metadata.get('MIMEType'),
'application/vnd.oasis.opendocument.text')
def testSetMetadataWithOpenOfficeStopped(self):
"""Test setMetadata with openoffice stopped"""
openoffice.stop()
data = encodestring(open("data/test.doc").read())
handler = OOHandler(self.tmp_url,
decodestring(data),
'doc',
**self.kw)
new_data = handler.setMetadata({"Title": "cloudooo Test -"})
new_handler = OOHandler(self.tmp_url,
new_data,
'doc',
**self.kw)
metadata = new_handler.getMetadata()
self.assertEquals(metadata.get('Title'), "cloudooo Test -")
def test_suite():
return make_suite(TestOOHandler)
if __name__ == "__main__":
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestOOHandler)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudoooTestCase import cloudoooTestCase
from cloudooo.application.openoffice import OpenOffice
from cloudoooTestCase import make_suite
from cloudooo.utils import waitStopDaemon
class TestOpenOffice(cloudoooTestCase):
"""Test OpenOffice object and manipulation of OOo Instance"""
def afterSetUp(self):
"""Instantiate one OpenOffice"""
self.openoffice = OpenOffice()
self.openoffice.loadSettings("localhost", 4090,
self.path_dir_run_cloudooo,
self.virtual_display_id,
self.office_bin_path,
self.uno_path)
self.openoffice.start()
def tearDown(self):
"""Stop the OpenOffice"""
if self.openoffice.status():
self.openoffice.stop()
def testPid(self):
"""Test pid function to validate if the return is correctly"""
self.assertNotEquals(self.openoffice.pid(), None)
self.openoffice.stop()
waitStopDaemon(self.openoffice)
self.assertEquals(self.openoffice.pid(), None)
def testOpenOfficeStart(self):
"""Test if the start method works correclty"""
self.assertEquals(self.openoffice.status(), True)
def testOpenOfficeStop(self):
"""Test if the stop method works correctly"""
self.openoffice.stop()
waitStopDaemon(self.openoffice)
self.assertEquals(self.openoffice.status(), False)
def testOpenOfficeRequest(self):
"""Test if the requisition amount is increasing right"""
self.openoffice.acquire()
self.assertEquals(self.openoffice.request, 1)
self.openoffice.release()
def testOpenOfficeRestart(self):
"""Test if the method restart works correctly"""
self.openoffice.restart()
self.assertEquals(self.openoffice.status(), True)
def testOpenOfficeLock(self):
"""Test lock and unlock"""
self.openoffice.acquire()
self.assertEquals(self.openoffice.isLocked(), True)
self.openoffice.release()
self.assertEquals(self.openoffice.isLocked(), False)
def test_suite():
return make_suite(TestOpenOffice)
if __name__ == "__main__":
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment(False)
suite = unittest.TestLoader().loadTestsFromTestCase(TestOpenOffice)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from os.path import join
from subprocess import Popen, PIPE
from xmlrpclib import ServerProxy, Fault
from base64 import encodestring, decodestring
from cloudoooTestCase import cloudoooTestCase, make_suite
from types import DictType
class TestServer(cloudoooTestCase):
"""Test XmlRpc Server. Needs cloudooo server started"""
def afterSetUp(self):
"""Creates a connection with cloudooo server"""
self.proxy = ServerProxy("http://%s:%s/RPC2" % \
(self.hostname, self.cloudooo_port), allow_none=True)
self.text_expected_list = [['doc', 'Microsoft Word 6.0'],
['doc', 'Microsoft Word 95'],
['doc', 'Microsoft Word 97/2000/XP'],
['htm', 'HTML Document (OpenOffice.org Writer)'],
['html', 'HTML Document (OpenOffice.org Writer)'],
['html', 'XHTML'], ['odt', 'ODF Text Document'],
['ott', 'ODF Text Document Template'],
['pdf', 'PDF - Portable Document Format'],
['rtf', 'Rich Text Format'], ['sdw', 'StarWriter 3.0'],
['sdw', 'StarWriter 4.0'], ['sdw', 'StarWriter 5.0'],
['sxw', 'OpenOffice.org 1.0 Text Document'],
['txt', 'Text'], ['txt', 'Text Encoded'],
['xhtml', 'XHTML'], ['pdb', 'AportisDoc (Palm)'],
['psw', 'Pocket Word']]
self.text_expected_list.sort()
self.presentation_expected_list = [['bmp', 'BMP - Windows Bitmap'],
['emf', 'EMF - Enhanced Metafile'],
['eps', 'EPS - Encapsulated PostScript'],
['gif', 'GIF - Graphics Interchange Format'],
['htm', 'HTML Document (OpenOffice.org Impress)'],
['html', 'HTML Document (OpenOffice.org Impress)'],
['html', 'XHTML'], ['jfif', 'JPEG - Joint Photographic Experts Group'],
['jif', 'JPEG - Joint Photographic Experts Group'],
['jpe', 'JPEG - Joint Photographic Experts Group'],
['jpeg', 'JPEG - Joint Photographic Experts Group'],
['jpg', 'JPEG - Joint Photographic Experts Group'],
['met', 'MET - OS/2 Metafile'], ['odg', 'ODF Drawing (Impress)'],
['odp', 'ODF Presentation'],
['otp', 'ODF Presentation Template'],
['pbm', 'PBM - Portable Bitmap'], ['pct', 'PCT - Mac Pict'],
['pdf', 'PDF - Portable Document Format'],
['pgm', 'PGM - Portable Graymap'], ['pict', 'PCT - Mac Pict'],
['png', 'PNG - Portable Network Graphic'],
['pot', 'Microsoft PowerPoint 97/2000/XP Template'],
['ppm', 'PPM - Portable Pixelmap'],
['pps', 'Microsoft PowerPoint 97/2000/XP'],
['ppt', 'Microsoft PowerPoint 97/2000/XP'],
['ras', 'RAS - Sun Raster Image'],
['sda', 'StarDraw 5.0 (OpenOffice.org Impress)'],
['sdd', 'StarDraw 3.0 (OpenOffice.org Impress)'],
['sdd', 'StarImpress 4.0'], ['sdd', 'StarImpress 5.0'],
['svg', 'SVG - Scalable Vector Graphics'],
['svm', 'SVM - StarView Metafile'],
['sxd', 'OpenOffice.org 1.0 Drawing (OpenOffice.org Impress)'],
['sxi', 'OpenOffice.org 1.0 Presentation'],
['tif', 'TIFF - Tagged Image File Format'],
['tiff', 'TIFF - Tagged Image File Format'],
['wmf', 'WMF - Windows Metafile'],
['xhtml', 'XHTML'], ['xpm', 'XPM - X PixMap']]
self.presentation_expected_list.sort()
def _testConvertFile(self, input_url, output_url,
source_format, destination_format,
stdout_msg, zip=False):
"""Generic test to use convertFile"""
data = encodestring(open(input_url).read())
output_data = self.proxy.convertFile(data,
source_format,
destination_format, zip)
open(output_url, 'w').write(decodestring(output_data))
stdout, stderr = Popen("file %s" % output_url, shell=True,
stdout=PIPE).communicate()
self.assertEquals(stdout, stdout_msg)
self.assertEquals(stderr, None)
def testGetAllowedExtensionListByType(self):
"""Call getAllowedExtensionList and verify if the returns is a list with
extension and ui_name. The request is by document type"""
text_request = {'document_type':"text"}
text_allowed_list = self.proxy.getAllowedExtensionList(text_request)
text_allowed_list.sort()
self.assertEquals(text_allowed_list, self.text_expected_list)
request_dict = {'document_type':"presentation"}
presentation_allowed_list = self.proxy.getAllowedExtensionList(request_dict)
presentation_allowed_list.sort()
self.assertEquals(presentation_allowed_list,
self.presentation_expected_list)
def testGetAllowedExtensionListByExtension(self):
"""Call getAllowedExtensionList and verify if the returns is a list with
extension and ui_name. The request is by extension"""
doc_allowed_list = self.proxy.getAllowedExtensionList({'extension':"doc"})
doc_allowed_list.sort()
self.assertEquals(doc_allowed_list, self.text_expected_list)
def testGetAllowedExtensionListByMimetype(self):
"""Call getAllowedExtensionList and verify if the returns is a list with
extension and ui_name. The request is by mimetype"""
request_dict = {"mimetype":"application/msword"}
msword_allowed_list = self.proxy.getAllowedExtensionList(request_dict)
msword_allowed_list.sort()
self.assertEquals(msword_allowed_list, self.text_expected_list)
def testConvertDocToOdt(self):
"""Test Convert Doc -> Odt"""
self._testConvertFile("data/test.doc",
"output/document_output.odt",
'doc',
'odt',
'output/document_output.odt: OpenDocument Text\n')
def testconvertDocToPdf(self):
"""Test Convert Doc -> Pdf"""
self._testConvertFile("data/test.doc",
"output/document_output.pdf",
'doc',
'pdf',
'output/document_output.pdf: PDF document, version 1.4\n')
def testgetFileMetadataItemListWithoutData(self):
"""Test server using method getFileMetadataItemList. Without data
converted"""
data = open(join('data','testMetadata.odt'),'r').read()
metadata_dict = self.proxy.getFileMetadataItemList(encodestring(data),
'odt')
self.assertEquals(metadata_dict.get("Data"), '')
self.assertEquals(metadata_dict.get("Title"), "cloudooo Test")
self.assertEquals(metadata_dict.get("Subject"), "Subject Test")
self.assertEquals(metadata_dict.get("Description"), "cloudooo Comments")
self.assertEquals(metadata_dict.get("Type"), "Text")
self.assertEquals(metadata_dict.get("MIMEType"),\
"application/vnd.oasis.opendocument.text")
def testgetFileMetadataItemListWithData(self):
"""Test server using method getFileMetadataItemList. With data converted"""
document_output_url = "output/testGetMetadata.odt"
data = open(join('data','testMetadata.odt'),'r').read()
metadata_dict = self.proxy.getFileMetadataItemList(encodestring(data),
"odt",
True)
self.assertNotEquals(metadata_dict.get("Data"), None)
open(document_output_url, 'w').write(decodestring(metadata_dict["Data"]))
stdout, stderr = Popen("file %s" % document_output_url, shell=True,
stdout=PIPE).communicate()
self.assertEquals(stdout,'output/testGetMetadata.odt: OpenDocument Text\n')
self.assertEquals(metadata_dict.get("Title"), "cloudooo Test")
self.assertEquals(metadata_dict.get("Subject"), "Subject Test")
self.assertEquals(metadata_dict.get("Description"), "cloudooo Comments")
self.assertEquals(metadata_dict.get("Type"), "Text")
self.assertEquals(metadata_dict.get("MIMEType"),\
"application/vnd.oasis.opendocument.text")
def testupdateFileMetadata(self):
"""Test server using method updateFileMetadata"""
document_output_url = "output/testSetMetadata.odt"
data = open(join('data','testMetadata.odt'),'r').read()
odf_data = self.proxy.updateFileMetadata(encodestring(data), 'odt',
{"Title":"testSetMetadata"})
open(document_output_url, 'w').write(decodestring(odf_data))
stdout, stderr = Popen("file %s" % document_output_url, shell=True,
stdout=PIPE).communicate()
self.assertEquals(stdout,'output/testSetMetadata.odt: OpenDocument Text\n')
metadata_dict = self.proxy.getFileMetadataItemList(odf_data, 'odt')
self.assertEquals(metadata_dict.get("Title"), "testSetMetadata")
self.assertEquals(metadata_dict.get("CreationDate"), "9/7/2009 17:38:25")
self.assertEquals(metadata_dict.get("Description"), "cloudooo Comments")
def testupdateFileMetadataWithUserMetadata(self):
"""Test server using method updateFileMetadata with unsual metadata"""
document_output_url = "output/testSetMetadata.odt"
data = open(join('data','testMetadata.odt'),'r').read()
odf_data = self.proxy.updateFileMetadata(encodestring(data),
'odt',
{"Reference":"testSetMetadata"})
open(document_output_url, 'w').write(decodestring(odf_data))
stdout, stderr = Popen("file %s" % document_output_url, shell=True,
stdout=PIPE).communicate()
self.assertEquals(stdout,'output/testSetMetadata.odt: OpenDocument Text\n')
metadata_dict = self.proxy.getFileMetadataItemList(odf_data, 'odt')
self.assertEquals(metadata_dict.get("Reference"), "testSetMetadata")
def testupdateFileMetadataUpdateSomeMetadata(self):
"""Test server using method updateFileMetadata when the same metadata is
updated"""
document_output_url = "output/testSetMetadata.odt"
data = open(join('data','testMetadata.odt'),'r').read()
odf_data = self.proxy.updateFileMetadata(encodestring(data), 'odt',
{"Reference":"testSetMetadata", "Something":"ABC"})
new_odf_data = self.proxy.updateFileMetadata(odf_data, 'odt',
{"Reference":"new value", "Something": "ABC"})
open(document_output_url, 'w').write(decodestring(new_odf_data))
stdout, stderr = Popen("file %s" % document_output_url, shell=True,
stdout=PIPE).communicate()
self.assertEquals(stdout,'output/testSetMetadata.odt: OpenDocument Text\n')
metadata_dict = self.proxy.getFileMetadataItemList(new_odf_data, 'odt')
self.assertEquals(metadata_dict.get("Reference"), "new value")
self.assertEquals(metadata_dict.get("Something"), "ABC")
def testupdateFileMetadataAlreadyHasMetadata(self):
"""Test document that already has metadata. Check if the metadata is not
deleted"""
data = open(join('data','testMetadata.odt'),'r').read()
metadata_dict = self.proxy.getFileMetadataItemList(encodestring(data), 'odt')
self.assertEquals(metadata_dict["Description"], "cloudooo Comments")
self.assertEquals(metadata_dict["Keywords"], "Keywords Test")
self.assertEquals(metadata_dict["Title"], "cloudooo Test")
odf_data = self.proxy.updateFileMetadata(encodestring(data), 'odt',
{"Title":"cloudooo Title"})
odf_metadata_dict = self.proxy.getFileMetadataItemList(odf_data, 'odt')
self.assertEquals(odf_metadata_dict["Description"], "cloudooo Comments")
self.assertEquals(odf_metadata_dict["Keywords"], "Keywords Test")
self.assertEquals(odf_metadata_dict["Title"], "cloudooo Title")
def testWithZipFile(self):
"""Test if send a zipfile returns a document correctly"""
output_msg = 'output/output_zipfile.zip: Zip archive data, \
at least v2.0 to extract\n'
self._testConvertFile("data/test.zip",
"output/output_zipfile.zip",
'html',
'txt',
output_msg,
True)
def testSendZipAndReturnTxt(self):
"""Convert compressed html to txt"""
output_url = "output/output.txt"
self._testConvertFile("data/test.zip",
output_url,
'html',
'txt',
'output/output.txt: ASCII text\n')
self.assertEquals(open(output_url).read().endswith('cloudooo Test\n \n'), True)
def testConvertPNGToSVG(self):
"""Test export png to svg"""
self._testConvertFile("data/test.png",
"output/output.svg",
'png',
'svg',
'output/output.svg: SVG Scalable Vector Graphics image\n')
def testConvertPPTXToODP(self):
"""Test export pptx to odp"""
self._testConvertFile("data/test.pptx",
"output/output.pptx",
'pptx',
'odp',
'output/output.pptx: OpenDocument Presentation\n')
def testConvertDocxToODT(self):
"""Test export docx to odt"""
self._testConvertFile("data/test.docx",
"output/output_docx.odt",
'docx',
'odt',
'output/output_docx.odt: OpenDocument Text\n')
def testSendInvalidFile(self):
"""Test to verify if the behavior of server is normal when a invalid file is
sent"""
input_url = "cloudoooTestCase.py"
error_msg = "This document can not be loaded or is empty\n"
data = encodestring(open(input_url).read())
try:
self.proxy.convertFile(data, 'py', 'pdf')
self.fail(error_msg)
except Fault, err:
self.assertEquals(err.faultString.endswith(error_msg), True,
err.faultString)
try:
self.proxy.getFileMetadataItemList(data, 'py')
self.fail(error_msg)
except Fault, err:
self.assertEquals(err.faultString.endswith(error_msg), True,
err.faultString)
try:
self.proxy.updateFileMetadata(data, 'odt',
{"Subject": "subject"})
self.fail(error_msg)
except Fault, err:
self.assertEquals(err.faultString.endswith(error_msg), True,
err.faultString)
def testSendEmptyRequest(self):
"""Test to verify if the behavior of server is normal when a empty string
is sent"""
data = encodestring("")
error_msg = "This document can not be loaded or is empty\n"
try:
self.proxy.convertFile(data, '', '')
self.fail(error_msg)
except Fault, err:
msg = "This format is not supported or is invalid"
self.assertEquals(err.faultString.endswith(msg), True,
"ConvertFile\n" + err.faultString)
try:
self.proxy.getFileMetadataItemList(data, '')
self.fail(error_msg)
except Fault, err:
self.assertEquals(err.faultString.endswith(error_msg), True,
"getFileMetadataItemList\n" + err.faultString)
try:
self.proxy.updateFileMetadata(data, '', {"Subject": "subject"})
self.fail(error_msg)
except Fault, err:
self.assertEquals(err.faultString.endswith(error_msg), True,
"updateFileMetadata\n" + err.faultString)
def testConvertDocumentToInvalidFormat(self):
"""Try convert one document for a invalid format"""
data = open(join('data','test.doc'),'r').read()
error_msg = "This format is not supported or is invalid"
try:
self.proxy.convertFile(encodestring(data), 'doc', 'xyz')
self.fail("")
except Fault, err:
err_str = err.faultString
self.assertEquals(err_str.endswith(error_msg), True,
"%s\n%s" % (error_msg, err_str))
def testConvertDocumentToImpossibleFormat(self):
"""Try convert one document to format not possible"""
data = open(join('data','test.odp'),'r').read()
error_msg = "This Document can not be converted for this format\n"
try:
self.proxy.convertFile(encodestring(data), 'odp', 'doc')
self.fail("")
except Fault, err:
err_str = err.faultString
self.assertEquals(err_str.endswith(error_msg), True,
"%s\n%s" % (error_msg, err_str))
def testRunConvertMethod(self):
"""Test run_convert method"""
data = open(join('data','test.doc'),'r').read()
response_code, response_dict, response_message = \
self.proxy.run_convert('test.doc', encodestring(data))
self.assertEquals(response_code, 200)
self.assertEquals(type(response_dict), DictType)
self.assertNotEquals(response_dict['data'], '')
self.assertEquals(sorted(response_dict.keys()),
['data', 'meta'])
self.assertEquals(response_message, '')
self.assertEquals(response_dict['meta']['MIMEType'], 'application/msword')
def testRunConvertFailResponse(self):
"""Test run_convert method with invalid file"""
data = open(join('data','test.doc'),'r').read()[:30]
response_code, response_dict, response_message = \
self.proxy.run_convert('test.doc', encodestring(data))
self.assertEquals(response_code, 402)
self.assertEquals(type(response_dict), DictType)
self.assertEquals(response_dict, {})
self.assertEquals(response_message.startswith('Traceback'), True)
def testRunGenerateMethod(self):
"""Test run_generate method"""
data = open(join('data','test.odt'),'r').read()
generate_result = self.proxy.run_generate('test.odt',
encodestring(data),
None, 'odt', 'pdf')
response_code, response_dict, response_message = generate_result
self.assertEquals(response_code, 200)
self.assertEquals(type(response_dict), DictType)
self.assertNotEquals(response_dict['data'], '')
self.assertEquals(response_dict['mime'],
'application/vnd.oasis.opendocument.text')
def testRunGenerateMethodFailResponse(self):
"""Test run_generate method with invalid document"""
data = open(join('data','test.odt'), 'r').read()[:100]
generate_result = self.proxy.run_generate('test.odt',
encodestring(data),
None, 'odt', 'pdf')
response_code, response_dict, response_message = generate_result
self.assertEquals(response_code, 402)
self.assertEquals(type(response_dict), DictType)
self.assertEquals(response_dict, {})
self.assertEquals(response_message.startswith('Traceback'), True)
def testRunSetMetadata(self):
"""Test run_setmetadata method"""
document_output_url = "output/testSetMetadata.odt"
data = open(join('data','testMetadata.odt'),'r').read()
setmetadata_result = self.proxy.run_setmetadata('testMetadata.odt',
encodestring(data),
{"Title":"testSetMetadata", "Description": "Music"})
response_code, response_dict, response_message = setmetadata_result
self.assertEquals(response_code, 200)
self.assertNotEquals(response_dict['data'], '')
getmetadata_result = self.proxy.run_getmetadata('testMetadata.odt',
response_dict['data'])
response_code, response_dict, response_message = getmetadata_result
self.assertEquals(response_code, 200)
self.assertEquals(response_dict['meta']['MIMEType'],
'application/vnd.oasis.opendocument.text')
self.assertEquals(response_dict['meta']['Description'], "Music")
def testRunSetMetadataFailResponse(self):
"""Test run_setmetadata method with invalid document"""
document_output_url = "output/testSetMetadata.odt"
data = open(join('data','testMetadata.odt'),'r').read()[:100]
setmetadata_result = self.proxy.run_setmetadata('testMetadata.odt',
encodestring(data),
{"Title":"testSetMetadata", "Description": "Music"})
response_code, response_dict, response_message = setmetadata_result
self.assertEquals(response_code, 402)
self.assertEquals(response_dict, {})
self.assertEquals(response_message.startswith('Traceback'), True)
def testGetAllowedTargetItemList(self):
"""Test if filter name returns correctly with ERP5 API"""
mimetype = 'application/vnd.oasis.opendocument.text'
response_code, response_dict, response_message = \
self.proxy.getAllowedTargetItemList(mimetype)
self.assertEquals(response_code, 200)
self.assertEquals(len(response_dict['response_data']), 35)
self.assertTrue(['htm', 'HTML Document'] in response_dict['response_data'])
def test_suite():
return make_suite(TestServer)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(TestServer)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
import jsonpickle
from subprocess import Popen, PIPE
from os.path import exists
from cloudoooTestCase import cloudoooTestCase, make_suite
from cloudooo.mimemapper import mimemapper
from cloudooo.application.openoffice import openoffice
from cloudooo.document import FileSystemDocument
class TestUnoConverter(cloudoooTestCase):
"""Test case to test all features of the unoconverter script"""
file_msg_list = ["Microsoft Office Document",
"CDF V2 Document, Little Endian, Os: Windows, Version 1.0,"]
def afterSetUp(self):
""" """
openoffice.acquire()
self.hostname, self.port = openoffice.getAddress()
data = open("data/test.odt", 'r').read()
self.document = FileSystemDocument(self.tmp_url, data, 'odt')
def tearDown(self):
"""Called to unlock the openoffice"""
openoffice.release()
def testUnoConverterOdtToDoc(self):
"""Test script unoconverter"""
mimemapper_pickled = jsonpickle.encode(mimemapper)
command = [self.python_path,
self.unoconverter_bin,
"--convert",
"--uno_path=%s" % self.uno_path,
"--office_bin_path=%s" % self.office_bin_path,
"--hostname=%s" % self.hostname,
"--port=%s" % self.port,
"--document_url=%s" % self.document.getUrl(),
"--destination_format=%s" % "doc",
"--source_format=%s" % "odt",
"--mimemapper='%s'" % mimemapper_pickled]
stdout, stderr = Popen(' '.join(command), shell=True,
stdout=PIPE, stderr=PIPE).communicate()
self.assertEquals(stderr, '')
output_url = stdout.replace('\n', '')
self.assertEquals(exists(output_url), True)
stdout, stderr = Popen("file %s" % output_url, shell=True,
stdout=PIPE, stderr=PIPE).communicate()
self.assertEquals(self.file_msg_list[1] in stdout \
or \
self.file_msg_list[0] in stdout,
True,
"%s don't have %s" % (self.file_msg_list, stdout))
self.document.trash()
self.assertEquals(exists(output_url), False)
def testUnoConverterWithoutMimemapper(self):
"""Test script unoconverter without mimemapper serialized"""
command = [self.python_path,
self.unoconverter_bin,
"--convert",
"--uno_path=%s" % self.uno_path,
"--office_bin_path=%s" % self.office_bin_path,
"--hostname=%s" % self.hostname,
"--port=%s" % self.port,
"--document_url=%s" % self.document.getUrl(),
"--destination_format=%s" % "doc",
"--source_format=%s" % "odt",
"--unomimemapper_bin=%s" % self.unomimemapper_bin,
"--python_path=%s" % self.python_path]
stdout, stderr = Popen(' '.join(command), shell=True,
stdout=PIPE, stderr=PIPE).communicate()
if not stdout:
self.fail(stderr)
output_url = stdout.replace('\n', '')
self.assertEquals(exists(output_url), True)
stdout, stderr = Popen("file %s" % output_url, shell=True,
stdout=PIPE, stderr=PIPE).communicate()
self.assertEquals(self.file_msg_list[1] in stdout \
or \
self.file_msg_list[0] in stdout,
True,
"%s don't have %s" % (self.file_msg_list, stdout))
self.document.trash()
self.assertEquals(exists(output_url), False)
def test_suite():
return make_suite(TestUnoConverter)
if __name__ == "__main__":
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestUnoConverter)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudooo.application.openoffice import openoffice
from subprocess import Popen, PIPE
from os import environ
from cloudoooTestCase import cloudoooTestCase, make_suite
class TestUnoMimeMapper(cloudoooTestCase):
"""Test Case to test all features of script unomimemapper.py"""
def afterSetUp(self):
""" """
environ['uno_path'] = ''
environ['office_bin_path'] = ''
openoffice.acquire()
def tearDown(self):
""" """
environ['uno_path'] = self.uno_path
environ['office_bin_path'] = self.office_bin_path
openoffice.release()
def testCreateLocalAttributes(self):
"""Test if filters returns correctly the filters and types in dict"""
hostname, host = openoffice.getAddress()
command = [self.python_path,
self.unomimemapper_bin,
"--uno_path=%s" % self.uno_path,
"--office_bin_path=%s" % self.uno_path,
"--hostname=%s" % self.hostname,
"--port=%s" % self.openoffice_port]
stdout, stderr = Popen(' '.join(command), shell=True,
stdout=PIPE, stderr=PIPE).communicate()
exec(stdout)
self.assertEquals('filter_dict' in locals(), True)
self.assertEquals('type_dict' in locals(), True)
self.assertNotEquals(filter_dict.get('writer8'), None)
self.assertEquals(type_dict.get('writer8').get('Name'), 'writer8')
self.assertNotEquals(filter_dict.get('writer8'), None)
self.assertEquals(type_dict.get('writer8').get('PreferredFilter'), 'writer8')
self.assertEquals(stderr, '')
def testCallUnoMimemapperWithoutSomeParameters(self):
""" Test call unomimemapper without uno_path and office_bin_path"""
hostname, host = openoffice.getAddress()
command = [self.python_path,
self.unomimemapper_bin,
"--hostname=%s" % self.hostname,
"--port=%s" % self.openoffice_port]
stdout, stderr = Popen(' '.join(command), shell=True,
stdout=PIPE, stderr=PIPE).communicate()
self.assertEquals(stderr.endswith('No module named uno\n'), True)
self.assertEquals(stdout, '')
def testWithoutOpenOffice(self):
"""Test when the openoffice is stopped"""
error_msg = "couldn\'t connect to socket (Success)\n"
hostname, host = openoffice.getAddress()
openoffice.stop()
command = [self.python_path,
self.unomimemapper_bin,
"--uno_path=%s" % self.uno_path,
"--office_bin_path=%s" % self.uno_path,
"--hostname=%s" % self.hostname,
"--port=%s" % self.openoffice_port]
stdout, stderr = Popen(' '.join(command), shell=True,
stdout=PIPE, stderr=PIPE).communicate()
self.assertEquals(stdout, '')
self.assertEquals(stderr.endswith(error_msg), True)
openoffice.start()
def test_suite():
return make_suite(TestUnoMimeMapper)
if __name__ == "__main__":
from cloudoooTestCase import startFakeEnvironment, stopFakeEnvironment
startFakeEnvironment()
suite = unittest.TestLoader().loadTestsFromTestCase(TestUnoMimeMapper)
unittest.TextTestRunner(verbosity=2).run(suite)
stopFakeEnvironment()
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
import logging
from cloudooo.utils import logger, configureLogger, convertStringToBool
from cloudoooTestCase import make_suite
class TestUtils(unittest.TestCase):
"""Test Utils"""
def testLog(self):
"""Instanciate Log and test __call__ called function log"""
configureLogger(logging.DEBUG)
logger.info("Test Log")
logger.debug("Test Log")
configureLogger(logging.INFO)
logger.info("Test Log")
logger.debug("Test Log")
def testConversion(self):
"""Test convertion to bool"""
self.assertEquals(convertStringToBool('true'), True)
self.assertEquals(convertStringToBool('false'), False)
self.assertEquals(convertStringToBool('truE'), True)
self.assertEquals(convertStringToBool('faLse'), False)
self.assertEquals(convertStringToBool(''), None)
def test_suite():
return make_suite(TestUtils)
if "__main__" == __name__:
suite = unittest.TestLoader().loadTestsFromTestCase(TestUtils)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
import unittest
from cloudoooTestCase import cloudoooTestCase, make_suite
from cloudooo.application.xvfb import Xvfb
from cloudooo.utils import waitStopDaemon
class TestXvfb(cloudoooTestCase):
def afterSetUp(self):
"""Instanciate a xvfb object"""
self.xvfb = Xvfb()
self.xvfb.loadSettings(self.hostname,
int(self.virtual_display_port_int),
self.path_dir_run_cloudooo,
self.virtual_display_id,
virtual_screen='1')
def testPid(self):
"""Test pid function to validate if the return is correctly"""
self.assertEquals(self.xvfb.pid(), None)
try:
self.xvfb.start()
self.assertNotEquals(self.xvfb.pid(), None)
finally:
self.xvfb.stop()
waitStopDaemon(self.xvfb)
self.assertEquals(self.xvfb.pid(), None)
def testStatus(self):
"""Test if xvfb is started and stopped correctly"""
self.assertEquals(self.xvfb.status(), False)
try:
self.xvfb.start()
self.assertEquals(self.xvfb.status(), True)
finally:
self.xvfb.stop()
waitStopDaemon(self.xvfb)
self.assertEquals(self.xvfb.status(), False)
def test_suite():
return make_suite(TestXvfb)
if "__main__" == __name__:
suite = unittest.TestLoader().loadTestsFromTestCase(TestXvfb)
unittest.TextTestRunner(verbosity=2).run(suite)
##############################################################################
#
# Copyright (c) 2002-2010 Nexedi SA and Contributors. All Rights Reserved.
# Gabriel M. Monnerat <gmonnerat@iff.edu.br>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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 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.
#
##############################################################################
from socket import socket, error
from errno import EADDRINUSE
from time import sleep
from os.path import join, isdir, exists
from os import listdir, remove
from shutil import rmtree
import logging
logger = logging.getLogger('Oood')
def removeDirectory(path):
"""Remove directory"""
try:
rmtree(path)
except OSError, msg:
logger.error(msg)
def cleanDirectory(path, ignore_list=[]):
"""Cleans directory.
Keyword arguments:
path -- folder path
"""
if not exists(path):
return
folder_list = [join(path, name) for name in listdir(path)]
ignore_list = [join(path, name) for name in ignore_list]
for path in folder_list:
if isdir(path) and path not in ignore_list:
removeDirectory(path)
else:
logger.debug("%s is not a folder or was ignored" % path)
def socketStatus(hostname, port):
"""Verify if the address is busy."""
try:
socket().bind((hostname, port),)
# False if the is free
return False
except error, (num, err):
if num == EADDRINUSE:
# True if the isn't free
return True
def waitStartDaemon(daemon, attempts):
"""Wait a certain time to start the daemon."""
for num in range(attempts):
sleep(1)
if daemon.status():
return
def waitStopDaemon(daemon, attempts=5):
"""Wait a certain time to stop the daemon."""
for num in range(attempts):
sleep(1)
if not daemon.status():
break
def usage(stream, msg=None):
"""Print the message"""
if msg:
print >>stream, msg
def configureLogger(level=None, debug_mode=False):
"""Configure logger.
Keyword arguments:
level -- Level to prints the log messages
"""
if level is None:
level = logging.INFO
if debug_mode:
level = logging.DEBUG
handler_list = logger.handlers
if handler_list:
for handler in iter(handler_list):
logger.removeHandler(handler)
# The propagate value indicates whether or not parents of this loggers will
# be traversed when looking for handlers. It doesn't really make sense in the
# root logger - it's just there because a root logger is almost like any
# other logger.
logger.propagate = 0
logger.setLevel(level)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(level)
# create formatter
formatter = logging.Formatter("%(asctime).19s - %(name)s - %(levelname)s - %(message)s")
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
def remove_file(filepath):
try:
remove(filepath)
except OSError, msg:
print msg.strerror
def convertStringToBool(string):
"""This function is used to convert string 'true' and 'false' only.
Keyword arguments:
string -- string to convert to boolean
"""
if string.upper() == "TRUE":
return True
elif string.upper() == "FALSE":
return False
else:
return None
# Copyright (c) 2006-2007 Open Source Applications Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
class WSGIXMLRPCApplication(object):
"""Application to handle requests to the XMLRPC service"""
def __init__(self, instance=None, methods=[]):
"""Create windmill xmlrpc dispatcher"""
self.dispatcher = SimpleXMLRPCDispatcher(allow_none=True, encoding=None)
if instance is not None:
self.dispatcher.register_instance(instance)
for method in methods:
self.dispatcher.register_function(method)
self.dispatcher.register_introspection_functions()
def handler(self, environ, start_response):
"""XMLRPC service for windmill browser core to communicate with"""
if environ['REQUEST_METHOD'] == 'POST':
return self.handle_POST(environ, start_response)
else:
start_response("400 Bad request", [('Content-Type','text/plain')])
return ['']
def handle_POST(self, environ, start_response):
"""Handles the HTTP POST request.
Attempts to interpret all HTTP POST requests as XML-RPC calls,
which are forwarded to the server's _dispatch method for handling.
Most code taken from SimpleXMLRPCServer with modifications for wsgi and my custom dispatcher.
"""
try:
# Get arguments by reading body of request.
# We read this in chunks to avoid straining
length = int(environ['CONTENT_LENGTH'])
data = environ['wsgi.input'].read(length)
max_chunk_size = 10*1024*1024
size_remaining = length
# In previous versions of SimpleXMLRPCServer, _dispatch
# could be overridden in this class, instead of in
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
# check to see if a subclass implements _dispatch and
# using that method if present.
response = self.dispatcher._marshaled_dispatch(
data, getattr(self.dispatcher, '_dispatch', None)
)
response += '\n'
except: # This should only happen if the module is buggy
# internal error, report as HTTP server error
start_response("500 Server error", [('Content-Type', 'text/plain')])
return []
else:
# got a valid XML RPC response
start_response("200 OK", [('Content-Type','text/xml'), ('Content-Length', str(len(response)),)])
return [response]
def __call__(self, environ, start_response):
return self.handler(environ, start_response)
import sys
from setuptools import setup, find_packages
from os.path import realpath, exists, join, dirname
from os import symlink, unlink
from shutil import copyfile
version = '0.6'
setup(name='cloudooo',
version=version,
description="XML-RPC openoffice document convertion server",
long_description="""\
""",
classifiers=["Topic :: System :: Networking",
"Topic :: System :: Operating System Kernels :: Linux",
"Topic :: Internet :: WWW/HTTP :: WSGI",
"Programming Language :: Python :: 2.6",
"Natural Language :: English",
"Framework :: Paste"],
keywords='xmlrpc openoffice wsgi paste python',
author='Gabriel M. Monnerat',
author_email='gabriel@tiolive.com',
url='https://svn.erp5.org/repos/experimental/oood',
license='GPL 2',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
install_requires=[
# -*- Extra requirements: -*-
'zope.interface',
'PasteDeploy',
'PasteScript',
'WSGIUtils',
'jsonpickle',
],
entry_points="""
# -*- Entry points: -*-
[paste.app_factory]
main = cloudooo.cloudooo:application
""",
)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment