diff --git a/Tests/Integration/GNUmakefile b/Tests/Integration/GNUmakefile deleted file mode 100644 index c93ca1c10..000000000 --- a/Tests/Integration/GNUmakefile +++ /dev/null @@ -1,8 +0,0 @@ -include $(GNUSTEP_MAKEFILES)/common.make - -TOOL_NAME = teststrings -teststrings_OBJC_FILES += \ - teststrings.m - --include GNUmakefile.preamble -include $(GNUSTEP_MAKEFILES)/tool.make diff --git a/Tests/Integration/GNUmakefile.preamble b/Tests/Integration/GNUmakefile.preamble deleted file mode 100644 index 68878f481..000000000 --- a/Tests/Integration/GNUmakefile.preamble +++ /dev/null @@ -1,19 +0,0 @@ -# compile settings - -ADDITIONAL_CPPFLAGS += \ - -DSOGO_MAJOR_VERSION=$(MAJOR_VERSION) \ - -DSOGO_MINOR_VERSION=$(MINOR_VERSION) \ - -DSOGO_SUBMINOR_VERSION=$(SUBMINOR_VERSION) \ - -DSOGO_LIBDIR="@\"$(SOGO_LIBDIR)\"" - -ADDITIONAL_INCLUDE_DIRS += \ - -D_GNU_SOURCE -I../../SOPE/ -I../../SoObjects/ - -ADDITIONAL_LIB_DIRS += \ - -L../../SoObjects/SOGo/SOGo.framework/Versions/Current/sogo -lSOGo \ - -L../../SOPE/GDLContentStore/$(GNUSTEP_OBJ_DIR)/ -lGDLContentStore \ - -L../../SOPE/NGCards/$(GNUSTEP_OBJ_DIR)/ -lNGCards \ - -L/usr/local/lib/sogo -L/usr/lib/sogo -L/usr/lib64/sogo -lEOControl -lNGStreams -lNGMime -lNGExtensions - - -ADDITIONAL_LDFLAGS += -Wl,--no-as-needed -Wl,--rpath,$(GNUSTEP_SYSTEM_LIBRARIES)/sogo diff --git a/Tests/Integration/README b/Tests/Integration/README deleted file mode 100644 index 7a7bd80c0..000000000 --- a/Tests/Integration/README +++ /dev/null @@ -1,58 +0,0 @@ -setup ------ - -(you need "python-simplejson", "python-xml", "python-vobject", "python-dateutil" and "python-m2crypto" - in order to run the scripts on Debian) - -1) copy config.py.in to config.py (make sure to never EVER add it to git) -2) edit config.py to suit your environment -3) make sure that you use a fresh database, with no prior information in it -4) make sure that SOGoCalendarDefaultRoles and SOGoContactsDefaultRoles are empty or undefined -5) run the test scripts - -runnable scripts ----------------- - -all.py - run all scripts below at once -test-webdavsync.py - explicit -test-davacl.py - dav acl tests for calendar and addressbook modules - -other scripts -------------- - -propfind.py - a sample implementation of a PROPFIND request using webdavlib - -* developers ------------- - -- Test methods are always prefixed with "test". Sometimes, it's easier to -track down a problem by enabling only one test at a time. One possible method -is to replace "def test" with "def xtest" and replace it back when the -problems are solved. - -- Test failures start with "FAIL:". Those are the ones that indicate possible - bugs in the application, if the test is itself known to work. - For example like this: - -====================================================================== -FAIL: 'modify' PUBLIC, 'view all' PRIVATE, 'view d&t' confidential ----------------------------------------------------------------------- -Traceback (most recent call last): - File "./davacl.py", line 75, in testModifyPublicViewAllPrivateViewDConfidential - self._testRights({ "pu": "m", "pr": "v", "co": "d" }) - File "./davacl.py", line 119, in _testRights - self._testCreate(rights) - File "./davacl.py", line 165, in _testCreate - exp_code) - File "./davacl.py", line 107, in _putEvent - % (exp_status, put.response["status"])) -AssertionError: event creation/modification: expected status code '403' (received '201') - -- Test errors start with "ERRORS" and most likely indicate a bug in the test - code itself. - -- Always set a doc string on the test methods, especially for complex test - cases. - -- When writing tests, be aware that contrarily to unit tests, functional tests - often imply a logical order between the different steps. diff --git a/Tests/Integration/all.py b/Tests/Integration/all.py deleted file mode 100755 index cb818980b..000000000 --- a/Tests/Integration/all.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/python - -import os, sys, unittest, getopt, traceback, time -import preferences -import sogotests -import unittest - -if __name__ == "__main__": - unittest._TextTestResult.oldStartTest = unittest._TextTestResult.startTest - unittest._TextTestResult.startTest = sogotests.UnitTestTextTestResultNewStartTest - unittest._TextTestResult.stopTest = sogotests.UnitTestTextTestResultNewStopTest - - loader = unittest.TestLoader() - modules = [] - - languages = preferences.SOGoSupportedLanguages - - # We can disable testing all languages - testLanguages = False - opts, args = getopt.getopt (sys.argv[1:], [], ["enable-languages"]) - for o, a in opts: - if o == "--enable-languages": - testLanguages = True - - - for mod in os.listdir("."): - if mod.startswith("test-") and mod.endswith(".py"): - modules.append(mod[:-3]) - __import__(mod[:-3]) - - if len(modules) > 0: - suite = loader.loadTestsFromNames(modules) - print "%d tests in modules: '%s'" % (suite.countTestCases(), - "', '".join(modules)) - runner = unittest.TextTestRunner(verbosity=2) - - if testLanguages: - prefs = preferences.preferences() - # Get the current language - userLanguageString = prefs.get ("SOGoLanguage") - if userLanguageString: - userLanguage = languages.index (userLanguageString) - else: - userLanguage = languages.index ("English") - - for i in range (0, len (languages)): - try: - prefs.set ("SOGoLanguage", i) - except Exception, inst: - print '-' * 60 - traceback.print_exc () - print '-' * 60 - - print "Running test in %s (%d/%d)" % \ - (languages[i], i + 1, len (languages)) - runner.verbosity = 2 - runner.run(suite) - # Revert to the original language - prefs.set ("SOGoLanguage", userLanguage) - else: - runner.run(suite) - - else: - print "No test available." diff --git a/Tests/Integration/carddav.py b/Tests/Integration/carddav.py deleted file mode 100644 index c6e32fbde..000000000 --- a/Tests/Integration/carddav.py +++ /dev/null @@ -1,144 +0,0 @@ -from config import hostname, port, username, password -import webdavlib -import simplejson -import sogoLogin - - -DEBUG=True -DEBUG=False - - -class HTTPPreferencesPOST (webdavlib.HTTPPOST): - cookie = None - - def prepare_headers (self): - headers = webdavlib.HTTPPOST.prepare_headers(self) - if self.cookie: - headers["Cookie"] = self.cookie - return headers - -class HTTPPreferencesGET (webdavlib.HTTPGET): - cookie = None - - def prepare_headers (self): - headers = webdavlib.HTTPGET.prepare_headers(self) - if self.cookie: - headers["Cookie"] = self.cookie - return headers - -class Carddav: - login = username - passw = password - - def __init__(self, otherLogin = None, otherPassword = None): - if otherLogin and otherPassword: - self.login = otherLogin - self.passw = otherPassword - - self.client = webdavlib.WebDAVClient(hostname, port) - authCookie = sogoLogin.getAuthCookie(hostname, port, self.login, self.passw) - self.cookie = authCookie - self.cards = None - self.calendars = None - self.fields = None - self.events = None - - #- If this is not set, we CAN'T save preferences - self.preferences = None - - def _get(self, url): - get = HTTPPreferencesGET(url) - get.cookie = self.cookie - self.client.execute(get) - if DEBUG: print "(url):", url - if DEBUG: print "(status):", get.response["status"] - if DEBUG: print "(body):", get.response['body'] - content = simplejson.loads(get.response['body']) - return content - - def _post(self, url, data, errormsg="failure POST"): - if DEBUG: print "URL:", url - post = HTTPPreferencesPOST(url, simplejson.dumps(data)) - post.content_type = "application/json" - post.cookie = self.cookie - self.client.execute(post) - # Raise an exception if the pref wasn't properly set - if post.response["status"] != 200: - raise Exception ("%s, (code = %d)" % (errormsg, post.response["status"])) - - def load_cards(self): - if not self.cards: - url = "/SOGo/so/%s/Contacts/personal/view" % (self.login) - content = self._get(url) - #print "\nCONTENT:", content - if 'headers' in content: - self.cards = [] - fields = content['headers'][0] - for h in content['headers'][1:]: - card = {} - for i, f in enumerate(fields): - card[f] = h[i] - self.cards.append(card) - else: - self.cards = [] - return self.cards - - def get_cards(self, pattern): - self.load_cards() - return [a for a in self.cards if pattern in a.values()] - - def get_card(self, idstr): - url = "/SOGo/so/%s/Contacts/personal/%s/view" % (self.login, idstr) - content = self._get(url) - return content - - def save_card(self, card): - url = "/SOGo/so/%s/Contacts/personal/%s/saveAsContact" % (self.login, card['id']) - self._post(url, card, "failure saving card") - - def load_calendars(self): - if not self.calendars: - url = "/SOGo/so/%s/Calendar/calendarslist" % (self.login) - content = self._get(url) - self.calendars = content['calendars'] - return self.calendars - - def get_calendars(self, pattern): - self.load_calendars() - return [a for a in self.calendars if pattern in a.values()] - - def get_calendar(self, idstr): - self.load_calendars() - callist = [a for a in self.calendars if a['id'] == idstr] - if len(callist): - return callist[0] - return None - - def save_calendar(self, calendar): - url = "/SOGo/so/%s/Contacts/personal/%s/saveAsContact" % (self.login, calendar['id']) - self._post(url, card, "failure saving calendar") - - def load_events(self): - if not self.events: - url = "/SOGo/so/%s/Calendar/eventslist" % (self.login) - content = self._get(url) - self.fields = content['fields'] - self.events = [] - months = content['events'] - for month in months.keys(): - days = months[month]['days'] - for day in days.keys(): - tmp_events = days[day]['events'] - self.events.extend(dict(zip(self.fields, event)) for event in tmp_events) - return self.events - - def newguid(self, folderpath): - url = "/SOGo/so/%s/%s/newguid" % (self.login, folderpath) - content = self._get(url) - return content['id'] - - def save_event(self, event, folder, gid): - #url = "/SOGo/so/%s/%s/%s/save" % (self.login, event['c_folder'], event['c_name']) - url = "/SOGo/so/%s/%s/%s/saveAsAppointment" % (self.login, folder, gid) - self._post(url, event, "failure saving event") - diff --git a/Tests/Integration/config.py.in b/Tests/Integration/config.py.in deleted file mode 100644 index 7e5c1ca78..000000000 --- a/Tests/Integration/config.py.in +++ /dev/null @@ -1,38 +0,0 @@ -# setup: 4 user are needed: username, superuser, attendee1, attendee1_delegate -# superuser must be a sogo superuser... - -hostname = "localhost" -port = "80" -username = "myuser" -password = "mypass" - -superuser = "super" -superuser_password="pass" - -subscriber_username = "otheruser" -subscriber_password = "otherpass" - -attendee1 = "user@domain.com" -attendee1_username = "user" -attendee1_password = "pass" - -attendee1_delegate = "user2@domain.com" -attendee1_delegate_username = "sogo2" -attendee1_delegate_password = "sogo" - -resource_no_overbook = "res" -resource_can_overbook = "res-nolimit" - -white_listed_attendee = '{"sogo1":"John Doe "}' - -mailserver = "imaphost" - -testput_nbrdays = 30 - -sieve_server = "localhost" -sieve_port = 2000 - -sogo_user = "sogo" -sogo_tool_path = "/usr/local/sbin/sogo-tool" - -webCalendarURL = "http://inverse.ca/sogo-integration-tests/CanadaHolidays.ics" diff --git a/Tests/Integration/ev_generator.py b/Tests/Integration/ev_generator.py deleted file mode 100644 index c12dadb24..000000000 --- a/Tests/Integration/ev_generator.py +++ /dev/null @@ -1,92 +0,0 @@ -import time - -def hours(nbr): - return nbr * 3600 - -def days(nbr): - return nbr * hours(24) - -class ev_generator: - ev_templ = """ -BEGIN:VCALENDAR\r -VERSION:2.0\r -PRODID:-//Inverse//Event Generator//EN\r -CALSCALE:GREGORIAN\r -BEGIN:VTIMEZONE\r -TZID:America/Montreal\r -BEGIN:DAYLIGHT\r -TZOFFSETFROM:-0500\r -TZOFFSETTO:-0400\r -DTSTART:20070311T020000\r -RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU\r -TZNAME:EDT\r -END:DAYLIGHT\r -BEGIN:STANDARD\r -TZOFFSETFROM:-0400\r -TZOFFSETTO:-0500\r -DTSTART:20071104T020000\r -RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU\r -TZNAME:EST\r -END:STANDARD\r -END:VTIMEZONE\r -BEGIN:VEVENT\r -SEQUENCE:4\r -TRANSP:OPAQUE\r -UID:%(uid)s\r -SUMMARY:%(summary)s\r -DTSTART;TZID=America/Montreal:%(start)s\r -DTEND;TZID=America/Montreal:%(end)s\r -CREATED:20080711T231608Z\r -DTSTAMP:20080711T231640Z\r -END:VEVENT\r -END:VCALENDAR\r -""" - def __init__(self, maxDays): - self.reset(maxDays) - - def reset(self, maxDays): - self.maxDays = maxDays - self.currentDay = 0 - self.currentStart = 0 - today = time.mktime(time.localtime()) - self.firstDay = today - days(maxDays + 30) - - def _calendarDate(self, eventTime): - timeStruct = time.localtime(eventTime) - return time.strftime("%Y%m%dT%H0000", timeStruct) - - def _iterValues(self): - event = None - - if (self.currentDay < self.maxDays): - eventStart = (self.firstDay - + days(self.currentDay) - + hours(self.currentStart + 8)) - eventEnd = eventStart + hours(1) - - thatDay = time.localtime(int(eventStart)) - uid = "Event%d%d" % (eventStart, eventEnd) - summary = "%s - event %d" % (time.strftime("%Y-%m-%d", thatDay), - self.currentStart) - start = self._calendarDate(eventStart) - end = self._calendarDate(eventEnd) - event = {'uid': uid, - 'summary': summary, - 'start': start, - 'end': end} - - self.currentStart = self.currentStart + 1 - if (self.currentStart > 7): - self.currentStart = 0 - self.currentDay = self.currentDay + 1 - - return event - - def iter(self): - hasMore = False - entryValues = self._iterValues() - if (entryValues is not None): - self.event = (self.ev_templ % entryValues).strip() - hasMore = True - - return hasMore diff --git a/Tests/Integration/managesieve.py b/Tests/Integration/managesieve.py deleted file mode 100644 index b248b13c5..000000000 --- a/Tests/Integration/managesieve.py +++ /dev/null @@ -1,631 +0,0 @@ -"""Sieve management client. - -A Protocol for Remotely Managing Sieve Scripts -Based on -""" - -__version__ = "0.4.2" -__author__ = """Hartmut Goebel -Ulrich Eck April 2001 -""" - -import binascii, re, socket, time, random, sys -try: - import ssl - ssl_wrap_socket = ssl.wrap_socket -except ImportError: - ssl_wrap_socket = socket.ssl - -__all__ = [ 'MANAGESIEVE', 'SIEVE_PORT', 'OK', 'NO', 'BYE', 'Debug'] - -Debug = 0 -CRLF = '\r\n' -SIEVE_PORT = 2000 - -OK = 'OK' -NO = 'NO' -BYE = 'BYE' - -AUTH_PLAIN = "PLAIN" -AUTH_LOGIN = "LOGIN" -# authentication mechanisms currently supported -# in order of preference -AUTHMECHS = [AUTH_PLAIN, AUTH_LOGIN] - -# todo: return results or raise exceptions? -# todo: on result 'BYE' quit immediatly -# todo: raise exception on 'BYE'? - -# Commands -commands = { - # name valid states - 'STARTTLS': ('NONAUTH',), - 'AUTHENTICATE': ('NONAUTH',), - 'LOGOUT': ('NONAUTH', 'AUTH', 'LOGOUT'), - 'CAPABILITY': ('NONAUTH', 'AUTH'), - 'GETSCRIPT': ('AUTH', ), - 'PUTSCRIPT': ('AUTH', ), - 'SETACTIVE': ('AUTH', ), - 'DELETESCRIPT': ('AUTH', ), - 'LISTSCRIPTS': ('AUTH', ), - 'HAVESPACE': ('AUTH', ), - # bogus command to receive a NO after STARTTLS (see starttls() ) - 'BOGUS': ('NONAUTH', 'AUTH', 'LOGOUT'), - } - -### needed -Oknobye = re.compile(r'(?P(OK|NO|BYE))' - r'( \((?P.*)\))?' - r'( (?P.*))?') -# draft-martin-managesieve-04.txt defines the size tag of literals to -# contain a '+' (plus sign) behind the digits, but timsieved does not -# send one. Thus we are less strikt here: -Literal = re.compile(r'.*{(?P\d+)\+?}$') -re_dquote = re.compile(r'"(([^"\\]|\\.)*)"') -re_esc_quote = re.compile(r'\\([\\"])') - - -class SSLFakeSocket: - """A fake socket object that really wraps a SSLObject. - - It only supports what is needed in managesieve. - """ - def __init__(self, realsock, sslobj): - self.realsock = realsock - self.sslobj = sslobj - - def send(self, str): - self.sslobj.write(str) - return len(str) - - sendall = send - - def close(self): - self.realsock.close() - -class SSLFakeFile: - """A fake file like object that really wraps a SSLObject. - - It only supports what is needed in managesieve. - """ - def __init__(self, sslobj): - self.sslobj = sslobj - - def readline(self): - str = "" - chr = None - while chr != "\n": - chr = self.sslobj.read(1) - str += chr - return str - - def read(self, size=0): - if size == 0: - return '' - else: - return self.sslobj.read(size) - - def close(self): - pass - - -def sieve_name(name): - # todo: correct quoting - return '"%s"' % name - -def sieve_string(string): - return '{%d+}%s%s' % ( len(string), CRLF, string ) - - -class MANAGESIEVE: - """Sieve client class. - - Instantiate with: MANAGESIEVE(host [, port]) - - host - host's name (default: localhost) - port - port number (default: standard Sieve port). - - use_tls - switch to TLS automatically, if server supports - keyfile - keyfile to use for TLS (optional) - certfile - certfile to use for TLS (optional) - - All Sieve commands are supported by methods of the same - name (in lower-case). - - Each command returns a tuple: (type, [data, ...]) where 'type' - is usually 'OK' or 'NO', and 'data' is either the text from the - tagged response, or untagged results from command. - - All arguments to commands are converted to strings, except for - AUTHENTICATE. - """ - - """ - However, the 'password' argument to the LOGIN command is always - quoted. If you want to avoid having an argument string quoted (eg: - the 'flags' argument to STORE) then enclose the string in - parentheses (eg: "(\Deleted)"). - - Errors raise the exception class .error(""). - IMAP4 server errors raise .abort(""), - which is a sub-class of 'error'. Mailbox status changes - from READ-WRITE to READ-ONLY raise the exception class - .readonly(""), which is a sub-class of 'abort'. - - "error" exceptions imply a program error. - "abort" exceptions imply the connection should be reset, and - the command re-tried. - "readonly" exceptions imply the command should be re-tried. - - Note: to use this module, you must read the RFCs pertaining - to the IMAP4 protocol, as the semantics of the arguments to - each IMAP4 command are left to the invoker, not to mention - the results. - """ - - class error(Exception): """Logical errors - debug required""" - class abort(error): """Service errors - close and retry""" - - def __clear_knowledge(self): - """clear/init any knowledge obtained from the server""" - self.capabilities = [] - self.loginmechs = [] - self.implementation = '' - self.supports_tls = 0 - - def __init__(self, host='', port=SIEVE_PORT, - use_tls=False, keyfile=None, certfile=None): - self.host = host - self.port = port - self.debug = Debug - self.state = 'NONAUTH' - - self.response_text = self.response_code = None - self.__clear_knowledge() - - # Open socket to server. - self._open(host, port) - - if __debug__: - self._cmd_log_len = 10 - self._cmd_log_idx = 0 - self._cmd_log = {} # Last `_cmd_log_len' interactions - if self.debug >= 1: - self._mesg('managesieve version %s' % __version__) - - # Get server welcome message, - # request and store CAPABILITY response. - typ, data = self._get_response() - if typ == 'OK': - self._parse_capabilities(data) - if use_tls and self.supports_tls: - typ, data = self.starttls(keyfile=keyfile, certfile=certfile) - if typ == 'OK': - self._parse_capabilities(data) - - - def _parse_capabilities(self, lines): - for line in lines: - if len(line) == 2: - typ, data = line - else: - assert len(line) == 1, 'Bad Capabilities line: %r' % line - typ = line[0] - data = None - if __debug__: - if self.debug >= 3: - self._mesg('%s: %r' % (typ, data)) - if typ == "IMPLEMENTATION": - self.implementation = data - elif typ == "SASL": - self.loginmechs = data.split() - elif typ == "SIEVE": - self.capabilities = data.split() - elif typ == "STARTTLS": - self.supports_tls = 1 - else: - # A client implementation MUST ignore any other - # capabilities given that it does not understand. - pass - return - - - def __getattr__(self, attr): - # Allow UPPERCASE variants of MANAGESIEVE command methods. - if commands.has_key(attr): - return getattr(self, attr.lower()) - raise AttributeError("Unknown MANAGESIEVE command: '%s'" % attr) - - - #### Private methods ### - def _open(self, host, port): - """Setup 'self.sock' and 'self.file'.""" - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) - self.file = self.sock.makefile('r') - - def _close(self): - self.file.close() - self.sock.close() - - def _read(self, size): - """Read 'size' bytes from remote.""" - data = "" - while len(data) < size: - data += self.file.read(size - len(data)) - return data - - def _readline(self): - """Read line from remote.""" - return self.file.readline() - - def _send(self, data): - return self.sock.send(data) - - def _get_line(self): - line = self._readline() - if not line: - raise self.abort('socket error: EOF') - # Protocol mandates all lines terminated by CRLF - line = line[:-2] - if __debug__: - if self.debug >= 4: - self._mesg('< %s' % line) - else: - self._log('< %s' % line) - return line - - def _simple_command(self, *args): - """Execute a command which does only return status. - - Returns (typ) with - typ = response type - - The responce code and text may be found in .response_code - and .response_text, respectivly. - """ - return self._command(*args)[0] # only return typ, ignore data - - - def _command(self, name, arg1=None, arg2=None, *options): - """ - Returns (typ, data) with - typ = response type - data = list of lists of strings read (only meaningfull if OK) - - The responce code and text may be found in .response_code - and .response_text, respectivly. - """ - if self.state not in commands[name]: - raise self.error( - 'Command %s illegal in state %s' % (name, self.state)) - # concatinate command and arguments (if any) - data = " ".join(filter(None, (name, arg1, arg2))) - if __debug__: - if self.debug >= 4: self._mesg('> %r' % data) - else: self._log('> %s' % data) - try: - try: - self._send('%s%s' % (data, CRLF)) - for o in options: - if __debug__: - if self.debug >= 4: self._mesg('> %r' % o) - else: self._log('> %r' % data) - self._send('%s%s' % (o, CRLF)) - except (socket.error, OSError), val: - raise self.abort('socket error: %s' % val) - return self._get_response() - except self.abort, val: - if __debug__: - if self.debug >= 1: - self.print_log() - raise - - - def _readstring(self, data): - if data[0] == ' ': # space -> error - raise self.error('Unexpected space: %r' % data) - elif data[0] == '"': # handle double quote: - if not self._match(re_dquote, data): - raise self.error('Unmatched quote: %r' % data) - snippet = self.mo.group(1) - return re_esc_quote.sub(r'\1', snippet), data[self.mo.end():] - elif self._match(Literal, data): - # read a 'literal' string - size = int(self.mo.group('size')) - if __debug__: - if self.debug >= 4: - self._mesg('read literal size %s' % size) - return self._read(size), self._get_line() - else: - data = data.split(' ', 1) - if len(data) == 1: - data.append('') - return data - - def _get_response(self): - """ - Returns (typ, data) with - typ = response type - data = list of lists of strings read (only meaningfull if OK) - - The responce code and text may be found in .response_code - and .response_text, respectivly. - """ - - """ - response-deletescript = response-oknobye - response-authenticate = *(string CRLF) (response-oknobye) - response-capability = *(string [SP string] CRLF) response-oknobye - response-listscripts = *(string [SP "ACTIVE"] CRLF) response-oknobye - response-oknobye = ("OK" / "NO" / "BYE") [SP "(" resp-code ")"] [SP string] CRLF - string = quoted / literal - quoted = <"> *QUOTED-CHAR <"> - literal = "{" number "+}" CRLF *OCTET - ;; The number represents the number of octets - ;; MUST be literal-utf8 except for values - ---> a response either starts with a quote-charakter, a left-bracket or - OK, NO, BYE - -"quoted" CRLF -"quoted" SP "quoted" CRLF -{size} CRLF *OCTETS CRLF -{size} CRLF *OCTETS CRLF -[A-Z-]+ CRLF - - """ - data = [] ; dat = None - resp = self._get_line() - while 1: - if self._match(Oknobye, resp): - typ, code, dat = self.mo.group('type','code','data') - if __debug__: - if self.debug >= 1: - self._mesg('%s response: %s %s' % (typ, code, dat)) - self.response_code = code - self.response_text = None - if dat: - self.response_text = self._readstring(dat)[0] - - # if server quits here, send code instead of empty data - if typ == "BYE": - return typ, code - - return typ, data -## elif 0: -## dat2 = None -## dat, resp = self._readstring(resp) -## if resp.startswith(' '): -## dat2, resp = self._readstring(resp[1:]) -## data.append( (dat, dat2)) -## resp = self._get_line() - else: - dat = [] - while 1: - dat1, resp = self._readstring(resp) - if __debug__: - if self.debug >= 4: - self._mesg('read: %r' % (dat1,)) - if self.debug >= 5: - self._mesg('rest: %r' % (resp,)) - dat.append(dat1) - if not resp.startswith(' '): - break - resp = resp[1:] - if len(dat) == 1: - dat.append(None) - data.append(dat) - resp = self._get_line() - return self.error('Should not come here') - - - def _match(self, cre, s): - # Run compiled regular expression match method on 's'. - # Save result, return success. - self.mo = cre.match(s) - if __debug__: - if self.mo is not None and self.debug >= 5: - self._mesg("\tmatched r'%s' => %s" % (cre.pattern, `self.mo.groups()`)) - return self.mo is not None - - - if __debug__: - - def _mesg(self, s, secs=None): - if secs is None: - secs = time.time() - tm = time.strftime('%M:%S', time.localtime(secs)) - sys.stderr.write(' %s.%02d %s\n' % (tm, (secs*100)%100, s)) - sys.stderr.flush() - - def _log(self, line): - # Keep log of last `_cmd_log_len' interactions for debugging. - self._cmd_log[self._cmd_log_idx] = (line, time.time()) - self._cmd_log_idx += 1 - if self._cmd_log_idx >= self._cmd_log_len: - self._cmd_log_idx = 0 - - def print_log(self): - self.self._mesg('last %d SIEVE interactions:' % len(self._cmd_log)) - i, n = self._cmd_log_idx, self._cmd_log_len - while n: - try: - self.self._mesg(*self._cmd_log[i]) - except: - pass - i += 1 - if i >= self._cmd_log_len: - i = 0 - n -= 1 - - ### Public methods ### - def authenticate(self, mechanism, *authobjects): - """Authenticate command - requires response processing.""" - # command-authenticate = "AUTHENTICATE" SP auth-type [SP string] *(CRLF string) - # response-authenticate = *(string CRLF) (response-oknobye) - mech = mechanism.upper() - if not mech in self.loginmechs: - raise self.error("Server doesn't allow %s authentication." % mech) - - if mech == AUTH_LOGIN: - authobjects = [ sieve_name(binascii.b2a_base64(ao)[:-1]) - for ao in authobjects - ] - elif mech == AUTH_PLAIN: - if len(authobjects) < 3: - # assume authorization identity (authzid) is missing - # and these two authobjects are username and password - authobjects.insert(0, '') - ao = '\0'.join(authobjects) - ao = binascii.b2a_base64(ao)[:-1] - authobjects = [ sieve_string(ao) ] - else: - raise self.error("managesieve doesn't support %s authentication." % mech) - - typ, data = self._command('AUTHENTICATE', - sieve_name(mech), *authobjects) - if typ == 'OK': - self.state = 'AUTH' - return typ - - - def login(self, auth, user, password): - """ - Authenticate to the Sieve server using the best mechanism available. - """ - for authmech in AUTHMECHS: - if authmech in self.loginmechs: - authobjs = [auth, user, password] - if authmech == AUTH_LOGIN: - authobjs = [user, password] - return self.authenticate(authmech, *authobjs) - else: - raise self.abort('No matching authentication mechanism found.') - - def logout(self): - """Terminate connection to server.""" - # command-logout = "LOGOUT" CRLF - # response-logout = response-oknobye - typ = self._simple_command('LOGOUT') - self.state = 'LOGOUT' - self._close() - return typ - - - def listscripts(self): - """Get a list of scripts on the server. - - (typ, [data]) = .listscripts() - - if 'typ' is 'OK', 'data' is list of (scriptname, active) tuples. - """ - # command-listscripts = "LISTSCRIPTS" CRLF - # response-listscripts = *(sieve-name [SP "ACTIVE"] CRLF) response-oknobye - typ, data = self._command('LISTSCRIPTS') - if typ != 'OK': return typ, data - scripts = [] - for dat in data: - if __debug__: - if not len(dat) in (1, 2): - self.error("Unexpected result from LISTSCRIPTS: %r" (dat,)) - scripts.append( (dat[0], dat[1] is not None )) - return typ, scripts - - - def getscript(self, scriptname): - """Get a script from the server. - - (typ, scriptdata) = .getscript(scriptname) - - 'scriptdata' is the script data. - """ - # command-getscript = "GETSCRIPT" SP sieve-name CRLF - # response-getscript = [string CRLF] response-oknobye - - typ, data = self._command('GETSCRIPT', sieve_name(scriptname)) - if typ != 'OK': return typ, data - if len(data) != 1: - self.error('GETSCRIPT returned more than one string/script') - # todo: decode data? - return typ, data[0][0] - - - def putscript(self, scriptname, scriptdata): - """Put a script onto the server.""" - # command-putscript = "PUTSCRIPT" SP sieve-name SP string CRLF - # response-putscript = response-oknobye - return self._simple_command('PUTSCRIPT', - sieve_name(scriptname), - sieve_string(scriptdata) - ) - - def deletescript(self, scriptname): - """Delete a scripts at the server.""" - # command-deletescript = "DELETESCRIPT" SP sieve-name CRLF - # response-deletescript = response-oknobye - return self._simple_command('DELETESCRIPT', sieve_name(scriptname)) - - - def setactive(self, scriptname): - """Mark a script as the 'active' one.""" - # command-setactive = "SETACTIVE" SP sieve-name CRLF - # response-setactive = response-oknobye - return self._simple_command('SETACTIVE', sieve_name(scriptname)) - - - def havespace(self, scriptname, size): - # command-havespace = "HAVESPACE" SP sieve-name SP number CRLF - # response-havespace = response-oknobye - return self._simple_command('HAVESPACE', - sieve_name(scriptname), - str(size)) - - - def capability(self): - """ - Isse a CAPABILITY command and return the result. - - As a side-effect, on succes these attributes are (re)set: - self.implementation - self.loginmechs - self.capabilities - self.supports_tls - """ - # command-capability = "CAPABILITY" CRLF - # response-capability = *(string [SP string] CRLF) response-oknobye - typ, data = self._command('CAPABILITY') - if typ == 'OK': - self._parse_capabilities(data) - return typ, data - - - def starttls(self, keyfile=None, certfile=None): - """Puts the connection to the SIEVE server into TLS mode. - - If the server supports TLS, this will encrypt the rest of the SIEVE - session. If you provide the keyfile and certfile parameters, - the identity of the SIEVE server and client can be checked. This, - however, depends on whether the socket module really checks the - certificates. - """ - # command-starttls = "STARTTLS" CRLF - # response-starttls = response-oknobye - typ, data = self._command('STARTTLS') - if typ == 'OK': - sslobj = ssl_wrap_socket(self.sock, keyfile, certfile) - self.sock = SSLFakeSocket(self.sock, sslobj) - self.file = SSLFakeFile(sslobj) - # MUST discard knowledge obtained from the server - self.__clear_knowledge() - # Some servers send capabilities after TLS handshake, some - # do not. We send a bogus command, and expect a NO. If you - # get something else instead, read the extra NO to clear - # the buffer. - typ, data = self._command('BOGUS') - if typ != 'NO': - typ, data = self._get_response() - # server may not advertise capabilities, thus we need to ask - self.capability() - if self.debug >= 3: self._mesg('started Transport Layer Security (TLS)') - return typ, data diff --git a/Tests/Integration/preferences.py b/Tests/Integration/preferences.py deleted file mode 100644 index 3742afac5..000000000 --- a/Tests/Integration/preferences.py +++ /dev/null @@ -1,164 +0,0 @@ -from config import hostname, port, username, password -import webdavlib -import urllib -import base64 -import simplejson - -import sogoLogin - - -DEBUG=False -#DEBUG=True - - -# must be kept in sync with SoObjects/SOGo/SOGoDefaults.plist -# this should probably be fetched magically... -SOGoSupportedLanguages = [ "Arabic", "Basque", "Bulgarian", "Catalan", "ChineseChina", "ChineseTaiwan", "Croatian", - "Czech", "Dutch", "Danish", "Welsh", "English", "Finnish", - "SpanishSpain", "SpanishArgentina", "French", "German", "Hebrew", - "Hungarian", "Indonesian", "Icelandic", "Italian", "Japanese", - "Latvian", "Lithuanian", "Macedonian", "Montenegrin", "Portuguese", "BrazilianPortuguese", - "NorwegianBokmal", "NorwegianNynorsk", "Polish", "Romanian", "Russian", - "Serbian", "SerbianLatin", "Slovak", "Slovenian", "Swedish", "TurkishTurkey", "Ukrainian" ]; -daysBetweenResponseList=[1,2,3,5,7,14,21,30] - -class HTTPPreferencesPOST (webdavlib.HTTPPOST): - cookie = None - - def prepare_headers (self): - headers = webdavlib.HTTPPOST.prepare_headers(self) - if self.cookie: - headers["Cookie"] = self.cookie - return headers - -class HTTPPreferencesGET (webdavlib.HTTPGET): - cookie = None - - def prepare_headers (self): - headers = webdavlib.HTTPGET.prepare_headers(self) - if self.cookie: - headers["Cookie"] = self.cookie - return headers - -class preferences: - login = username - passw = password - - def __init__(self, otherLogin = None, otherPassword = None): - if otherLogin and otherPassword: - self.login = otherLogin - self.passw = otherPassword - - self.client = webdavlib.WebDAVClient(hostname, port) - - authCookie = sogoLogin.getAuthCookie(hostname, port, self.login, self.passw) - self.cookie = authCookie - - #- If this is not set, we CAN'T save preferences - self.preferences = None - - def find_key(self, d, key): - if key in d: - return d - subdicts = [a[1] for a in d.iteritems() if type(a[1]) == dict] - for subd in subdicts: - ret = self.find_key(subd, key) - if ret: - return ret - return None - - def load_preferences(self): - defaults = self.get_defaults() - settings = self.get_settings() - self.preferences = {'defaults': defaults, 'settings': settings} - #print "LOAD PREFS:", self.preferences - - def get(self, preference=None): - if not self.preferences: - self.load_preferences() - #- Want the whole thing - if not preference: - return self.preferences - else: - tmpdict = self.find_key(self.preferences, preference) - if tmpdict: - return tmpdict[preference] - else: - return None - - def _get(self, subtype='jsonDefault', preference=None): - url = "/SOGo/so/%s/%s" % (self.login, subtype) - get = HTTPPreferencesGET(url) - get.cookie = self.cookie - self.client.execute(get) - if DEBUG: print "DEBUG (url):", url - if DEBUG: print "DEBUG (status):", get.response["status"] - if DEBUG: print "DEBUG (body):", get.response['body'] - content = simplejson.loads(get.response['body']) - result = None - try: - if preference: - result = content[preference] - else: - result = content - except: - pass - return result - - def get_defaults(self, preference=None): - return self._get('jsonDefaults', preference) - - def get_settings(self, preference=None): - return self._get('jsonSettings', preference) - - def set_nosave(self, preference, value=None): - # First check if we did a get, if not, must get first - if not self.preferences: - self.load_preferences() - - # Get the right sub-dict and change the key/value - subdict = self.find_key(self.preferences, preference) - if not subdict: - raise AttributeError("ERROR(nosubdict): looking for %s in: %s" %(preference, str(self.preferences))) - subdict[preference] = value - - def set(self, preference, value=None): - self.set_nosave(preference, value) - self.save() - - def set_multiple(self, preferences={}): - for key, value in preferences.iteritems(): - self.set_nosave(key, value) - self.save() - - def set_or_create(self, preference, value, paths=['defaults']): - if not self.preferences: - self.load_preferences() - subdict = self.find_key(self.preferences, preference) - #- Pref is not set - if not subdict: - subdict = self.preferences - for path in paths: - subdict = subdict.setdefault(path, {}) - subdict[preference] = value - - def save(self): - url = "/SOGo/so/%s/Preferences/save" % self.login - - post = HTTPPreferencesPOST(url, simplejson.dumps(self.preferences)) - post.content_type = "application/json" - post.cookie = self.cookie - self.client.execute(post) - - # Raise an exception if the pref wasn't properly set - if post.response["status"] != 200: - raise Exception ("failure setting prefs, (code = %d)" \ - % post.response["status"]) - - -# Simple main to test this class -if __name__ == "__main__": - p = preferences () - print p.get ("SOGoLanguage") - p.set ("SOGoLanguage", SOGoSupportedLanguages.index("French")) - print p.get ("SOGoLanguage") diff --git a/Tests/Integration/propfind.py b/Tests/Integration/propfind.py deleted file mode 100755 index 117cf0409..000000000 --- a/Tests/Integration/propfind.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password - -import webdavlib - -import sys -import getopt -import xml.dom.minidom - -def parseArguments(): - arguments = {} - -depth = "0" -quiet = False -(opts, args) = getopt.getopt(sys.argv[1:], "d:q", ["depth=", "quiet"]) - -for pair in opts: - if (pair[0] == "-d" or pair[0] == "--depth"): - depth = pair[1] - elif (pair[0] == "-q" or pair[0] == "--quiet"): - quiet = True - -# print "depth: " + depth - -nargs = len(args) -if (nargs > 0): - resource = args[0] - if (nargs > 1): - properties = args[1:] - else: - properties = [ "allprop" ] -else: - print "resource required" - sys.exit(-1) - -client = webdavlib.WebDAVClient(hostname, port, username, password) -propfind = webdavlib.WebDAVPROPFIND(resource, properties, depth) -client.execute(propfind) - -sys.stderr.write("response:\n\n") -print propfind.response["body"] - -if propfind.response.has_key("document"): - sys.stderr.write("document tree:\n") - elem = propfind.response["document"] - dom = xml.dom.minidom.parseString(xml.etree.ElementTree.tostring(elem)) - print dom.toprettyxml() - diff --git a/Tests/Integration/sogoLogin.py b/Tests/Integration/sogoLogin.py deleted file mode 100644 index f700e300e..000000000 --- a/Tests/Integration/sogoLogin.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password -import sys, getopt -import webdavlib -import urllib -import urllib2 -import base64 -import simplejson -import cookielib - -def getAuthCookie(hostname, port, username, password) : - cjar = cookielib.CookieJar(); - opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cjar)) - urllib2.install_opener(opener) - - creds2 = simplejson.dumps({"userName":username, "password": password}) - req = urllib2.Request("http://%s:%s/SOGo/connect" % (hostname, port), creds2, - {'Content-Type': 'application/json'}) - - fd = urllib2.urlopen(req) - - for cookie in cjar : - if cookie.name == "0xHIGHFLYxSOGo": - authinfo = cookie.value - break - - return "0xHIGHFLYxSOGo="+authinfo - -def usage() : - msg ="""Usage: -%s [-h] [-H | --host=hostname] [-p|--passwd=password] \ -[-P|--port=port] [-u|--user=username]\n""" % sys.argv[0] - - sys.stderr.write(msg); - -if __name__ == "__main__" : - try: - opts, args = getopt.getopt (sys.argv[1:], "hH:p:P:u:", \ - ("host=", "passwd=", "port=", "user=")); - except getopt.GetoptError: - usage() - exit(1) - for o, v in opts : - if o == "-h" : - usage() - exit(1) - elif o == "-H" or o == "--host" : - hostname = v - elif o == "-p" or o == "--passwd" : - password = v - elif o == "-P" or o == "--port" : - port = v - elif o == "-u" or o == "--user" : - username = v - - print getAuthCookie(hostname, port, username, password) diff --git a/Tests/Integration/sogotests.py b/Tests/Integration/sogotests.py deleted file mode 100644 index becf4f78c..000000000 --- a/Tests/Integration/sogotests.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys -import unittest -import time - -def UnitTestTextTestResultNewStartTest(self, test): - self.xstartTime = time.time() - self.oldStartTest(test) - -def UnitTestTextTestResultNewStopTest(self, test): - unittest.TestResult.stopTest(self, test) - endTime = time.time() - delta = endTime - self.xstartTime - print " %f ms" % delta - -def runTests(): - unittest._TextTestResult.oldStartTest = unittest._TextTestResult.startTest - unittest._TextTestResult.startTest = UnitTestTextTestResultNewStartTest - unittest._TextTestResult.stopTest = UnitTestTextTestResultNewStopTest - - argv = [] - argv.extend(sys.argv) - argv.append("-v") - unittest.main(argv=argv) diff --git a/Tests/Integration/test-caldav-scheduling.py b/Tests/Integration/test-caldav-scheduling.py deleted file mode 100755 index e7037ca40..000000000 --- a/Tests/Integration/test-caldav-scheduling.py +++ /dev/null @@ -1,1081 +0,0 @@ -#!/usr/bin/python - -# setup: 4 users are needed: username, attendee1_username, -# attendee1_delegate_username and superuser. -# when writing new tests, avoid using superuser when not absolutely needed - -# TODO -# - Individual tests should set the ACLs themselves on Resources tests - -from config import hostname, port, username, password, \ - superuser, superuser_password, \ - attendee1, attendee1_username, \ - attendee1_password, \ - attendee1_delegate, attendee1_delegate_username, \ - attendee1_delegate_password, \ - resource_no_overbook, resource_can_overbook - -import datetime -import dateutil.tz -import sogotests -import sys -import time -import unittest -import utilities -import vobject -import vobject.base -import vobject.icalendar -import webdavlib -import StringIO -import xml.etree.ElementTree - -class CalDAVPropertiesTest(unittest.TestCase): - def setUp(self): - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - self.test_calendar \ - = "/SOGo/dav/%s/Calendar/test-dav-properties/" % username - mkcol = webdavlib.WebDAVMKCOL(self.test_calendar) - self.client.execute(mkcol) - - def tearDown(self): - delete = webdavlib.WebDAVDELETE(self.test_calendar) - self.client.execute(delete) - - def testDavScheduleCalendarTransparency(self): - """{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp""" - - ## PROPFIND - propfind = webdavlib.WebDAVPROPFIND(self.test_calendar, - ["{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp"], - 0) - self.client.execute(propfind) - response = propfind.response["document"].find('{DAV:}response') - propstat = response.find('{DAV:}propstat') - status = propstat.find('{DAV:}status').text[9:12] - - self.assertEquals(status, "200", - "schedule-calendar-transp marked as 'Not Found' in response") - transp = propstat.find('{DAV:}prop/{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp') - values = transp.getchildren() - self.assertEquals(len(values), 1, "one and only one element expected") - value = values[0] - self.assertTrue(xml.etree.ElementTree.iselement(value)) - ns = value.tag[0:31] - tag = value.tag[31:] - self.assertTrue(ns == "{urn:ietf:params:xml:ns:caldav}", - "schedule-calendar-transp must have a value in"\ - " namespace '%s', not '%s'" - % ("urn:ietf:params:xml:ns:caldav", ns)) - self.assertTrue(tag == "opaque", - "schedule-calendar-transp must be 'opaque' on new" \ - " collections, not '%s'" % tag) - - ## PROPPATCH - newValueNode = "{urn:ietf:params:xml:ns:caldav}thisvaluedoesnotexist" - proppatch = webdavlib.WebDAVPROPPATCH(self.test_calendar, - {"{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": \ - { newValueNode: True }}) - self.client.execute(proppatch) - self.assertEquals(proppatch.response["status"], 400, - "expecting failure when setting transparency to" \ - " an invalid value") - - newValueNode = "{urn:ietf:params:xml:ns:caldav}transparent" - proppatch = webdavlib.WebDAVPROPPATCH(self.test_calendar, - {"{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": \ - { newValueNode: True }}) - self.client.execute(proppatch) - self.assertEquals(proppatch.response["status"], 207, - "failure (%s) setting transparency to" \ - " 'transparent': '%s'" - % (proppatch.response["status"], - proppatch.response["body"])) - - newValueNode = "{urn:ietf:params:xml:ns:caldav}opaque" - proppatch = webdavlib.WebDAVPROPPATCH(self.test_calendar, - {"{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp": \ - { newValueNode: True }}) - self.client.execute(proppatch) - self.assertEquals(proppatch.response["status"], 207, - "failure (%s) setting transparency to" \ - " 'transparent': '%s'" - % (proppatch.response["status"], - proppatch.response["body"])) - -class CalDAVSchedulingTest(unittest.TestCase): - def setUp(self): - self.superuser_client = webdavlib.WebDAVClient(hostname, port, - superuser, superuser_password) - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - self.attendee1_client = webdavlib.WebDAVClient(hostname, port, - attendee1_username, attendee1_password) - self.attendee1_delegate_client = webdavlib.WebDAVClient(hostname, port, - attendee1_delegate_username, attendee1_delegate_password) - - utility = utilities.TestUtility(self, self.client) - (self.user_name, self.user_email) = utility.fetchUserInfo(username) - (self.attendee1_name, self.attendee1_email) = utility.fetchUserInfo(attendee1) - (self.attendee1_delegate_name, self.attendee1_delegate_email) = utility.fetchUserInfo(attendee1_delegate) - (self.res_no_ob_name, self.res_no_ob_email) = utility.fetchUserInfo(resource_no_overbook) - (self.res_can_ob_name, self.res_can_ob_email) = utility.fetchUserInfo(resource_can_overbook) - - self.user_calendar = "/SOGo/dav/%s/Calendar/personal/" % username - self.attendee1_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1 - self.attendee1_delegate_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1_delegate - self.res_calendar = "/SOGo/dav/%s/Calendar/personal/" % resource_no_overbook - self.res_ob_calendar = "/SOGo/dav/%s/Calendar/personal/" % resource_can_overbook - - # fetch non existing event to let sogo create the calendars in the db - self._getEvent(self.client, "%snonexistent" % self.user_calendar, exp_status=404) - self._getEvent(self.attendee1_client, "%snonexistent" % self.attendee1_calendar, exp_status=404) - self._getEvent(self.attendee1_delegate_client, "%snonexistent" % - self.attendee1_delegate_calendar, exp_status=404) - - # list of ics used by the test. - # tearDown will loop over this and wipe them in all users' calendar - self.ics_list = [] - - - def tearDown(self): - # delete all created events from all users' calendar - for ics in self.ics_list: - self._deleteEvent(self.superuser_client, - "%s%s" % (self.user_calendar, ics), None) - self._deleteEvent(self.superuser_client, - "%s%s" % (self.attendee1_calendar, ics), None) - self._deleteEvent(self.superuser_client, - "%s%s" % (self.attendee1_delegate_calendar, ics), None) - self._deleteEvent(self.superuser_client, - "%s%s" % (self.res_calendar, ics), None) - self._deleteEvent(self.superuser_client, - "%s%s" % (self.res_ob_calendar, ics), None) - - def _newEvent(self, summary="test event", uid="test", transp=0): - transparency = ("OPAQUE", "TRANSPARENT") - - newCal = vobject.iCalendar() - vevent = newCal.add('vevent') - vevent.add('summary').value = summary - vevent.add('transp').value = transparency[transp] - - now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) - startdate = vevent.add('dtstart') - startdate.value = now - enddate = vevent.add('dtend') - enddate.value = now + datetime.timedelta(0, 3600) - vevent.add('uid').value = uid - vevent.add('dtstamp').value = now - vevent.add('last-modified').value = now - vevent.add('created').value = now - vevent.add('class').value = "PUBLIC" - vevent.add('sequence').value = "0" - - return newCal - - def _putEvent(self, client, filename, event, exp_status = 201): - put = webdavlib.HTTPPUT(filename, event.serialize()) - put.content_type = "text/calendar; charset=utf-8" - client.execute(put) - if exp_status is not None: - self.assertEquals(put.response["status"], exp_status) - - def _postEvent(self, client, outbox, event, originator, recipients, - exp_status = 200): - post = webdavlib.CalDAVPOST(outbox, event.serialize(), - originator, recipients) - client.execute(post) - if exp_status is not None: - self.assertEquals(post.response["status"], exp_status) - - def _getEvent(self, client, filename, exp_status = 200): - get = webdavlib.HTTPGET(filename) - client.execute(get) - - if exp_status is not None: - self.assertEquals(get.response["status"], exp_status) - - if get.response["headers"]["content-type"].startswith("text/calendar"): - stream = StringIO.StringIO(get.response["body"]) - event = vobject.base.readComponents(stream).next() - else: - event = None - - return event - - def _deleteEvent(self, client, filename, exp_status = 204): - delete = webdavlib.WebDAVDELETE(filename) - client.execute(delete) - if exp_status is not None: - self.assertEquals(delete.response["status"], exp_status) - - def _getAllEvents(self, client, collection, exp_status = 207): - propfind = webdavlib.WebDAVPROPFIND(collection, None) - client.execute(propfind) - if exp_status is not None: - self.assertEquals(propfind.response["status"], exp_status) - - content = [] - nodes = propfind.response["document"].findall('{DAV:}response') - for node in nodes: - responseHref = node.find('{DAV:}href').text - content += [responseHref] - - return content - - def _deleteAllEvents(self, client, collection, exp_status = 204): - content = self._getAllEvents(client, collection) - for item in content: - self._deleteEvent(client, item) - - def _eventAttendees(self, event): - attendees = {} - - event_component = event.vevent - for child in event_component.getChildren(): - if child.name == "ATTENDEE": - try: - delegated_to = child.delegated_to_param - except: - delegated_to = "(none)" - try: - delegated_from = child.delegated_from_param - except: - delegated_from = "(none)" - attendees[child.value] = ("%s/%s/%s" - % (child.partstat_param, - delegated_to, - delegated_from)) - - return attendees - - def _compareAttendees(self, compared_event, event): - compared_attendees = self._eventAttendees(compared_event) - compared_emails = compared_attendees.keys() - self.assertTrue(len(compared_emails) > 0, - "no attendee found") - compared_emails.sort() - - attendees = self._eventAttendees(event) - emails = attendees.keys() - emails.sort() - - self.assertEquals(len(compared_emails), len(emails), - "number of attendees is not equal" - + " (actual: %d, exp: %d)" - % (len(compared_emails), len(emails))) - - for email in emails: - self.assertEquals(compared_attendees[email], - attendees[email], - "partstat for attendee '%s' does not match" - " (actual: '%s', expected: '%s')" - % (email, - compared_attendees[email], attendees[email])) - - def testAddAttendee(self): - """ add attendee after event creation """ - - # make sure the event doesn't exist - ics_name = "test-add-attendee.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar,ics_name), None) - - # 1. create an event in the organiser's calendar - event = self._newEvent(summary="Test add attendee", uid="Test add attendee") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. add an attendee - event.add("method").value = "REQUEST" - attendee = event.vevent.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event, - exp_status=204) - - - # 3. verify that the attendee has the event - attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name)) - - # 4. make sure the received event match the original one - # XXX is this enough? - self.assertEquals(event.vevent.uid, attendee_event.vevent.uid) - - def testUninviteAttendee(self): - """ Remove attendee after event creation """ - - # make sure the event doesn't exist - ics_name = "test-remove-attendee.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar,ics_name), None) - - # 1. create an event in the organiser's calendar - event = self._newEvent(summary="Test uninvite attendee", uid="Test uninvite attendee") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # keep a copy around for updates without other attributes - noAttendeeEvent = vobject.iCalendar() - noAttendeeEvent.copy(event) - - # 2. add an attendee - event.add("method").value = "REQUEST" - attendee = event.vevent.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event, - exp_status=204) - - # 3. verify that the attendee has the event - attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name)) - - # 4. make sure the received event match the original one - self.assertEquals(event.vevent.uid, attendee_event.vevent.uid) - - # 5. uninvite the attendee - put the event back without the attendee - now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) - noAttendeeEvent.vevent.last_modified.value = now - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), noAttendeeEvent, - exp_status=204) - - # 6. verify that the attendee doesn't have the event anymore - attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), 404) - - def testResourceNoOverbook(self): - """ try to overbook a resource """ - - # make sure there are no events in the resource calendar - self._deleteAllEvents(self.superuser_client, self.res_calendar) - - # make sure the event doesn't exist - ics_name = "test-no-overbook.ics" - self.ics_list += [ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ics_name), None) - - ob_ics_name = "test-no-overbook-overlap.ics" - self.ics_list += [ob_ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ob_ics_name), None) - - # 1. create an event in the organiser's calendar - event = self._newEvent(summary="Test no overbook", uid="test no overbook") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.res_no_ob_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.res_no_ob_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. create a second event overlapping the first one - event = self._newEvent(summary="Test no overbook - overlap", uid="test no overbook - overlap") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.res_no_ob_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.res_no_ob_email - - # put the event - should trigger a 409 - self._putEvent(self.client, "%s%s" % (self.user_calendar, ob_ics_name), event, exp_status=409) - - def testResourceCanOverbook(self): - """ try to overbook a resource - multiplebookings=0""" - - # make sure there are no events in the resource calendar - self._deleteAllEvents(self.superuser_client, self.res_ob_calendar) - - # make sure the event doesn't exist - ics_name = "test-can-overbook.ics" - self.ics_list += [ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ics_name), None) - - ob_ics_name = "test-can-overbook-overlap.ics" - self.ics_list += [ob_ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ob_ics_name), None) - - # 1. create an event in the organiser's calendar - event = self._newEvent(summary="Test can overbook", uid="test can overbook") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.res_can_ob_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.res_can_ob_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. create a second event overlapping the first one - event = self._newEvent(summary="Test can overbook - overlap", uid="test can overbook - overlap") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.res_can_ob_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.res_can_ob_email - - # put the event - should be fine since we can overbook this one - self._putEvent(self.client, "%s%s" % (self.user_calendar, ob_ics_name), event) - - def testResourceBookingOverlapDetection(self): - """ Resource booking overlap detection - bug #1837""" - - # There used to be some problems with recurring events and resources booking - # This test implements these edge cases - - # 1. Create recurring event (with resource) - # 2. Create single event overlaping one instance for the previous event - # (should fail) - # 3. Create recurring event which _doesn't_ overlap the first event - # (should be OK, used to fail pre1.3.17) - # 4. Create recurring event overlapping the previous recurring event - # (should fail) - - # make sure there are no events in the resource calendar - self._deleteAllEvents(self.superuser_client, self.res_calendar) - - # make sure the event doesn't exist - ics_name = "test-res-overlap-detection.ics" - self.ics_list += [ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ics_name), None) - - overlap_ics_name = "test-res-overlap-detection-overlap.ics" - self.ics_list += [overlap_ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.attendee1_calendar,overlap_ics_name), None) - - nooverlap_recurring_ics_name = "test-res-overlap-detection-nooverlap.ics" - self.ics_list += [nooverlap_recurring_ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,nooverlap_recurring_ics_name), None) - - overlap_recurring_ics_name = "test-res-overlap-detection-overlap-recurring.ics" - self.ics_list += [overlap_recurring_ics_name] - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,overlap_recurring_ics_name), None) - - # 1. create recurring event with resource - event = self._newEvent(summary="recurring event with resource", - uid="recurring event w resource") - event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5" - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.res_no_ob_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.res_no_ob_email - - # keep a copy around for #3 - nooverlap_event = vobject.iCalendar() - nooverlap_event.copy(event) - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. Create single event overlaping one instance for the previous event - event = self._newEvent(summary="recurring event with resource", - uid="recurring event w resource - overlap") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.attendee1_name - organizer.value = self.attendee1_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.res_no_ob_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.res_no_ob_email - # should fail - self._putEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, overlap_ics_name), event, exp_status=409) - - # 3. Create recurring event which _doesn't_ overlap the first event - # (should be OK, used to fail pre1.3.17) - # shift the start date to one hour after the original event end time - nstartdate = nooverlap_event.vevent.dtend.value + datetime.timedelta(0, 3600) - nooverlap_event.vevent.dtstart.value = nstartdate - nooverlap_event.vevent.dtend.value = nstartdate + datetime.timedelta(0, 3600) - nooverlap_event.vevent.uid.value = "recurring - nooverlap" - - self._putEvent(self.client, "%s%s" % (self.user_calendar, nooverlap_recurring_ics_name), nooverlap_event) - - # 4. Create recurring event overlapping the previous recurring event - # should fail with a 409 - nstartdate = nooverlap_event.vevent.dtstart.value + datetime.timedelta(0, 300) - nooverlap_event.vevent.dtstart.value = nstartdate - nooverlap_event.vevent.dtend.value = nstartdate + datetime.timedelta(0, 3600) - nooverlap_event.vevent.uid.value = "recurring - overlap" - self._putEvent(self.client, "%s%s" % (self.user_calendar, overlap_recurring_ics_name), nooverlap_event, exp_status=409) - - - def NOtestRruleExceptionInvitationDance(self): - """ RRULE exception invitation dance """ - - # This workflow is based on what lightning 1.2.1 does - # create a reccurring event - # add an exception - # invite bob to the exception: - # bob is declined in the master event - # bob needs-action in the exception - # bob accepts - # bob is declined in the master event - # bob is accepted in the exception - # the organizer 'uninvites' bob - # the event disappears from bob's calendar - # bob isn't in the master+exception event - - ics_name = "test-rrule-exception-invitation-dance.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar, ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar, ics_name), None) - - # 1. create a recurring event in the organiser's calendar - summary="Test reccuring exception invite cancel" - uid="Test-recurring-exception-invite-cancel" - event = self._newEvent(summary, uid) - event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5" - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # read the event back from the server - org_ev = self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name)) - - # 2. Add an exception to the master event and invite attendee1 to it - now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) - org_ev.vevent.last_modified.value = now - orig_dtstart = org_ev.vevent.dtstart.value - orig_dtend = org_ev.vevent.dtend.value - - ev_exception = org_ev.add("vevent") - ev_exception.add('created').value = now - ev_exception.add('last-modified').value = now - ev_exception.add('dtstamp').value = now - ev_exception.add('uid').value = uid - ev_exception.add('summary').value = summary - # out of laziness, add the exception for the first occurence of the event - recurrence_id = orig_dtstart - ev_exception.add('recurrence-id').value = recurrence_id - - ev_exception.add('transp').value = "OPAQUE" - ev_exception.add('description').value = "Exception" - ev_exception.add('sequence').value = "1" - ev_exception.add('dtstart').value = orig_dtstart - ev_exception.add('dtend').value = orig_dtend - - # 2.1 Add attendee1 and organizer to the exception - organizer = ev_exception.add('organizer') - organizer.cn_param = self.user_name - organizer.partstat_param = "ACCEPTED" - organizer.value = self.user_email - attendee = ev_exception.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.role_param = "REQ-PARTICIPANT" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), org_ev, - exp_status=204) - - # 3. Make sure the attendee got the event - attendee_ev = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name)) - - for ev in attendee_ev.vevent_list: - try: - if (ev.recurrence_id.value): - attendee_ev_exception = ev - except: - attendee_ev_master = ev - - # make sure sogo doesn't duplicate attendees - yes, we've seen that - self.assertEquals(len(attendee_ev_master.attendee_list), 1) - self.assertEquals(len(attendee_ev_exception.attendee_list), 1) - - # 4. The master event must contain the invitation, declined - self.assertEquals(attendee_ev_master.attendee.partstat_param, "DECLINED") - - # 5. The exception event contain the invitation, NEEDS-ACTION - self.assertEquals(attendee_ev_exception.attendee.partstat_param, "NEEDS-ACTION") - - # 6. attendee accepts invitation - attendee_ev_exception.attendee.partstat_param = "ACCEPTED" - self._putEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), - attendee_ev, exp_status=204) - - # fetch the organizer's event - org_ev = self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name)) - org_ev_exception = False - for ev in org_ev.vevent_list: - try: - if (ev.recurrence_id.value): - org_ev_exception = ev - except: - org_ev_master = ev - - # make sure sogo doesn't duplicate attendees - self.assertEquals(len(org_ev_master.attendee_list), 1) - self.assertEquals(len(org_ev_exception.attendee_list), 1) - - # 7. Make sure organizer got the accept for the exception and - # that the attendee is still declined in the master - self.assertEquals(org_ev_exception.attendee.partstat_param, "ACCEPTED") - self.assertEquals(org_ev_master.attendee.partstat_param, "DECLINED") - - # 8. delete the attendee from the master event (uninvite) - # The event should be deleted from the attendee's calendar - del org_ev_exception.attendee - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), - org_ev, exp_status=204) - del org_ev_master.attendee - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), - org_ev, exp_status=204) - - self._getEvent(self.client, "%s%s" % (self.attendee1_calendar, ics_name), - exp_status=404) - - # now be happy - - def testRruleInvitationDeleteExdate(self): - """RRULE invitation delete exdate dance""" - - # Workflow: - # Create an recurring event and invite Bob - # Add an exdate to the master event - # Verify that the exdate has propagated to Bob's calendar - # Add an exdate to bob's version of the event - # Verify that an exception has been created in the org's calendar - # and that bob is 'declined' - - ics_name = "test-rrule-invitation-deleted-exdate-dance.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar, ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar, ics_name), None) - - # 1. create a recurring event in the organiser's calendar - summary="Test-rrule-invitation-deleted-exdate-dance" - uid=summary - event = self._newEvent(summary, uid) - event.vevent.add('rrule').value = "FREQ=DAILY;COUNT=5" - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.partstat_param = "ACCEPTED" - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.role_param = "REQ-PARTICIPANT" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. Make sure the attendee got it - self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name)) - - # 3. Add exdate to master event - org_ev=self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name)) - orig_dtstart = org_ev.vevent.dtstart.value - # exdate is a list in vobject.icalendar - org_exdate = [orig_dtstart.astimezone(dateutil.tz.gettz("UTC"))] - org_ev.vevent.add('exdate').value = org_exdate - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), org_ev, exp_status=204) - - # 4. make sure the attendee has the exdate - attendee_ev = self._getEvent(self.attendee1_client, "%s%s" % - (self.attendee1_calendar, ics_name)) - self.assertEqual(org_exdate, attendee_ev.vevent.exdate.value) - - # 5. Create an exdate in the attendee's calendar - new_exdate = orig_dtstart + datetime.timedelta(days=2) - attendee_exdate = [new_exdate.astimezone(dateutil.tz.gettz("UTC"))] - attendee_ev.vevent.add('exdate').value = attendee_exdate - now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) - attendee_ev.vevent.last_modified.value = now - self._putEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, ics_name), - attendee_ev, exp_status=204) - - # 6. Make sure the attendee is: - # needs-action in master event - # declined in the new exception created by the exdate above - org_ev=self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name)) - org_ev_exception = False - for ev in org_ev.vevent_list: - try: - if (ev.recurrence_id.value == attendee_exdate[0]): - org_ev_exception = ev - except: - org_ev_master = ev - - self.assertTrue(org_ev_exception) - # make sure sogo doesn't duplicate attendees - self.assertEquals(len(org_ev_master.attendee_list), 1) - self.assertEquals(len(org_ev_exception.attendee_list), 1) - - self.assertEqual(org_ev_master.attendee.partstat_param, "NEEDS-ACTION"); - self.assertEqual(org_ev_exception.attendee.partstat_param, "DECLINED"); - - def testOrganizerIsAttendee(self): - """ iCal organizer is attendee - bug #1839 """ - - # This tries to have the same behavior as iCal - # 1. create an event, add an attendee and add the organizer as an attendee - # 2. SOGo should remove the organizer from the attendee list - ics_name = "test-organizer-is-attendee.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar, ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar, ics_name), None) - - # 1. create a recurring event in the organiser's calendar - summary="org is attendee" - uid=summary - event = self._newEvent(summary, uid) - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.partstat_param = "ACCEPTED" - organizer.value = self.user_email - attendee = event.vevent.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.role_param = "REQ-PARTICIPANT" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - - # 1.1 add the organizer as an attendee - attendee = event.vevent.add('attendee') - attendee.cn_param = self.user_name - attendee.rsvp_param = "TRUE" - attendee.role_param = "REQ-PARTICIPANT" - attendee.partstat_param = "ACCEPTED" - attendee.value = self.user_email - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. Fetch the event and make sure the organizer is not in the attendee list anymore - org_ev = self._getEvent(self.client, "%s%s" % (self.user_calendar, ics_name)) - - for attendee in org_ev.vevent.attendee_list: - self.assertNotEqual(self.user_email, attendee.value) - - def testEventsWithSameUID(self): - """ PUT 2 events with the same UID - bug #1853 """ - - ics_name = "test-same-uid.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar, ics_name), None) - - conflict_ics_name = "test-same-uid-conflict.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar, conflict_ics_name), None) - - # 1. create simple event - summary="same uid" - uid=summary - event = self._newEvent(summary, uid) - - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # PUT the same event with a new filename - should trigger a 409 - self._putEvent(self.client, "%s%s" % (self.user_calendar, conflict_ics_name), event, exp_status=409) - - def testInvitationDelegation(self): - """ invitation delegation """ - - ics_name = "test-delegation.ics" - self.ics_list += [ics_name] - - # the invitation must not exist - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar, ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar, ics_name), None) - self._deleteEvent(self.attendee1_delegate_client, - "%s%s" % (self.attendee1_delegate_calendar, ics_name), None) - - # 1. org -> attendee => org: 1, attendee: 1 (pst=N-A), delegate: 0 - - invitation = self._newEvent() - invitation.add("method").value = "REQUEST" - organizer = invitation.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - attendee = invitation.vevent.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - - self._postEvent(self.client, self.user_calendar, invitation, - self.user_email, [self.attendee1_email]) - del invitation.method - self._putEvent(self.client, - "%stest-delegation.ics" % self.user_calendar, - invitation) - - att_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" - % self.attendee1_calendar) - self._compareAttendees(att_inv, invitation) - - # 2. attendee delegates to delegate - # => org: 1 (updated), attendee: 1 (updated,pst=D), - # delegate: 1 (new,pst=N-A) - - invitation.add("method").value = "REQUEST" - attendee1 = invitation.vevent.attendee - attendee1.partstat_param = "DELEGATED" - attendee1.delegated_to_param = self.attendee1_delegate_email - delegate = invitation.vevent.add('attendee') - delegate.delegated_from_param = self.attendee1_email - delegate.cn_param = self.attendee1_delegate_name - delegate.rsvp_param = "TRUE" - delegate.partstat_param = "NEEDS-ACTION" - delegate.value = self.attendee1_delegate_email - - self._postEvent(self.attendee1_client, - self.attendee1_calendar, invitation, - self.attendee1_email, [self.attendee1_delegate_email]) - invitation.method.value = "REPLY" - self._postEvent(self.attendee1_client, - self.attendee1_calendar, invitation, - self.attendee1_email, [self.user_email]) - del invitation.method - self._putEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar, - invitation, 204) - - del_inv = self._getEvent(self.attendee1_delegate_client, - "%stest-delegation.ics" - % self.attendee1_delegate_calendar) - self._compareAttendees(del_inv, invitation) - org_inv = self._getEvent(self.client, - "%stest-delegation.ics" % self.user_calendar) - self._compareAttendees(org_inv, invitation) - - # 3. delegate accepts - # => org: 1 (updated), attendee: 1 (updated,pst=D), - # delegate: 1 (accepted,pst=A) - - invitation.add("method").value = "REPLY" - delegate.partstat_param = "ACCEPTED" - self._postEvent(self.attendee1_delegate_client, - self.attendee1_delegate_calendar, invitation, - self.attendee1_delegate_email, [self.user_email, self.attendee1_email]) - del invitation.method - self._putEvent(self.attendee1_delegate_client, - "%stest-delegation.ics" % self.attendee1_delegate_calendar, - invitation, 204) - - org_inv = self._getEvent(self.client, - "%stest-delegation.ics" % self.user_calendar) - self._compareAttendees(org_inv, invitation) - att_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar) - self._compareAttendees(att_inv, invitation) - - # 4. attendee accepts - # => org: 1 (updated), attendee: 1 (updated,pst=A), - # delegate: 0 (cancelled, deleted) - - cancellation = vobject.iCalendar() - cancellation.copy(invitation) - cancellation.add("method").value = "CANCEL" - cancellation.vevent.sequence.value = "1" - self._postEvent(self.attendee1_client, - self.attendee1_calendar, cancellation, - self.attendee1_email, [self.attendee1_delegate_email]) - - attendee1 = invitation.vevent.attendee - attendee1.partstat_param = "ACCEPTED" - del attendee1.delegated_to_param - invitation.add("method").value = "REPLY" - invitation.vevent.remove(delegate) - self._postEvent(self.attendee1_client, - self.attendee1_calendar, invitation, - self.attendee1_email, [self.user_email]) - - del invitation.method - self._putEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar, - invitation, 204) - - org_inv = self._getEvent(self.client, - "%stest-delegation.ics" % self.user_calendar) - self._compareAttendees(org_inv, invitation) - - del_inv = self._getEvent(self.attendee1_delegate_client, - "%stest-delegation.ics" % self.attendee1_delegate_calendar, 404) - - # 5. org updates inv. - # => org: 1 (updated), attendee: 1 (updated), delegate: 0 - - invitation.add("method").value = "REQUEST" - #invitation.vevent.summary.value = "Updated invitation" - invitation.vevent.sequence.value = "1" - attendee.partstat_param = "NEEDS-ACTION" - now = datetime.datetime.now() - invitation.vevent.last_modified.value = now - invitation.vevent.dtstamp.value = now - - self._postEvent(self.client, self.user_calendar, invitation, - self.user_email, [self.attendee1_email]) - - del invitation.method - self._putEvent(self.client, - "%stest-delegation.ics" % self.user_calendar, - invitation, 204) - - att_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar) - self._compareAttendees(att_inv, invitation) - - # 6. attendee delegates to delegate - # => org: 1 (updated), attendee: 1 (updated), delegate: 1 (new) - - invitation.add("method").value = "REQUEST" - attendee1.partstat_param = "DELEGATED" - attendee1.delegated_to_param = self.attendee1_delegate_email - - delegate = invitation.vevent.add('attendee') - delegate.delegated_from_param = self.attendee1_email - delegate.cn_param = self.attendee1_delegate_name - delegate.rsvp_param = "TRUE" - delegate.partstat_param = "NEEDS-ACTION" - delegate.value = self.attendee1_delegate_email - - self._postEvent(self.attendee1_client, - self.attendee1_calendar, invitation, - self.attendee1_email, [self.attendee1_delegate_email]) - invitation.method.value = "REPLY" - self._postEvent(self.attendee1_client, - self.attendee1_calendar, invitation, - self.attendee1_email, [self.user_email]) - del invitation.method - self._putEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar, - invitation, 204) - - org_inv = self._getEvent(self.client, - "%stest-delegation.ics" % self.user_calendar) - self._compareAttendees(org_inv, invitation) - del_inv = self._getEvent(self.attendee1_delegate_client, - "%stest-delegation.ics" - % self.attendee1_delegate_calendar) - self._compareAttendees(del_inv, invitation) - - # 7. delegate accepts - # => org: 1 (updated), attendee: 1 (updated), delegate: 1 (accepted) - - invitation.add("method").value = "REPLY" - delegate.partstat_param = "ACCEPTED" - self._postEvent(self.attendee1_delegate_client, - self.attendee1_delegate_calendar, invitation, - self.attendee1_delegate_email, [self.user_email, - self.attendee1_email]) - del invitation.method - self._putEvent(self.attendee1_delegate_client, - "%stest-delegation.ics" % self.attendee1_delegate_calendar, - invitation, 204) - - org_inv = self._getEvent(self.client, - "%stest-delegation.ics" % self.user_calendar) - self._compareAttendees(org_inv, invitation) - att_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar) - self._compareAttendees(att_inv, invitation) - - # 8. org updates inv. - # => org: 1 (updated), attendee: 1 (updated,partstat unchanged), - # delegate: 1 (updated,partstat reset) - - invitation.add("method").value = "REQUEST" - now = datetime.datetime.now() - invitation.vevent.last_modified.value = now - invitation.vevent.dtstamp.value = now - #invitation.vevent.summary.value = "Updated invitation (again)" - invitation.vevent.sequence.value = "2" - delegate.partstat_param = "NEEDS-ACTION" - - self._postEvent(self.client, self.user_calendar, invitation, - self.user_email, [self.attendee1_email, self.attendee1_delegate_email]) - - del invitation.method - self._putEvent(self.client, - "%stest-delegation.ics" % self.user_calendar, - invitation, 204) - - att_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar) - self._compareAttendees(att_inv, invitation) - del_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar) - self._compareAttendees(del_inv, invitation) - - # 9. org cancels invitation - # => org: 1 (updated), attendee: 0 (cancelled, deleted), - # delegate: 0 (cancelled, deleted) - - invitation.add("method").value = "CANCEL" - now = datetime.datetime.now() - invitation.vevent.last_modified.value = now - invitation.vevent.dtstamp.value = now - #invitation.vevent.summary.value = "Cancelled invitation (again)" - invitation.vevent.sequence.value = "3" - - self._postEvent(self.client, self.user_calendar, invitation, - self.user_email, [self.attendee1_email, self.attendee1_delegate_email]) - - del invitation.method - invitation.vevent.remove(attendee) - invitation.vevent.remove(delegate) - self._putEvent(self.client, - "%stest-delegation.ics" % self.user_calendar, - invitation, 204) - - att_inv = self._getEvent(self.attendee1_client, - "%stest-delegation.ics" % self.attendee1_calendar, 404) - del_inv = self._getEvent(self.attendee1_delegate_client, - "%stest-delegation.ics" % self.attendee1_delegate_calendar, 404) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-carddav.py b/Tests/Integration/test-carddav.py deleted file mode 100755 index 0d642d683..000000000 --- a/Tests/Integration/test-carddav.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python - - -from config import hostname, port, username, password - -import carddav -import sogotests -import unittest -import webdavlib -import time - - -class JsonDavEventTests(unittest.TestCase): - def setUp(self): - self._connect_as_user() - - def _connect_as_user(self, newuser=username, newpassword=password): - self.dv = carddav.Carddav(newuser, newpassword) - - def _create_new_event(self, path): - gid = self.dv.newguid(path) - event = {'startDate': "2015-12-25", - 'startTime': "10:00", - 'endDate': "2015-12-25", - 'endTime': "23:00", - 'isTransparent': 0, - 'sendAppointmentNotifications': 0, - 'summary': "Big party", - 'alarm': {'action': 'display', - 'quantity': 10, - 'unit': "MINUTES", - 'reference': "BEFORE", - 'relation': "START", - 'email': "sogo1@example.com"}, - 'organizer': {'name': u"Balthazar C\xe9sar", - 'email': "sogo2@example.com"}, - 'c_name': gid, - 'c_folder': path - } - return (event, path, gid) - - def _get_dav_data(self, filename, user=username, passwd=password): - w = webdavlib.WebDAVClient(hostname, port, user, passwd) - query = webdavlib.HTTPGET("http://localhost/SOGo/dav/%s/Calendar/personal/%s" % (username, filename)) - w.execute(query) - self.assertEquals(query.response['status'], 200) - return query.response['body'].split("\r\n") - - def _get_dav_field(self, davdata, fieldname): - try: - data = [a.split(':')[1] for a in davdata if fieldname in a][0] - except IndexError: - data = '' - return data - - def test_create_new_event(self): - path = 'Calendar/personal' - (event, folder, gid) = self._create_new_event(path) - #print "Saving Event to:", folder, gid - self.dv.save_event(event, folder, gid) - #- Get the event back with JSON - self._connect_as_user() - self.dv.load_events() - elist = [e for e in self.dv.events if e['c_name'] == gid] - #- MUST have this event -- only once - self.assertEquals(len(elist), 1) - strdate = "%d-%.02d-%.02d" % time.gmtime(elist[0]['c_startdate'])[0:3] - self.assertEquals(strdate, event['startDate']) - #- Get the event back with DAV - dav = self._get_dav_data(gid, username, password) - self.assertEquals(self._get_dav_field(dav, 'SUMMARY:'), event['summary']) - - -class JsonDavPhoneTests(unittest.TestCase): - - def setUp(self): - self._connect_as_user() - self.newphone = [{'type': 'home', 'value': '123.456.7890'}] - self.newphones_difftype = [{'type': 'home', 'value': '123.456.7890'}, - {'type': 'work', 'value': '987.654.3210'}, - {'type': 'fax', 'value': '555.666.7777'}] - self.newphones_sametype = [{'type': 'work', 'value': '123.456.7890'}, - {'type': 'work', 'value': '987.654.3210'}] - # Easier to erase them all in tearDown - self.allphones = list(self.newphone) - self.allphones.extend(self.newphones_difftype) - self.allphones.extend(self.newphones_sametype) - #- In case there are no cards for this user - try: - self._get_card() - except IndexError: - path = 'Contacts/personal' - (card, path, gid) = self._create_new_card(path) - self._save_card(card) - - def tearDown(self): - self._connect_as_user() - self._get_card() - #- Remove the phones we just added - for phone in self.allphones: - try: - self.card['phones'].pop(self.card['phones'].index(phone)) - except ValueError: - #print "Can't find", phone - pass - self._save_card() - - - def _connect_as_user(self, newuser=username, newpassword=password): - self.dv = carddav.Carddav(newuser, newpassword) - - def _create_new_card(self, path): - gid = self.dv.newguid(path) - card = {'c_categories': None, - 'c_cn': 'John Doe', - 'c_component': 'vcard', - 'c_givenname': 'John Doe', - 'c_mail': 'johndoe@nothere.com', - 'c_name': gid, - 'c_o': '', - 'c_screenname': '', - 'c_sn': '', - 'c_telephonenumber': '123.456.7890', - 'emails': [{'type': 'pref', 'value': 'johndoe@nothere.com'}], - 'phones': [{'type': 'home', 'value': '111.222.3333'}], - 'id': gid} - return (card, path, gid) - - def _get_card(self, name="John Doe"): - tmp_card = self.dv.get_cards(name)[0] - self.card = self.dv.get_card(tmp_card['c_name']) - - def _save_card(self, card=None): - if card: - self.dv.save_card(card) - else: - self.dv.save_card(self.card) - - def _get_dav_data(self, filename, user=username, passwd=password): - w = webdavlib.WebDAVClient(hostname, port, user, passwd) - query = webdavlib.HTTPGET("http://localhost/SOGo/dav/%s/Contacts/personal/%s" % (username, filename)) - w.execute(query) - self.assertEquals(query.response['status'], 200) - return query.response['body'].split("\r\n") - - def _phone_to_dav_str(self, phonedict): - return "TEL;TYPE=%s:%s" % (phonedict['type'], phonedict['value']) - - def _testMultiplePhones(self, phones): - """ Add Multiple Phones to Contact JSON and verify with DAV """ - #- Use JSON to get CARD and add a phone and save it back - self._get_card() - oldphones = self.card['phones'] - oldphones.extend(phones) - self._save_card() - #- Make sure that the phone is there when using JSON - self._connect_as_user() - self._get_card() - #print "C:::", self.card - testphones = self.card['phones'] - #print "P1:", oldphones - #print "P2:", testphones - self.assertEquals(sorted(oldphones), sorted(testphones)) - #- Verify that DAV has the same values - dav = self._get_dav_data(self.card['id'], username, password) - for phone in phones: - found = dav.index(self._phone_to_dav_str(phone)) - self.assertTrue(found > 0) - - def testSinglePhone(self): - self._testMultiplePhones(self.newphone) - - def testMultipleDifferentPhones(self): - self._testMultiplePhones(self.newphones_difftype) - - def testMultipleSameTypePhones(self): - self._testMultiplePhones(self.newphones_sametype) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-config.py b/Tests/Integration/test-config.py deleted file mode 100755 index c76af88b2..000000000 --- a/Tests/Integration/test-config.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password, mailserver, subscriber_username, attendee1, attendee1_delegate - -import sogotests -import unittest -import time - -class CalDAVITIPDelegationTest(unittest.TestCase): - def testConfigPY(self): - """ config.py validation """ - try: - test = hostname - except: - self.fail("'hostname' is not defined") - - try: - test = username - except: - self.fail("'username' is not defined") - - try: - test = subscriber_username - except: - self.fail("'subscriber_username' is not defined") - - try: - test = attendee1 - except: - self.fail("'attendee1' is not defined") - - try: - test = attendee1_delegate - except: - self.fail("'attendee1_delegate' is not defined") - - self.assertEquals(subscriber_username, attendee1, - "'subscriber_username' and 'attendee1'" - + " must be the same user") - - try: - test = mailserver - except: - self.fail("'mailserver' is not defined") - - userHash = {} - userList = [ username, subscriber_username, attendee1_delegate ] - for user in userList: - self.assertFalse(userHash.has_key(user), - "username, attendee1, attendee1_delegate must" - + " all be different users ('%s')" - % user) - userHash[user] = True - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-contact-categories.py b/Tests/Integration/test-contact-categories.py deleted file mode 100755 index df5734243..000000000 --- a/Tests/Integration/test-contact-categories.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/python - -import sogotests -import unittest -import webdavlib - -from config import * - -class HTTPContactCategoriesTest(unittest.TestCase): - def _setCategories(self, user, categories = None): - resource = '/SOGo/dav/%s/Contacts/' % user - if categories is None: - categories = [] - elements = [ { "{urn:inverse:params:xml:ns:inverse-dav}category": x } - for x in categories ] - props = { "{urn:inverse:params:xml:ns:inverse-dav}contacts-categories": elements } - proppatch = webdavlib.WebDAVPROPPATCH(resource, props) - client = webdavlib.WebDAVClient(hostname, port, username, password) - client.execute(proppatch) - self.assertEquals(proppatch.response["status"], 207, - "failure (%s) setting '%s' categories on %s's contacts" - % (proppatch.response["status"], - "', '".join(categories), user)) - - def _getCategories(self, user): - resource = '/SOGo/dav/%s/Contacts/' % user - props = [ "{urn:inverse:params:xml:ns:inverse-dav}contacts-categories" ] - propfind = webdavlib.WebDAVPROPFIND(resource, props, "0") - client = webdavlib.WebDAVClient(hostname, port, username, password) - client.execute(propfind) - self.assertEquals(propfind.response["status"], 207, - "failure (%s) getting categories on %s's contacts" - % (propfind.response["status"], user)) - - categories = [] - prop_nodes = propfind.response["document"].findall("{DAV:}response/{DAV:}propstat/{DAV:}prop/{urn:inverse:params:xml:ns:inverse-dav}contacts-categories") - for prop_node in prop_nodes: - cat_nodes = prop_node.findall("{urn:inverse:params:xml:ns:inverse-dav}category") - if cat_nodes is not None: - for cat_node in cat_nodes: - categories.append(cat_node.text) - - return categories - - - def test(self): - self._setCategories(username, []) - cats = self._getCategories(username) - self.assertTrue(cats is not None and len(cats) == 0) - - self._setCategories(username, [ "Coucou" ]) - cats = self._getCategories(username) - self.assertTrue(cats is not None and len(cats) == 1) - self.assertEquals(cats[0], "Coucou") - - self._setCategories(username, [ "Toto", "Cuicui" ]) - cats = self._getCategories(username) - self.assertTrue(cats is not None and len(cats) == 2) - self.assertEquals(cats[0], "Toto") - self.assertEquals(cats[1], "Cuicui") - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-davacl.py b/Tests/Integration/test-davacl.py deleted file mode 100755 index b15222ad2..000000000 --- a/Tests/Integration/test-davacl.py +++ /dev/null @@ -1,1130 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password, subscriber_username, subscriber_password, \ - superuser, superuser_password - -import sys -import unittest -import webdavlib -import time - -import sogotests -import utilities - -# TODO: -# - cal: complete test for "modify": "respond to" causes a 204 but no actual -# modification should occur -# - ab: testcase for addressbook-query, webdav-sync (no "calendar-data" -# equivalent) -# - cal: testcase for "calendar-query" -# - test rights validity: -# - send invalid rights to SOGo and expect failures -# - refetch the set of rights and make sure it matches what was set -# originally -# - test "current-user-acl-set" - -class DAVCalendarSuperUserAclTest(unittest.TestCase): - def __init__(self, arg): - self.client = webdavlib.WebDAVClient(hostname, port, - superuser, superuser_password) - self.resource = "/SOGo/dav/%s/Calendar/test-dav-superuser-acl/" % subscriber_username - self.filename = "suevent.ics" - self.url = "%s%s" % (self.resource, self.filename) - - unittest.TestCase.__init__(self, arg) - - def setUp(self): - delete = webdavlib.WebDAVDELETE(self.resource) - self.client.execute(delete) - mkcol = webdavlib.WebDAVMKCOL(self.resource) - self.client.execute(mkcol) - self.assertEquals(mkcol.response["status"], 201, - "preparation: failure creating collection" - "(code = %d)" % mkcol.response["status"]) - - def tearDown(self): - delete = webdavlib.WebDAVDELETE(self.resource) - self.client.execute(delete) - - def _getEvent(self): - get = webdavlib.HTTPGET(self.url) - self.client.execute(get) - - if get.response["status"] == 200: - event = get.response["body"].replace("\r", "") - else: - event = None - - return event - - def _calendarDataInMultistatus(self, query, response_tag = "{DAV:}response"): - event = None - - # print "\n\n\n%s\n\n" % query.response["body"] - # print "\n\n" - response_nodes = query.response["document"].findall(response_tag) - for response_node in response_nodes: - href_node = response_node.find("{DAV:}href") - href = href_node.text - if href.endswith(self.filename): - propstat_node = response_node.find("{DAV:}propstat") - if propstat_node is not None: - status_node = propstat_node.find("{DAV:}status") - status = status_node.text - if status.endswith("200 OK"): - data_node = propstat_node.find("{DAV:}prop/{urn:ietf:params:xml:ns:caldav}calendar-data") - event = data_node.text - elif not (status.endswith("404 Resource Not Found") - or status.endswith("404 Not Found")): - self.fail("%s: unexpected status code: '%s'" - % (self.filename, status)) - - return event - - def _propfindEvent(self): - propfind = webdavlib.WebDAVPROPFIND(self.resource, - ["{urn:ietf:params:xml:ns:caldav}calendar-data"], - 1) - self.client.execute(propfind) - if propfind.response["status"] != 404: - event = self._calendarDataInMultistatus(propfind) - - return event - - def _multigetEvent(self): - event = None - - multiget = webdavlib.CalDAVCalendarMultiget(self.resource, - ["{urn:ietf:params:xml:ns:caldav}calendar-data"], - [ self.url ]) - self.client.execute(multiget) - if multiget.response["status"] != 404: - event = self._calendarDataInMultistatus(multiget) - - return event - - def _webdavSyncEvent(self): - event = None - - sync_query = webdavlib.WebDAVSyncQuery(self.resource, None, - ["{urn:ietf:params:xml:ns:caldav}calendar-data"]) - self.client.execute(sync_query) - if sync_query.response["status"] != 404: - event = self._calendarDataInMultistatus(sync_query, "{DAV:}response") - - return event - - def testSUAccess(self): - """create, read, modify, delete for superuser""" - event = event_template % { "class": "PUBLIC", - "filename": self.filename, - "organizer_line": "", - "attendee_line": "" } - - # 1. Create - put = webdavlib.HTTPPUT(self.url, event) - put.content_type = "text/calendar; charset=utf-8" - self.client.execute(put) - self.assertEquals(put.response["status"], 201, - "%s: event creation/modification:" - " expected status code '201' (received '%d')" - % (self.filename, put.response["status"])) - - # 2. Read - readEvent = self._getEvent() - self.assertEquals(readEvent, event, - "GET: returned event does not match") - readEvent = self._propfindEvent() - self.assertEquals(readEvent, event, - "PROPFIND: returned event does not match") - readEvent = self._multigetEvent() - self.assertEquals(readEvent, event, - "MULTIGET: returned event does not match") - readEvent = self._webdavSyncEvent() - self.assertEquals(readEvent, event, - "WEBDAV-SYNC: returned event does not match") - - # 3. Modify - for eventClass in [ "CONFIDENTIAL", "PRIVATE", "PUBLIC" ]: - event = event_template % { "class": eventClass, - "filename": self.filename, - "organizer_line": "", - "attendee_line": "" } - put = webdavlib.HTTPPUT(self.url, event) - put.content_type = "text/calendar; charset=utf-8" - self.client.execute(put) - self.assertEquals(put.response["status"], 204, - "%s: event modification failed" - " expected status code '204' (received '%d')" - % (self.filename, put.response["status"])) - - # 4. Delete - delete = webdavlib.WebDAVDELETE(self.url) - self.client.execute(delete) - self.assertEquals(delete.response["status"], 204, - "%s: event deletion failed" - " expected status code '204' (received '%d')" - % (self.filename, put.response["status"])) - -class DAVAclTest(unittest.TestCase): - resource = None - - def __init__(self, arg): - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - unittest.TestCase.__init__(self, arg) - - def setUp(self): - delete = webdavlib.WebDAVDELETE(self.resource) - self.client.execute(delete) - mkcol = webdavlib.WebDAVMKCOL(self.resource) - self.client.execute(mkcol) - self.assertEquals(mkcol.response["status"], 201, - "preparation: failure creating collection" - "(code = %d)" % mkcol.response["status"]) - self.subscriber_client = webdavlib.WebDAVClient(hostname, port, - subscriber_username, - subscriber_password) - - def tearDown(self): - delete = webdavlib.WebDAVDELETE(self.resource) - self.client.execute(delete) - - def _versitLine(self, previous_key, line): - if line[:1] == ' ' and previous_key: - key, value = (previous_key, line[1:]) - else: - key, value = line.split(":") - semicolon = key.find(";") - if semicolon > -1: - key = key[:semicolon] - - return (key, value) - - def versitDict(self, event): - versitStruct = {} - key = '' - for line in event.splitlines(): - (key, value) = self._versitLine(key, line) - if not (key == "BEGIN" or key == "UID" or key == "END"): - if key in versitStruct: - versitStruct[key] += value - else: - versitStruct[key] = value - - return versitStruct - -event_template = """BEGIN:VCALENDAR -PRODID:-//Inverse//Event Generator//EN -VERSION:2.0 -BEGIN:VEVENT -SEQUENCE:0 -TRANSP:OPAQUE -UID:12345-%(class)s-%(filename)s -SUMMARY:%(class)s event (orig. title) -DTSTART:20090805T100000Z -DTEND:20090805T140000Z -CLASS:%(class)s -DESCRIPTION:%(class)s description -LOCATION:location -%(organizer_line)s%(attendee_line)sCREATED:20090805T100000Z -DTSTAMP:20090805T100000Z -END:VEVENT -END:VCALENDAR""" - -task_template = """BEGIN:VCALENDAR -PRODID:-//Inverse//Event Generator//EN -VERSION:2.0 -BEGIN:VTODO -CREATED:20100122T201440Z -LAST-MODIFIED:20100201T175246Z -DTSTAMP:20100201T175246Z -UID:12345-%(class)s-%(filename)s -SUMMARY:%(class)s event (orig. title) -CLASS:%(class)s -DESCRIPTION:%(class)s description -STATUS:IN-PROCESS -PERCENT-COMPLETE:0 -END:VTODO -END:VCALENDAR""" - -class DAVCalendarAclTest(DAVAclTest): - resource = '/SOGo/dav/%s/Calendar/test-dav-acl/' % username - user_email = None - - def __init__(self, arg): - DAVAclTest.__init__(self, arg) - self.acl_utility = utilities.TestCalendarACLUtility(self, - self.client, - self.resource) - - def setUp(self): - DAVAclTest.setUp(self) - self.user_email = self.acl_utility.fetchUserInfo(username)[1] - self.classToICSClass = { "pu": "PUBLIC", - "pr": "PRIVATE", - "co": "CONFIDENTIAL" } - self._putEvent(self.client, "public-event.ics", "PUBLIC") - self._putEvent(self.client, "private-event.ics", "PRIVATE") - self._putEvent(self.client, "confidential-event.ics", "CONFIDENTIAL") - self._putTask(self.client, "public-task.ics", "PUBLIC") - self._putTask(self.client, "private-task.ics", "PRIVATE") - self._putTask(self.client, "confidential-task.ics", "CONFIDENTIAL") - - def testViewAllPublic(self): - """'view all' on a specific class (PUBLIC)""" - self._testRights({ "pu": "v" }) - - def testModifyPublicViewAllPrivateViewDConfidential(self): - """'modify' PUBLIC, 'view all' PRIVATE, 'view d&t' confidential""" - self._testRights({ "pu": "m", "pr": "v", "co": "d" }) - - def testCreateOnly(self): - """'create' only""" - self._testRights({ "c": True }) - - def testDeleteOnly(self): - """'delete' only""" - self._testRights({ "d": True }) - - def testCreateDeleteModifyPublicViewAllPrivateViewDConfidential(self): - """'create', 'delete', 'view d&t' PUBLIC, 'modify' PRIVATE""" - self._testRights({ "c": True, "d": True, "pu": "d", "pr": "m" }) - - def testCreateRespondToPublic(self): - """'create', 'respond to' PUBLIC""" - self._testRights({ "c": True, "pu": "r" }) - - def testNothing(self): - """no right given""" - self._testRights({}) - - def _putEvent(self, client, filename, - event_class = "PUBLIC", - exp_status = 201, - organizer = None, attendee = None, - partstat = "NEEDS-ACTION"): - url = "%s%s" % (self.resource, filename) - if organizer is not None: - organizer_line = "ORGANIZER:%s\n" % organizer - else: - organizer_line = "" - if attendee is not None: - attendee_line = "ATTENDEE;PARTSTAT=%s:%s\n" % (partstat, attendee) - else: - attendee_line = "" - event = event_template % { "class": event_class, - "filename": filename, - "organizer_line": organizer_line, - "attendee_line": attendee_line } - put = webdavlib.HTTPPUT(url, event) - put.content_type = "text/calendar; charset=utf-8" - client.execute(put) - self.assertEquals(put.response["status"], exp_status, - "%s: event creation/modification:" - " expected status code '%d' (received '%d')" - % (filename, exp_status, put.response["status"])) - - def _putTask(self, client, filename, - task_class = "PUBLIC", - exp_status = 201): - url = "%s%s" % (self.resource, filename) - task = task_template % { "class": task_class, - "filename": filename } - put = webdavlib.HTTPPUT(url, task) - put.content_type = "text/calendar; charset=utf-8" - client.execute(put) - self.assertEquals(put.response["status"], exp_status, - "%s: task creation/modification:" - " expected status code '%d' (received '%d')" - % (filename, exp_status, put.response["status"])) - - def _deleteEvent(self, client, filename, exp_status = 204): - url = "%s%s" % (self.resource, filename) - delete = webdavlib.WebDAVDELETE(url) - client.execute(delete) - self.assertEquals(delete.response["status"], exp_status, - "%s: event deletion: expected status code '%d'" - " (received '%d')" - % (filename, exp_status, delete.response["status"])) - - def _currentUserPrivilegeSet(self, resource, expStatus = 207): - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}current-user-privilege-set"], - 0) - self.subscriber_client.execute(propfind) - self.assertEquals(propfind.response["status"], expStatus, - "unexected status code when reading privileges:" - + " %s instead of %d" - % (propfind.response["status"], expStatus)) - - privileges = [] - if expStatus < 300: - response_nodes = propfind.response["document"].findall("{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}current-user-privilege-set/{DAV:}privilege") - for node in response_nodes: - privileges.extend([x.tag for x in node.getchildren()]) - - return privileges - - def _comparePrivilegeSets(self, expectedPrivileges, privileges): - testHash = dict(map(lambda x: (x, True), privileges)) - for privilege in expectedPrivileges: - self.assertTrue(testHash.has_key(privilege), - "expected privilege '%s' not found" % privilege) - testHash = dict(map(lambda x: (x, True), expectedPrivileges)) - for privilege in privileges: - self.assertTrue(testHash.has_key(privilege), - "excessive privilege '%s' found" % privilege) - - def _testCollectionDAVAcl(self, rights): - if len(rights) > 0: - expectedPrivileges = ['{DAV:}read', - '{DAV:}read-current-user-privilege-set', - '{urn:ietf:params:xml:ns:caldav}read-free-busy'] - else: - expectedPrivileges = [] - if rights.has_key("c"): - extraPrivileges = ["{DAV:}bind", - "{DAV:}write-content", - '{urn:ietf:params:xml:ns:caldav}schedule', - '{urn:ietf:params:xml:ns:caldav}schedule-post', - '{urn:ietf:params:xml:ns:caldav}schedule-post-vevent', - '{urn:ietf:params:xml:ns:caldav}schedule-post-vtodo', - '{urn:ietf:params:xml:ns:caldav}schedule-post-vjournal', - '{urn:ietf:params:xml:ns:caldav}schedule-post-vfreebusy', - '{urn:ietf:params:xml:ns:caldav}schedule-deliver', - '{urn:ietf:params:xml:ns:caldav}schedule-deliver-vevent', - '{urn:ietf:params:xml:ns:caldav}schedule-deliver-vtodo', - '{urn:ietf:params:xml:ns:caldav}schedule-deliver-vjournal', - '{urn:ietf:params:xml:ns:caldav}schedule-deliver-vfreebusy', - '{urn:ietf:params:xml:ns:caldav}schedule-respond', - '{urn:ietf:params:xml:ns:caldav}schedule-respond-vevent', - '{urn:ietf:params:xml:ns:caldav}schedule-respond-vtodo'] - expectedPrivileges.extend(extraPrivileges) - if rights.has_key("d"): - expectedPrivileges.append("{DAV:}unbind") - if len(expectedPrivileges) == 0: - expStatus = 404 - else: - expStatus = 207 - privileges = self._currentUserPrivilegeSet(self.resource, expStatus) - - # When comparing privileges on DAV collection, we remove all 'default' - # privileges on the collection. - privileges_set = set(privileges); - for x in ("public", "private", "confidential"): - privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}viewwhole-%s-records" % x) - privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}viewdant-%s-records" % x) - privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}modify-%s-records" % x) - privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}respondto-%s-records" %x) - - self._comparePrivilegeSets(expectedPrivileges, list(privileges_set)) - - def _testEventDAVAcl(self, event_class, right, error_code): - icsClass = self.classToICSClass[event_class].lower() - for suffix in [ "event", "task" ]: - url = "%s%s-%s.ics" % (self.resource, icsClass, suffix) - - if right is None: - expStatus = error_code - expectedPrivileges = None - else: - expStatus = 207 - expectedPrivileges = ['{DAV:}read-current-user-privilege-set', - '{urn:inverse:params:xml:ns:inverse-dav}view-date-and-time', - '{DAV:}read'] - if right != "d": - extraPrivilege = '{urn:inverse:params:xml:ns:inverse-dav}view-whole-component' - expectedPrivileges.append(extraPrivilege) - if right != "v": - extraPrivileges = ['{urn:inverse:params:xml:ns:inverse-dav}respond-to-component', - '{DAV:}write-content'] - expectedPrivileges.extend(extraPrivileges) - if right != "r": - extraPrivileges = ['{DAV:}write-properties', - '{DAV:}write'] - expectedPrivileges.extend(extraPrivileges) - - privileges = self._currentUserPrivilegeSet(url, expStatus) - if expStatus != error_code: - self._comparePrivilegeSets(expectedPrivileges, privileges) - - def _testRights(self, rights): - self.acl_utility.setupRights(subscriber_username, rights) - self._testCreate(rights) - self._testCollectionDAVAcl(rights) - self._testEventRight("pu", rights) - self._testEventRight("pr", rights) - self._testEventRight("co", rights) - self._testDelete(rights) - - def _testCreate(self, rights): - if rights.has_key("c") and rights["c"]: - exp_code = 201 - elif len(rights) == 0: - exp_code = 404 - else: - exp_code = 403 - self._putEvent(self.subscriber_client, "creation-test.ics", "PUBLIC", - exp_code) - - def _testDelete(self, rights): - if rights.has_key("d") and rights["d"]: - exp_code = 204 - elif len(rights) == 0: - exp_code = 404 - else: - exp_code = 403 - self._deleteEvent(self.subscriber_client, "public-event.ics", - exp_code) - self._deleteEvent(self.subscriber_client, "private-event.ics", - exp_code) - self._deleteEvent(self.subscriber_client, "confidential-event.ics", - exp_code) - - def _testEventRight(self, event_class, rights): - if rights.has_key(event_class): - right = rights[event_class] - else: - right = None - - event = self._getEvent(event_class) - self._checkViewEventRight("GET", event, event_class, right) - event = self._propfindEvent(event_class) - self._checkViewEventRight("PROPFIND", event, event_class, right) - event = self._multigetEvent(event_class) - self._checkViewEventRight("multiget", event, event_class, right) - event = self._webdavSyncEvent(event_class) - self._checkViewEventRight("webdav-sync", event, event_class, right) - - if len(rights) > 0: - error_code = 403 - else: - error_code = 404 - self._testModify(event_class, right, error_code) - self._testRespondTo(event_class, right, error_code) - self._testEventDAVAcl(event_class, right, error_code) - - def _getEvent(self, event_class, is_invitation = False): - icsClass = self.classToICSClass[event_class] - if is_invitation: - filename = "invitation-%s" % icsClass.lower() - else: - filename = "%s" % icsClass.lower() - url = "%s%s-event.ics" % (self.resource, filename) - get = webdavlib.HTTPGET(url) - self.subscriber_client.execute(get) - - if get.response["status"] == 200: - event = get.response["body"].replace("\r", "") - else: - event = None - - return event - - def _getTask(self, task_class): - filename = "%s" % self.classToICSClass[task_class].lower() - url = "%s%s-task.ics" % (self.resource, filename) - get = webdavlib.HTTPGET(url) - self.subscriber_client.execute(get) - - if get.response["status"] == 200: - task = get.response["body"] - else: - task = None - - return task - - def _calendarDataInMultistatus(self, query, filename, - response_tag = "{DAV:}response"): - event = None - - # print "\n\n\n%s\n\n" % query.response["body"] - # print "\n\n" - response_nodes = query.response["document"].findall("%s" % response_tag) - for response_node in response_nodes: - href_node = response_node.find("{DAV:}href") - href = href_node.text - if href.endswith(filename): - propstat_node = response_node.find("{DAV:}propstat") - if propstat_node is not None: - status_node = propstat_node.find("{DAV:}status") - status = status_node.text - if status.endswith("200 OK"): - data_node = propstat_node.find("{DAV:}prop/{urn:ietf:params:xml:ns:caldav}calendar-data") - event = data_node.text - elif not (status.endswith("404 Resource Not Found") - or status.endswith("404 Not Found")): - self.fail("%s: unexpected status code: '%s'" - % (filename, status)) - - return event - - def _propfindEvent(self, event_class): - event = None - - icsClass = self.classToICSClass[event_class] - filename = "%s-event.ics" % icsClass.lower() - propfind = webdavlib.WebDAVPROPFIND(self.resource, - ["{urn:ietf:params:xml:ns:caldav}calendar-data"], - 1) - self.subscriber_client.execute(propfind) - if propfind.response["status"] != 404: - event = self._calendarDataInMultistatus(propfind, filename) - - return event - - def _multigetEvent(self, event_class): - event = None - - icsClass = self.classToICSClass[event_class] - url = "%s%s-event.ics" % (self.resource, icsClass.lower()) - multiget = webdavlib.CalDAVCalendarMultiget(self.resource, - ["{urn:ietf:params:xml:ns:caldav}calendar-data"], - [ url ]) - self.subscriber_client.execute(multiget) - if multiget.response["status"] != 404: - event = self._calendarDataInMultistatus(multiget, url) - - return event - - def _webdavSyncEvent(self, event_class): - event = None - - icsClass = self.classToICSClass[event_class] - url = "%s%s-event.ics" % (self.resource, icsClass.lower()) - sync_query = webdavlib.WebDAVSyncQuery(self.resource, None, - ["{urn:ietf:params:xml:ns:caldav}calendar-data"]) - self.subscriber_client.execute(sync_query) - if sync_query.response["status"] != 404: - event = self._calendarDataInMultistatus(sync_query, url, - "{DAV:}response") - - return event - - def _checkViewEventRight(self, operation, event, event_class, right): - if right is None: - self.assertEquals(event, None, - "None right expecting event invisibility for" - " operation '%s'" % operation) - else: - self.assertTrue(event is not None, - "no event returned during operation '%s'" - " (right: %s)" % (operation, right)) - if right == "v" or right == "r" or right == "m": - icsClass = self.classToICSClass[event_class] - complete_event = (event_template % {"class": icsClass, - "filename": "%s-event.ics" % icsClass.lower(), - "organizer_line": "", - "attendee_line": ""}) - self.assertTrue(event.strip() == complete_event.strip(), - "Right '%s' should return complete event" - " during operation '%s'" - % (right, operation)) - elif right == "d": - self._testEventIsSecureVersion(event_class, event) - else: - self.fail("Right '%s' is not supported" % right) - - def _testEventIsSecureVersion(self, event_class, event): - icsClass = self.classToICSClass[event_class] - expected_dict = { "VERSION": "2.0", - "PRODID": "-//Inverse//Event Generator//EN", - "SUMMARY": "(%s event)" % icsClass.capitalize(), - "DTSTART": "20090805T100000Z", - "DTEND": "20090805T140000Z", - "DTSTAMP": "20090805T100000Z", - "X-SOGO-SECURE": "YES" } - event_dict = self.versitDict(event) - for key in event_dict.keys(): - self.assertTrue(expected_dict.has_key(key), - "key '%s' of secure event not expected" % key) - self.assertTrue(expected_dict[key] == event_dict[key] - or key == "SUMMARY", - "value for key '%s' of secure does not match" - " (exp: '%s', obtained: '%s'" - % (key, expected_dict[key], event_dict[key] )) - - for key in expected_dict.keys(): - self.assertTrue(event_dict.has_key(key), - "expected key '%s' not found in secure event" - % key) - - def _testModify(self, event_class, right, error_code): - if right == "m" or right == "r": - exp_code = 204 - else: - exp_code = error_code - icsClass = self.classToICSClass[event_class] - filename = "%s-event.ics" % icsClass.lower() - self._putEvent(self.subscriber_client, filename, icsClass, - exp_code) - - def _testRespondTo(self, event_class, right, error_code): - icsClass = self.classToICSClass[event_class] - filename = "invitation-%s-event.ics" % icsClass.lower() - self._putEvent(self.client, filename, icsClass, - 201, - "mailto:nobody@somewhere.com", self.user_email, - "NEEDS-ACTION") - - if right == "m" or right == "r": - exp_code = 204 - else: - exp_code = error_code - - # here we only do 'passive' validation: if a user has a "respond to" - # right, only the attendee entry will me modified. The change of - # organizer must thus be silently ignored below. - self._putEvent(self.subscriber_client, filename, icsClass, - exp_code, "mailto:someone@nowhere.com", self.user_email, - "ACCEPTED") - if exp_code == 204: - att_line = "ATTENDEE;PARTSTAT=ACCEPTED:%s\n" % self.user_email - if right == "r": - exp_event = event_template % {"class": icsClass, - "filename": filename, - "organizer_line": "ORGANIZER;CN=nobody@somewhere.com:mailto:nobody@somewhere.com\n", - "attendee_line": att_line} - else: - exp_event = event_template % {"class": icsClass, - "filename": filename, - "organizer_line": "ORGANIZER;CN=someone@nowhere.com:mailto:someone@nowhere.com\n", - "attendee_line": att_line} - event = self._getEvent(event_class, True) - ics_diff = utilities.ics_compare(exp_event, event) - self.assertTrue(ics_diff.areEqual(), - "'respond to' event does not match:\n" - "Diff(expected, got):\n %s" % ics_diff.textDiff()) - -class DAVAddressBookAclTest(DAVAclTest): - resource = '/SOGo/dav/%s/Contacts/test-dav-acl/' % username - cards = { "new.vcf": """BEGIN:VCARD -VERSION:3.0 -PRODID:-//Inverse//Card Generator//EN -UID:NEWTESTCARD -N:New;Carte -FN:Carte 'new' -ORG:societe;service -NICKNAME:surnom -ADR;TYPE=work:adr2 societe;;adr societe;ville societe;etat soc;code soc;pays soc -ADR;TYPE=home:rue perso 2;;rue perso;ville perso;etat perso;code post perso;pays perso -TEL;TYPE=work:+1 514 123-3372 -TEL;TYPE=home:tel dom -TEL;TYPE=cell:portable -TEL;TYPE=fax:fax -TEL;TYPE=pager:pager -X-MOZILLA-HTML:FALSE -EMAIL;TYPE=work:address.email@domaine.ca -EMAIL;TYPE=home:address.email@domaine2.com -URL;TYPE=home:web perso -TITLE:fonction -URL;TYPE=work:page soc -CUSTOM1:divers1 -CUSTOM2:divers2 -CUSTOM3:divers3 -CUSTOM4:divers4 -NOTE:Remarque -X-AIM:pseudo aim -END:VCARD""", - "old.vcf": """BEGIN:VCARD -VERSION:3.0 -PRODID:-//Inverse//Card Generator//EN -UID:NEWTESTCARD -N:Old;Carte -FN:Carte 'old' -ORG:societe;service -NICKNAME:surnom -ADR;TYPE=work:adr2 societe;;adr societe;ville societe;etat soc;code soc;pays soc -ADR;TYPE=home:rue perso 2;;rue perso;ville perso;etat perso;code post perso;pays perso -TEL;TYPE=work:+1 514 123-3372 -TEL;TYPE=home:tel dom -TEL;TYPE=cell:portable -TEL;TYPE=fax:fax -TEL;TYPE=pager:pager -X-MOZILLA-HTML:FALSE -EMAIL;TYPE=work:address.email@domaine.ca -EMAIL;TYPE=home:address.email@domaine2.com -URL;TYPE=home:web perso -TITLE:fonction -URL;TYPE=work:page soc -CUSTOM1:divers1 -CUSTOM2:divers2 -CUSTOM3:divers3 -CUSTOM4:divers4 -NOTE:Remarque -X-AIM:pseudo aim -END:VCARD""", - "new-modified.vcf": """BEGIN:VCARD -VERSION:3.0 -PRODID:-//Inverse//Card Generator//EN -UID:NEWTESTCARD -N:New;Carte modifiee -FN:Carte modifiee 'new' -ORG:societe;service -NICKNAME:surnom -ADR;TYPE=work:adr2 societe;;adr societe;ville societe;etat soc;code soc;pays soc -ADR;TYPE=home:rue perso 2;;rue perso;ville perso;etat perso;code post perso;pays perso -TEL;TYPE=work:+1 514 123-3372 -TEL;TYPE=home:tel dom -TEL;TYPE=cell:portable -TEL;TYPE=fax:fax -TEL;TYPE=pager:pager -X-MOZILLA-HTML:FALSE -EMAIL;TYPE=work:address.email@domaine.ca -EMAIL;TYPE=home:address.email@domaine2.com -URL;TYPE=home:web perso -TITLE:fonction -URL;TYPE=work:page soc -CUSTOM1:divers1 -CUSTOM2:divers2 -CUSTOM3:divers3 -CUSTOM4:divers4 -NOTE:Remarque -X-AIM:pseudo aim -END:VCARD""", - "old-modified.vcf": """BEGIN:VCARD -VERSION:3.0 -PRODID:-//Inverse//Card Generator//EN -UID:NEWTESTCARD -N:Old;Carte modifiee -FN:Carte modifiee 'old' -ORG:societe;service -NICKNAME:surnom -ADR;TYPE=work:adr2 societe;;adr societe;ville societe;etat soc;code soc;pays soc -ADR;TYPE=home:rue perso 2;;rue perso;ville perso;etat perso;code post perso;pays perso -TEL;TYPE=work:+1 514 123-3372 -TEL;TYPE=home:tel dom -TEL;TYPE=cell:portable -TEL;TYPE=fax:fax -TEL;TYPE=pager:pager -X-MOZILLA-HTML:FALSE -EMAIL;TYPE=work:address.email@domaine.ca -EMAIL;TYPE=home:address.email@domaine2.com -URL;TYPE=home:web perso -TITLE:fonction -URL;TYPE=work:page soc -CUSTOM1:divers1 -CUSTOM2:divers2 -CUSTOM3:divers3 -CUSTOM4:divers4 -NOTE:Remarque -X-AIM:pseudo aim -END:VCARD""" } - - def __init__(self, arg): - DAVAclTest.__init__(self, arg) - self.acl_utility = utilities.TestAddressBookACLUtility(self, - self.client, - self.resource) - - def setUp(self): - DAVAclTest.setUp(self) - self._putCard(self.client, "old.vcf", 201) - - def testView(self): - """'view' only""" - self._testRights({ "v": True }) - - def testEdit(self): - """'edit' only""" - self._testRights({ "e": True }) - - def testCreateOnly(self): - """'create' only""" - self._testRights({ "c": True }) - - def testDeleteOnly(self): - """'delete' only""" - self._testRights({ "d": True }) - - def testCreateDelete(self): - """'create', 'delete'""" - self._testRights({ "c": True, "d": True }) - - def testViewCreate(self): - """'view' and 'create'""" - self._testRights({ "c": True, "v": True }) - - def testViewDelete(self): - """'view' and 'delete'""" - self._testRights({ "d": True, "v": True }) - - def testEditCreate(self): - """'edit' and 'create'""" - self._testRights({ "c": True, "e": True }) - - def testEditDelete(self): - """'edit' and 'delete'""" - self._testRights({ "d": True, "e": True }) - - def _testRights(self, rights): - self.acl_utility.setupRights(subscriber_username, rights) - self._testCreate(rights) - self._testView(rights) - self._testEdit(rights) - self._testDelete(rights) - - def _putCard(self, client, filename, exp_status, real_card = None): - url = "%s%s" % (self.resource, filename) - if real_card is None: - real_card = filename - card = self.cards[real_card] - put = webdavlib.HTTPPUT(url, card) - put.content_type = "text/x-vcard; charset=utf-8" - client.execute(put) - self.assertEquals(put.response["status"], exp_status, - "%s: card creation/modification:" - " expected status code '%d' (received '%d')" - % (filename, exp_status, put.response["status"])) - - def _getCard(self, client, filename, exp_status): - url = "%s%s" % (self.resource, filename) - get = webdavlib.HTTPGET(url) - client.execute(get) - self.assertEquals(get.response["status"], exp_status, - "%s: card get:" - " expected status code '%d' (received '%d')" - % (filename, exp_status, get.response["status"])) - - def _deleteCard(self, client, filename, exp_status): - url = "%s%s" % (self.resource, filename) - delete = webdavlib.WebDAVDELETE(url) - client.execute(delete) - self.assertEquals(delete.response["status"], exp_status, - "%s: card deletion:" - " expected status code '%d' (received '%d')" - % (filename, exp_status, delete.response["status"])) - - def _testCreate(self, rights): - if rights.has_key("c") and rights["c"]: - exp_code = 201 - else: - exp_code = 403 - self._putCard(self.subscriber_client, "new.vcf", exp_code) - - def _testView(self, rights): - if ((rights.has_key("v") and rights["v"]) - or (rights.has_key("e") and rights["e"])): - exp_code = 200 - else: - exp_code = 403 - self._getCard(self.subscriber_client, "old.vcf", exp_code) - - def _testEdit(self, rights): - if rights.has_key("e") and rights["e"]: - exp_code = 204 - else: - exp_code = 403 - self._putCard(self.subscriber_client, "old.vcf", exp_code, "old-modified.vcf") - - def _testDelete(self, rights): - if rights.has_key("d") and rights["d"]: - exp_code = 204 - else: - exp_code = 403 - self._deleteCard(self.subscriber_client, "old.vcf", exp_code) - -class DAVPublicAccessTest(unittest.TestCase): - def setUp(self): - self.client = webdavlib.WebDAVClient(hostname, port) - self.anon_client = webdavlib.WebDAVClient(hostname, port) - self.dav_utility = utilities.TestUtility(self, self.client) - - def testPublicAccess(self): - resource = '/SOGo/so/public' - options = webdavlib.HTTPOPTIONS(resource) - self.anon_client.execute(options) - self.assertEquals(options.response["status"], 404, - "/SOGo/so/public is unexpectedly available") - - resource = '/SOGo/public' - options = webdavlib.HTTPOPTIONS(resource) - self.anon_client.execute(options) - self.assertEquals(options.response["status"], 404, - "/SOGo/public is unexpectedly available") - - resource = '/SOGo/dav/%s' % username - options = webdavlib.HTTPOPTIONS(resource) - self.anon_client.execute(options) - self.assertEquals(options.response["status"], 401, - "Non-public resources should request authentication") - - resource = '/SOGo/dav/public' - options = webdavlib.HTTPOPTIONS(resource) - self.anon_client.execute(options) - self.assertNotEquals(options.response["status"], 401, - "Non-public resources must NOT request authentication") - self.assertEquals(options.response["status"], 200, - "/SOGo/dav/public is not available, check user defaults") - - -class DAVCalendarPublicAclTest(unittest.TestCase): - def setUp(self): - self.createdRsrc = None - self.superuser_client = webdavlib.WebDAVClient(hostname, port, - superuser, superuser_password) - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - self.subscriber_client = webdavlib.WebDAVClient(hostname, port, - subscriber_username, - subscriber_password) - self.anon_client = webdavlib.WebDAVClient(hostname, port) - - def tearDown(self): - if self.createdRsrc is not None: - delete = webdavlib.WebDAVDELETE(self.createdRsrc) - self.superuser_client.execute(delete) - - def testCollectionAccessNormalUser(self): - """normal user access to (non-)shared resource from su""" - - # 1. all rights removed - parentColl = '/SOGo/dav/%s/Calendar/' % username - self.createdRsrc = '%stest-dav-acl/' % parentColl - for rsrc in [ 'personal', 'test-dav-acl' ]: - resource = '%s%s/' % (parentColl, rsrc) - mkcol = webdavlib.WebDAVMKCOL(resource) - self.client.execute(mkcol) - acl_utility = utilities.TestCalendarACLUtility(self, - self.client, - resource) - acl_utility.setupRights("anonymous", {}) - acl_utility.setupRights(subscriber_username, {}) - acl_utility.setupRights("", {}) - - propfind = webdavlib.WebDAVPROPFIND(parentColl, [ "displayname" ], 1) - self.subscriber_client.execute(propfind) - hrefs = propfind.response["document"] \ - .findall("{DAV:}response/{DAV:}href") - - self.assertEquals(len(hrefs), 1, - "expected 1 href in response instead of %d" % len(hrefs)) - self.assertEquals(hrefs[0].text, parentColl, - "the href must be the 'Calendar' parent coll.") - - acl_utility = utilities.TestCalendarACLUtility(self, - self.client, - self.createdRsrc) - - # 2. creation right added - acl_utility.setupRights(subscriber_username, { "c": True }) - - self.subscriber_client.execute(propfind) - hrefs = propfind.response["document"].findall("{DAV:}response/{DAV:}href") - self.assertEquals(len(hrefs), 4, - "expected 4 hrefs in response, got %d: %s" - % (len(hrefs), ", ".join([ x.text for x in hrefs ]))) - self.assertEquals(hrefs[0].text, parentColl, - "the first href is not a 'Calendar' parent coll.") - - resourceHrefs = { resource: False, - "%s.xml" % resource[:-1]: False, - "%s.ics" % resource[:-1]: False } - for href in hrefs[1:]: - self.assertTrue(resourceHrefs.has_key(href.text), - "received unexpected href: %s" % href.text) - self.assertFalse(resourceHrefs[href.text], - "href was returned more than once: %s" % href.text) - resourceHrefs[href.text] = True - - acl_utility.setupRights(subscriber_username) - - # 3. creation right added for "default user" - # subscriber_username expected to have access, but not "anonymous" - acl_utility.setupRights("", { "c": True }) - - self.subscriber_client.execute(propfind) - hrefs = propfind.response["document"] \ - .findall("{DAV:}response/{DAV:}href") - - self.assertEquals(len(hrefs), 4, - "expected 4 hrefs in response, got %d: %s" - % (len(hrefs), ", ".join([ x.text for x in hrefs ]))) - self.assertEquals(hrefs[0].text, parentColl, - "the first href is not a 'Calendar' parent coll.") - resourceHrefs = { resource: False, - "%s.xml" % resource[:-1]: False, - "%s.ics" % resource[:-1]: False } - for href in hrefs[1:]: - self.assertTrue(resourceHrefs.has_key(href.text), - "received unexpected href: %s" % href.text) - self.assertFalse(resourceHrefs[href.text], - "href was returned more than once: %s" % href.text) - resourceHrefs[href.text] = True - - anonParentColl = '/SOGo/dav/public/%s/Calendar/' % username - anon_propfind = webdavlib.WebDAVPROPFIND(anonParentColl, - [ "displayname" ], 1) - - self.anon_client.execute(anon_propfind) - hrefs = anon_propfind.response["document"] \ - .findall("{DAV:}response/{DAV:}href") - self.assertEquals(len(hrefs), 1, "expected only 1 href in response") - self.assertEquals(hrefs[0].text, anonParentColl, - "the first href is not a 'Calendar' parent coll.") - - acl_utility.setupRights("", {}) - - # 4. creation right added for "anonymous" - # "anonymous" expected to have access, but not subscriber_username - acl_utility.setupRights("anonymous", { "c": True }) - - self.anon_client.execute(anon_propfind) - hrefs = anon_propfind.response["document"] \ - .findall("{DAV:}response/{DAV:}href") - - - self.assertEquals(len(hrefs), 4, - "expected 4 hrefs in response, got %d: %s" - % (len(hrefs), ", ".join([ x.text for x in hrefs ]))) - self.assertEquals(hrefs[0].text, anonParentColl, - "the first href is not a 'Calendar' parent coll.") - anonResource = '%stest-dav-acl/' % anonParentColl - resourceHrefs = { anonResource: False, - "%s.xml" % anonResource[:-1]: False, - "%s.ics" % anonResource[:-1]: False } - for href in hrefs[1:]: - self.assertTrue(resourceHrefs.has_key(href.text), - "received unexpected href: %s" % href.text) - self.assertFalse(resourceHrefs[href.text], - "href was returned more than once: %s" % href.text) - resourceHrefs[href.text] = True - - self.subscriber_client.execute(propfind) - hrefs = propfind.response["document"] \ - .findall("{DAV:}response/{DAV:}href") - self.assertEquals(len(hrefs), 1, "expected only 1 href in response") - self.assertEquals(hrefs[0].text, parentColl, - "the first href is not a 'Calendar' parent coll.") - - def testCollectionAccessSuperUser(self): - # super user accessing (non-)shared res from nu - - parentColl = '/SOGo/dav/%s/Calendar/' % subscriber_username - self.createdRsrc = '%stest-dav-acl/' % parentColl - for rsrc in [ 'personal', 'test-dav-acl' ]: - resource = '%s%s/' % (parentColl, rsrc) - mkcol = webdavlib.WebDAVMKCOL(resource) - self.superuser_client.execute(mkcol) - acl_utility = utilities.TestCalendarACLUtility(self, - self.subscriber_client, - resource) - acl_utility.setupRights(username, {}) - - propfind = webdavlib.WebDAVPROPFIND(parentColl, [ "displayname" ], 1) - self.subscriber_client.execute(propfind) - hrefs = [x.text \ - for x in propfind.response["document"] \ - .findall("{DAV:}response/{DAV:}href")] - self.assertTrue(len(hrefs) > 2, - "expected at least 3 hrefs in response") - self.assertEquals(hrefs[0], parentColl, - "the href must be the 'Calendar' parent coll.") - for rsrc in [ 'personal', 'test-dav-acl' ]: - resource = '%s%s/' % (parentColl, rsrc) - self.assertTrue(hrefs.index(resource) > -1, - "resource '%s' not returned" % resource) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-default-classification.py b/Tests/Integration/test-default-classification.py deleted file mode 100755 index 8994b6aff..000000000 --- a/Tests/Integration/test-default-classification.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/python - -import sogotests -import unittest -import webdavlib - -from config import * - -class HTTPDefaultClassificationTest(unittest.TestCase): - def _setClassification(self, user, component, classification = ""): - resource = '/SOGo/dav/%s/Calendar/' % user - props = { "{urn:inverse:params:xml:ns:inverse-dav}%s-default-classification" % component: classification } - proppatch = webdavlib.WebDAVPROPPATCH(resource, props) - client = webdavlib.WebDAVClient(hostname, port, username, password) - client.execute(proppatch) - - return (proppatch.response["status"] == 207); - - def _getClassification(self, user, component): - resource = '/SOGo/dav/%s/Calendar/' % user - property_name = "{urn:inverse:params:xml:ns:inverse-dav}%s-default-classification" % component - propfind = webdavlib.WebDAVPROPFIND(resource, [ property_name ], "0") - client = webdavlib.WebDAVClient(hostname, port, username, password) - client.execute(propfind) - classification = None - propstat_nodes = propfind.response["document"].findall("{DAV:}response/{DAV:}propstat") - for propstat_node in propstat_nodes: - status_nodes = propstat_node.findall("{DAV:}status") - if status_nodes[0].text.lower() == "http/1.1 200 ok": - property_nodes = propstat_node.findall("{DAV:}prop/%s" % property_name) - if len(property_nodes) > 0: - classification = property_nodes[0].text - - return classification - - def test(self): - self.assertFalse(self._setClassification(username, "123456", "PUBLIC"), - "expected failure when setting a classification with an invalid property") - self.assertFalse(self._setClassification(username, "events", ""), - "expected failure when setting an empty classification") - self.assertFalse(self._setClassification(username, "events", "pouet"), - "expected failure when setting an invalid classification") - for component in [ "events", "tasks" ]: - for classification in [ "PUBLIC", "PRIVATE", "CONFIDENTIAL" ]: - self.assertTrue(self._setClassification(username, component, classification), - "error when setting classification to '%s'" % classification) - fetched_class = self._getClassification(username, component) - self.assertTrue(classification == fetched_class, - "set and fetched classifications do not match (%s != %s)" % (classification, fetched_class)) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-ical.py b/Tests/Integration/test-ical.py deleted file mode 100755 index 6f128de7c..000000000 --- a/Tests/Integration/test-ical.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/python - -# FIXME: we should avoid using superuser if possible - -from config import hostname, port, username, password, subscriber_username, \ - superuser, superuser_password - -import unittest -import sogotests -import utilities -import webdavlib - -class iCalTest(unittest.TestCase): - def testPrincipalCollectionSet(self): - """principal-collection-set: 'DAV' header must be returned with iCal 4""" - client = webdavlib.WebDAVClient(hostname, port, username, password) - resource = '/SOGo/dav/%s/' % username - - # NOT iCal4 - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}principal-collection-set"], - 0) - client.execute(propfind) - self.assertEquals(propfind.response["status"], 207) - headers = propfind.response["headers"] - self.assertFalse(headers.has_key("dav"), - "DAV header must not be returned when user-agent is NOT iCal 4") - - # iCal4 - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}principal-collection-set"], - 0) - client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)" - client.execute(propfind) - self.assertEquals(propfind.response["status"], 207) - headers = propfind.response["headers"] - self.assertTrue(headers.has_key("dav"), - "DAV header must be returned when user-agent is iCal 4") - - expectedDAVClasses = ["1", "2", "access-control", "calendar-access", - "calendar-schedule", "calendar-auto-schedule", - "calendar-proxy"] - davClasses = [x.strip() for x in headers["dav"].split(",")] - for davClass in expectedDAVClasses: - self.assertTrue(davClass in davClasses, - "DAV class '%s' not found" % davClass) - - def _setMemberSet(self, owner, members, perm): - resource = '/SOGo/dav/%s/calendar-proxy-%s/' % (owner, perm) - membersHref = [ { "{DAV:}href": '/SOGo/dav/%s/' % x } - for x in members ] - props = { "{DAV:}group-member-set": membersHref } - proppatch = webdavlib.WebDAVPROPPATCH(resource, props) - client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password) - client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)" - client.execute(proppatch) - self.assertEquals(proppatch.response["status"], 207, - "failure (%s) setting '%s' permission for '%s' on %s's calendars" - % (proppatch.response["status"], perm, - "', '".join(members), owner)) - - def _getMembership(self, user): - resource = '/SOGo/dav/%s/' % user - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}group-membership"], 0) - client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password) - client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)" - client.execute(propfind) - - hrefs = propfind.response["document"].findall("{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}group-membership/{DAV:}href") - members = [x.text for x in hrefs] - - return members - - def _getProxyFor(self, user, perm): - resource = '/SOGo/dav/%s/' % user - prop = "{http://calendarserver.org/ns/}calendar-proxy-%s-for" % perm - propfind = webdavlib.WebDAVPROPFIND(resource, [prop], 0) - client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password) - client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)" - client.execute(propfind) - - hrefs = propfind.response["document"].findall("{DAV:}response/{DAV:}propstat/{DAV:}prop/{http://calendarserver.org/ns/}calendar-proxy-%s-for/{DAV:}href" - % perm) - members = [x.text[len("/SOGo/dav/"):-1] for x in hrefs] - - return members - - def testCalendarProxy(self): - """calendar-proxy as used from iCal""" - self._setMemberSet(username, [], "read") - self._setMemberSet(username, [], "write") - self._setMemberSet(subscriber_username, [], "read") - self._setMemberSet(subscriber_username, [], "write") - self.assertEquals([], self._getMembership(username), - "'%s' must have no membership" - % username) - self.assertEquals([], self._getMembership(subscriber_username), - "'%s' must have no membership" - % subscriber_username) - self.assertEquals([], self._getProxyFor(username, "read"), - "'%s' must not be a proxy for anyone" % username) - self.assertEquals([], self._getProxyFor(username, "write"), - "'%s' must not be a proxy for anyone" % username) - self.assertEquals([], self._getProxyFor(subscriber_username, "read"), - "'%s' must not be a proxy for anyone" % subscriber_username) - self.assertEquals([], self._getProxyFor(subscriber_username, "write"), - "'%s' must not be a proxy for anyone" % subscriber_username) - - for perm in ("read", "write"): - for users in ((username, subscriber_username), - (subscriber_username, username)): - self._setMemberSet(users[0], [users[1]], perm) - membership = self._getMembership(users[1]) - self.assertEquals(['/SOGo/dav/%s/calendar-proxy-%s/' - % (users[0], perm)], - membership, - "'%s' must have %s access to %s's calendars" - % (users[1], perm, users[0])) - proxyFor = self._getProxyFor(users[1], perm) - self.assertEquals([users[0]], proxyFor, - "'%s' expected to be %s proxy for %s: %s" - % (users[1], perm, users[0], proxyFor)) - - def _testMapping(self, client, perm, resource, rights): - dav_utility = utilities.TestCalendarACLUtility(self, client, resource) - dav_utility.setupRights(subscriber_username, rights) - - membership = self._getMembership(subscriber_username) - self.assertEquals(['/SOGo/dav/%s/calendar-proxy-%s/' - % (username, perm)], - membership, - "'%s' must have %s access to %s's calendars:\n%s" - % (subscriber_username, perm, username, membership)) - proxyFor = self._getProxyFor(subscriber_username, perm) - self.assertEquals([username], proxyFor, - "'%s' expected to be %s proxy for %s: %s" - % (subscriber_username, perm, username, proxyFor)) - - def testCalendarProxy2(self): - """calendar-proxy as used from SOGo""" - client = webdavlib.WebDAVClient(hostname, port, superuser, superuser_password) - client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)" - personal_resource = "/SOGo/dav/%s/Calendar/personal/" % username - dav_utility = utilities.TestCalendarACLUtility(self, - client, - personal_resource) - dav_utility.setupRights(subscriber_username, {}) - dav_utility.subscribe([subscriber_username]) - - other_resource = ("/SOGo/dav/%s/Calendar/test-calendar-proxy2/" - % username) - delete = webdavlib.WebDAVDELETE(other_resource) - client.execute(delete) - mkcol = webdavlib.WebDAVMKCOL(other_resource) - client.execute(mkcol) - dav_utility = utilities.TestCalendarACLUtility(self, - client, - other_resource) - dav_utility.setupRights(subscriber_username, {}) - dav_utility.subscribe([subscriber_username]) - - ## we test the rights mapping - # write: write on 'personal', none on 'test-calendar-proxy2' - self._testMapping(client, "write", personal_resource, - { "c": True, "d": False, "pu": "v" }) - self._testMapping(client, "write", personal_resource, - { "c": False, "d": True, "pu": "v" }) - self._testMapping(client, "write", personal_resource, - { "c": False, "d": False, "pu": "m" }) - self._testMapping(client, "write", personal_resource, - { "c": False, "d": False, "pu": "r" }) - - # read: read on 'personal', none on 'test-calendar-proxy2' - self._testMapping(client, "read", personal_resource, - { "c": False, "d": False, "pu": "d" }) - self._testMapping(client, "read", personal_resource, - { "c": False, "d": False, "pu": "v" }) - - # write: read on 'personal', write on 'test-calendar-proxy2' - self._testMapping(client, "write", other_resource, - { "c": False, "d": False, "pu": "r" }) - - ## we test the unsubscription - # unsubscribed from personal, subscribed to 'test-calendar-proxy2' - dav_utility = utilities.TestCalendarACLUtility(self, client, - personal_resource) - dav_utility.unsubscribe([subscriber_username]) - membership = self._getMembership(subscriber_username) - self.assertEquals(['/SOGo/dav/%s/calendar-proxy-write/' % username], - membership, - "'%s' must have write access to %s's calendars" - % (subscriber_username, username)) - # unsubscribed from personal, unsubscribed from 'test-calendar-proxy2' - dav_utility = utilities.TestCalendarACLUtility(self, client, - other_resource) - dav_utility.unsubscribe([subscriber_username]) - membership = self._getMembership(subscriber_username) - self.assertEquals([], - membership, - "'%s' must have no access to %s's calendars" - % (subscriber_username, username)) - - delete = webdavlib.WebDAVDELETE(other_resource) - client.execute(delete) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-maildav.py b/Tests/Integration/test-maildav.py deleted file mode 100755 index abdb40426..000000000 --- a/Tests/Integration/test-maildav.py +++ /dev/null @@ -1,677 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password, mailserver, subscriber_username, subscriber_password - -import sys -import sogotests -import unittest -import webdavlib -import time - -# TODO -# add test with multiple sort criterias - -def fetchUserEmail(login): - client = webdavlib.WebDAVClient(hostname, port, - username, password) - resource = '/SOGo/dav/%s/' % login - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{urn:ietf:params:xml:ns:caldav}calendar-user-address-set"], - 0) - client.execute(propfind) - nodes = propfind.xpath_evaluate('{DAV:}response/{DAV:}propstat/{DAV:}prop/C:calendar-user-address-set/{DAV:}href', - None) - - return nodes[0].childNodes[0].nodeValue - -message1 = """Return-Path: -Received: from cyril.dev (localhost [127.0.0.1]) - by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA; - Tue, 17 Dec 2009 07:42:16 -0400 -Received: from aloha.dev (localhost [127.0.0.1]) - by aloha.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA; - Tue, 29 Sep 2009 07:42:16 -0400 -Message-ID: <4AC1F29sept6.5060801@cyril.dev> -Date: Mon, 28 Sep 2009 07:42:14 -0400 -From: Cyril -User-Agent: Thunderbird 2.0.0.22 (Macintosh/20090605) -References: <4AC3BF1B.3010806@inverse.ca> -MIME-Version: 1.0 -To: message1to@cyril.dev -CC: 2message1cc@cyril.dev, user10@cyril.dev -Subject: message1subject -Content-Type: text/plain; charset=us-ascii; format=flowed -Content-Transfer-Encoding: 7bit -Reply-To: support@inverse.ca - -Hello Jacques, - -Can you read me? - --- -Cyril -""" - -message2 = """Return-Path: -Received: from cyril.dev (localhost [127.0.0.1]) - by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA; - Tue, 09 Dec 2009 07:42:16 -0400 -Message-ID: <410sepAC1F296.5060801a@cyril.dev> -Date: Tue, 10 Sep 2009 07:42:14 -0400 -User-Agent: Thunderbird 2.0.0.22 (Macintosh/20090605) -MIME-Version: 1.0 -From: Cyril -To: message2to@cyril.dev -CC: 3message2cc@cyril.dev -Subject: message2subject -Content-Type: text/plain; charset=us-ascii; format=flowed -Content-Transfer-Encoding: 7bit -Reply-To: support@inverse.ca - -Hello Jacques, - -Can you read me? - -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff -Stuff StuffStuffStuffStuff StuffStuffStuff StuffStuff --- -Cyril -""" - -message3 = """Return-Path: -Received: from cyril.dev (localhost [127.0.0.1]) - by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA; - Tue, 15 Dec 2009 07:42:16 -0400 -Message-ID: <4AC1aF2dec96.5060801a@cyril.dev> -Date: Tue, 10 Dec 2009 07:42:14 -0400 -User-Agent: Thunderbird 2.0.0.22 (Macintosh/20090605) -MIME-Version: 1.0 -From: Cyril -To: message3to@cyril.dev -CC: 1message3cc@cyril.dev -Subject: Hallo -Content-Type: text/plain; charset=us-ascii; format=flowed -Content-Transfer-Encoding: 7bit -Reply-To: support@inverse.ca - -Hello Jacques, - -Can you read me? - -This message is just a bit larger than message1 but smaller than message2 --- -Cyril -""" -message1_received = """Received: from cyril.dev (localhost [127.0.0.1]) - by cyril.dev (Cyrus v2.3.8-Debian-2.3.8-1) with LMTPA; - Tue, 17 Dec 2009 07:42:16 -0400""" - -class DAVMailCollectionTest(): - resource = '/SOGo/dav/%s/Mail/' % username - user_email = None - - def setUp(self): - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - if self.user_email is None: - self.user_email = fetchUserEmail(username) - if self.user_email.startswith("mailto:"): - self.user_email = self.user_email[7:] - - self.resource = '/SOGo/dav/%s/Mail/%s_A_%s/' \ - % (username, - username.replace("@", "_A_").replace(".", "_D_"), - mailserver.replace(".", "_D_")) - - ## helper methods - def _makeCollection(self, name, status = 201): - url = "%s%s" % (self.resource, name) - mkcol = webdavlib.WebDAVMKCOL(url) - self.client.execute(mkcol) - self.assertEquals(mkcol.response["status"], status, - "failure creating collection" - "(code = %d)" % mkcol.response["status"]) - - def _deleteCollection(self, name, status = 204): - url = "%sfolder%s" % (self.resource, name) - delete = webdavlib.WebDAVDELETE(url) - self.client.execute(delete) - self.assertEquals(delete.response["status"], status, - "failure deleting collection" - "(code = %d)" % delete.response["status"]) - - def _putMessage(self, client, folder, message, - exp_status = 201): - url = "%sfolder%s" % (self.resource, folder) - put = webdavlib.HTTPPUT(url, message) - put.content_type = "message/rfc822" - client.execute(put) - if (exp_status is not None): - self.assertEquals(put.response["status"], exp_status, - "message creation/modification:" - " expected status code '%d' (received '%d')" - % (exp_status, put.response["status"])) - return put.response["headers"]["location"] - - def _testProperty (self, url, property, expected, isDate = 0): - propfind = webdavlib.WebDAVPROPFIND(url, (property, ), 0) - self.client.execute(propfind) - key = property.replace("{urn:schemas:httpmail:}", "a:") - key = key.replace("{urn:schemas:mailheader:}", "a:") - tmp = propfind.xpath_evaluate("{DAV:}response/{DAV:}propstat/{DAV:}prop") - prop = tmp[0].firstChild; - result = None - - if prop: - result = prop._get_firstChild()._get_nodeValue() - #print key, result - - if isDate: - tstruct = time.strptime (result, "%a, %d %b %Y %H:%M:%S %Z") - result = int (time.mktime (tstruct)) - - self.assertEquals(result, expected, - "failure in propfind" - "(%s != %s)" % (result, expected)) - - def testMKCOL(self): - """Folder creation""" - self._deleteCollection("test-dav-mail-%40-abc") - self._deleteCollection("test-dav-mail-@-def") - self._deleteCollection("test-dav-mail-%20-ghi") - - self._makeCollection("test-dav-mail-%40-abc") - self._makeCollection("test-dav-mail-@-def") - self._makeCollection("test-dav-mail-%20-ghi") - self._makeCollection("test-dav-mail-%25-jkl", 500) - - # Test MOVE -# self._makeCollection ("test-dav-mail-movable") -# url = "%sfolder%s" % (self.resource, "test-dav-mail-movable") -# move = webdavlib.WebDAVMOVE (url) -# move.destination = "http://cyril.dev%s%s2" % (self.resource, "test-dav-mail-movable") -# move.host = "cyril.dev" -# self.client.execute (move) -# self.assertEquals(move.response["status"], 204, -# "failure creating collection" -# "(code = %d)" % move.response["status"]) - - def testPUT(self): - """Message creation""" - self._deleteCollection("test-dav-mail") - self._makeCollection("test-dav-mail") - - # message creation on collection url - url = "%s%s" % (self.resource, "foldertest-dav-mail/") - put = webdavlib.HTTPPUT(url, message1) - put.content_type = "message/rfc822" - self.client.execute(put) - self.assertEquals(put.response["status"], 201, - "failure putting message" - "(code = %d)" % put.response["status"]) - - itemLocation = put.response["headers"]["location"] - get = webdavlib.WebDAVGET(itemLocation) - self.client.execute(get) - self.assertEquals(get.response["status"], 200, - "failure getting item" - "(code = %d)" % get.response["status"]) - - # message creation with explicit filename - url = "%s%s" %(self.resource, "foldertest-dav-mail/blabla.eml") - put = webdavlib.HTTPPUT(url, message1) - put.content_type = "message/rfc822" - self.client.execute(put) - self.assertEquals(put.response["status"], 201, - "failure putting message" - "(code = %d)" % put.response["status"]) - - itemLocation = put.response["headers"]["location"] - get = webdavlib.WebDAVGET(itemLocation) - self.client.execute(get) - self.assertEquals(get.response["status"], 200, - "failure getting item" - "(code = %d)" % get.response["status"]) - - self._deleteCollection("test-dav-mail") - - def _testFilters(self, filters): - for filter in filters: - self._testFilter(filter) - - def _testFilter(self, filter): - expected_hrefs = {} - expected_count = len(filter[1]) - for href in filter[1]: - expected_hrefs[href] = True - - received_count = 0 - url = "%sfolder%s" % (self.resource, "test-dav-mail") - query = webdavlib.MailDAVMailQuery(url, ["displayname"], filter[0]) - self.client.execute(query) - self.assertEquals(query.response["status"], 207, - "filter %s:\n\tunexpected status: %d" - % (filter[0], query.response["status"])) - response_nodes = query.xpath_evaluate("{DAV:}response") - for response_node in response_nodes: - href_node = query.xpath_evaluate("{DAV:}href", response_node)[0] - href = href_node.childNodes[0].nodeValue - received_count = received_count + 1 - self.assertTrue(expected_hrefs.has_key(href), - "filter %s:\n\tunexpected href: %s" % (filter[0], href)) - - self.assertEquals(len(filter[1]), received_count, - "filter %s:\n\tunexpected amount of refs: %d" - % (filter[0], received_count)) - - def _testSort(self, sortOrder, ascending = True): - expected_hrefs = sortOrder[1] - expected_count = len(expected_hrefs) - - received_count = 0 - url = "%sfolder%s" % (self.resource, "test-dav-mail") - query = webdavlib.MailDAVMailQuery(url, ["displayname"], - None, sortOrder[0], ascending) - self.client.execute(query) - self.assertEquals(query.response["status"], 207, - "sortOrder %s:\n\tunexpected status: %d" - % (sortOrder[0], query.response["status"])) - response_nodes = query.response["document"].findall("{DAV:}response") - for response_node in response_nodes: - href_node = response_node.find("{DAV:}href") - href = href_node.text - self.assertEquals(expected_hrefs[received_count], href, - "sortOrder %s:\n\tunexpected href: %s (expecting: %s)" - % (sortOrder[0], href, - expected_hrefs[received_count])) - received_count = received_count + 1 - - self.assertEquals(expected_count, received_count, - "sortOrder %s:\n\tunexpected amount of refs: %d" - % (sortOrder[0], received_count)) - - def testREPORTMailQueryFilters(self): - """mail-query filters""" - self._deleteCollection("test-dav-mail") - self._makeCollection("test-dav-mail") - - msg1Loc = self._putMessage(self.client, "test-dav-mail", message1) - parsed = webdavlib.HTTPUnparsedURL(msg1Loc) - msg1Path = parsed.path - msg2Loc = self._putMessage(self.client, "test-dav-mail", message2) - parsed = webdavlib.HTTPUnparsedURL(msg2Loc) - msg2Path = parsed.path - msg3Loc = self._putMessage(self.client, "test-dav-mail", message3) - parsed = webdavlib.HTTPUnparsedURL(msg3Loc) - msg3Path = parsed.path - - properties = ["{DAV:}displayname"] - - ## 1. test filter: receive-date - # SINCE, BEFORE, ON - # q = MailDAVMailQuery(self.resource, properties, filters = None) - - filters = (({ "receive-date": { "from": "20091201T000000Z", - "to": "20091208T000000Z" } }, - []), - ({ "receive-date": { "from": "20091208T000000Z", - "to": "20091213T134300Z" } }, - [ msg2Loc ]), - ({ "receive-date": { "from": "20091208T000000Z", - "to": "20091216T134300Z" } }, - [ msg2Loc, msg3Loc ]), - ({ "receive-date": { "from": "20091216T000000Z", - "to": "20091220T134300Z" } }, - [ msg1Loc ]), - ({ "receive-date": { "from": "20091220T000000Z", - "to": "20091229T134300Z" } }, - [])) - # receive-date seems to be considered the same as date by imapd - print "Warning, receive-date test disabled" - #self._testFilters(filters) - - ## 1. test filter: date - # SENTSINCE, SENTBEFORE, SENTON - - filters = (({ "date": { "from": "20090101T000000Z", - "to": "20090201T000000Z" } }, - []), - ({ "date": { "from": "20090912T000000Z", - "to": "20090929T134300Z" } }, - [ msg1Loc ]), - ({ "date": { "from": "20090929T134300Z", - "to": "20091209T000000Z" } }, - []), - ({ "date": { "from": "20090901T134300Z", - "to": "20091209T000000Z" } }, - [ msg1Loc, msg2Loc ]), - ({ "date": { "from": "20091201T000000Z", - "to": "20091211T000000Z" } }, - [ msg3Loc ]), - ({ "date": { "from": "20091211T000000Z", - "to": "20101211T000000Z" } }, - []), - ({ "date": { "from": "20090101T000000Z", - "to": "20100101T000000Z" } }, - [ msg1Loc, msg2Loc, msg3Loc ])) - self._testFilters(filters) - - ## 1. test filter: sequence - # x:y - filters = (({ "sequence": { "from": "1" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "sequence": { "from": "5" }}, - []), - ({ "sequence": { "to": "5" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "sequence": { "from": "1", - "to": "2" }}, - [ msg1Loc, msg2Loc ])) - # Sequence not yet implemented - print "Warning, sequence test disabled" - #self._testFilters(filters) - - ## 1. test filter: uid - # UID - filters = (({ "uid": { "from": "1" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - # disabled because we get 3 - #({ "uid": { "from": "5" }}, - # []), - ({ "uid": { "to": "5" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "uid": { "from": "1", - "to": "2" }}, - [ msg1Loc, msg2Loc ])) - print "Warning, one of the uid tests is disabled" - self._testFilters(filters) - - ## 1. test filter: from - # FROM - filters = (({ "from": { "match": "message" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "from": { "match": "Cyril" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "from": { "match": "cyril.dev" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "from": { "match": "message1from" }}, - [ msg1Loc ]), - ({ "from": { "match": "message2from" }}, - [ msg2Loc ]), - ({ "from": { "match": "message3from" }}, - [ msg3Loc ])) - self._testFilters(filters) - - ## 1. test filter: to - # TO - filters = (({ "to": { "match": "message" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "to": { "match": "Cyril" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "to": { "match": "cyril.dev" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "to": { "match": "message1to" }}, - [ msg1Loc ]), - ({ "to": { "match": "message2to" }}, - [ msg2Loc ]), - ({ "to": { "match": "message3to" }}, - [ msg3Loc ])) - self._testFilters(filters) - - ## 1. test filter: cc - # CC - filters = (({ "cc": { "match": "message" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "cc": { "match": "Cyril" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "cc": { "match": "cyril.dev" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "cc": { "match": "message1cc" }}, - [ msg1Loc ]), - ({ "cc": { "match": "message2cc" }}, - [ msg2Loc ]), - ({ "cc": { "match": "message3cc" }}, - [ msg3Loc ])) - self._testFilters(filters) - - ## 1. test filter: bcc - # BCC - ## TODO - - ## 1. test filter: body - # BODY - filters = (({ "body": { "match": "Hello" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "body": { "match": "Stuff" }}, - [ msg2Loc ]), - ({ "body": { "match": "DOESNOT MATCH" }}, - [])) - self._testFilters(filters) - - ## 1. test filter: size - # LARGER, SMALLER - #1 848 - #2 4308 - #3 699 - filters = (({ "size": { "min": "300", - "max": "300" }}, - []), - ({ "size": { "min": "800", - "max": "800" }}, - []), - ({ "size": { "min": "5000", - "max": "5000" }}, - []), - ({ "size": { "min": "838", - "max": "838" }}, - [ msg1Loc ]), - ({ "size": { "min": "699", - "max": "4308" }}, - [ msg1Loc, msg2Loc, msg3Loc ]), - ({ "size": { "min": "700", - "max": "4308" }}, - [ msg1Loc, msg2Loc ]), - ({ "size": { "min": "698", - "max": "848" }}, - [ msg1Loc, msg3Loc ]), - ({ "size": { "min": "300", - "max": "5000" }, - "size": { "min": "840", - "max": "850", - "not": "true" }}, - [ msg2Loc, msg3Loc ])) - - print "message flags are not handled yet" - ## 1. test filter: answered - # ANSWERED, UNANSWERED - ## 1. test filter: draft - # DRAFT - ## 1. test filter: flagged - # FLAGGED - ## 1. test filter: recent - # RECENT - ## 1. test filter: seen - # SEEN - ## 1. test filter: deleted - # DELETED - ## 1. test filter: keywords - # KEYWORD x - - ## 1. test filter: multiple combinations - filters = (({ "body": { "match": "Hello" }, - "cc": { "match": "message1cc" }}, - [ msg1Loc ]), - ({ "to": { "match": "message" }, - "uid": { "from": "1", - "to": "2" }}, - [ msg1Loc, msg2Loc ]), - ({ "to": { "match": "message" }, - "uid": { "from": "1", - "to": "2" }, - "cc": { "match": "message3cc" }}, - [])) - self._testFilters(filters) - - self._deleteCollection("test-dav-mail") - - def testREPORTMailQuerySort(self): - """mail-query sort""" - self._deleteCollection("test-dav-mail") - self._makeCollection("test-dav-mail") - - msg1Loc = self._putMessage(self.client, "test-dav-mail", message1) - parsed = webdavlib.HTTPUnparsedURL(msg1Loc) - msg1Path = parsed.path - msg2Loc = self._putMessage(self.client, "test-dav-mail", message2) - parsed = webdavlib.HTTPUnparsedURL(msg2Loc) - msg2Path = parsed.path - msg3Loc = self._putMessage(self.client, "test-dav-mail", message3) - parsed = webdavlib.HTTPUnparsedURL(msg3Loc) - msg3Path = parsed.path - - # 1. test sort: (receive-date) ARRIVAL - self._testSort(([ "{urn:schemas:mailheader:}received" ], - [ msg1Loc, msg2Loc, msg3Loc ])) - - # 1. test sort: (date) DATE - self._testSort(([ "{urn:schemas:mailheader:}date" ], - [ msg2Loc, msg1Loc, msg3Loc ])) - - # 1. test sort: FROM - self._testSort(([ "{urn:schemas:mailheader:}from" ], - [ msg1Loc, msg2Loc, msg3Loc ])) - - # 1. test sort: TO - self._testSort(([ "{urn:schemas:mailheader:}to" ], - [ msg1Loc, msg2Loc, msg3Loc ])) - - # 1. test sort: CC - self._testSort(([ "{urn:schemas:mailheader:}cc" ], - [ msg3Loc, msg1Loc, msg2Loc ])) - - # 1. test sort: SUBJECT - self._testSort(([ "{DAV:}displayname" ], - [ msg3Loc, msg1Loc, msg2Loc ])) - self._testSort(([ "{urn:schemas:mailheader:}subject" ], - [ msg3Loc, msg1Loc, msg2Loc ])) - - # 1. test sort: SIZE - self._testSort(([ "{DAV:}getcontentlength" ], - [ msg3Loc, msg1Loc, msg2Loc ])) - - # 1. test sort: REVERSE CC - self._testSort(([ "{urn:schemas:mailheader:}cc" ], - [ msg2Loc, msg1Loc, msg3Loc ]), - False) - - self._deleteCollection("test-dav-mail") - - def testPROPFIND(self): - """Message properties""" - self._deleteCollection ("test-dav-mail") - self._makeCollection ("test-dav-mail") - - url = "%s%s" % (self.resource, "foldertest-dav-mail/") - put = webdavlib.HTTPPUT (url, message1) - put.content_type = "message/rfc822" - self.client.execute (put) - self.assertEquals(put.response["status"], 201, - "failure putting message" - "(code = %d)" % put.response["status"]) - - itemLocation = put.response["headers"]["location"] - tests = (("{urn:schemas:httpmail:}date", 1254156134, 1), - ("{urn:schemas:httpmail:}hasattachment", "0", 0), - ("{urn:schemas:httpmail:}read", "0", 0), - ("{urn:schemas:httpmail:}textdescription", - "" % message1, 0), - ("{urn:schemas:httpmail:}unreadcount", None, 0), - ("{urn:schemas:mailheader:}cc", - "2message1cc@cyril.dev, user10@cyril.dev", 0), - ("{urn:schemas:mailheader:}date", - "Mon, 28 Sep 2009 11:42:14 GMT", 0), - ("{urn:schemas:mailheader:}from", - "Cyril ", 0), - ("{urn:schemas:mailheader:}in-reply-to", None, 0), - ("{urn:schemas:mailheader:}message-id", - "<4AC1F29sept6.5060801@cyril.dev>", 0), - ("{urn:schemas:mailheader:}received", message1_received, 0), - ("{urn:schemas:mailheader:}references", - "<4AC3BF1B.3010806@inverse.ca>", 0), - ("{urn:schemas:mailheader:}subject", "message1subject", 0), - ("{urn:schemas:mailheader:}to", "message1to@cyril.dev", 0)) - - for test in tests: - property, expected, isDate = test - self._testProperty(itemLocation, property, expected, isDate) - - self._deleteCollection ("test-dav-mail") - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-preferences.py b/Tests/Integration/test-preferences.py deleted file mode 100644 index 6d11241de..000000000 --- a/Tests/Integration/test-preferences.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python -from config import hostname, port, username, password, white_listed_attendee - -import preferences -import simplejson -import sogotests -import unittest -import utilities - -class preferencesTest(unittest.TestCase): - - def setUp(self): - self.prefs = preferences.preferences() - # because if not set in vacation will not be found later - # we must make sure they are there at the start - self.prefs.set_or_create("autoReplyText", '', ["defaults", "Vacation"]) - self.prefs.set_or_create("PreventInvitations", 0, ["settings", "Calendar"]) - self.prefs.set_or_create("PreventInvitationsWhitelist", [], ["settings", "Calendar"]) - - def tearDown(self): - self.prefs.set("autoReplyText", "") - - def _setTextPref(self, prefText = None ): - """ set a text preference to a known value """ - self.prefs.set("autoReplyText", prefText) - - # make sure it was set correctly - prefData = self.prefs.get("Vacation") - - self.assertEqual(prefData["autoReplyText"], prefText, - "%s != %s" % (prefData["autoReplyText"], prefText)) - - def testSetTextPreferences(self): - """ Set/get a text preference - normal characters""" - self._setTextPref("defaultText") - - def testSetTextPreferencesWeirdChars(self): - """ Set/get a text preference - weird characters - used to crash on 1.3.12""" - prefText = "weird data \ ' \"; ^" - self._setTextPref(prefText) - - def testSetPreventInvitation(self): - """ Set/get the PreventInvitation pref""" - self.prefs.set('PreventInvitations', 0) - notset = self.prefs.get('Calendar')['PreventInvitations'] - self.assertEqual(notset, 0) - self.prefs.set('PreventInvitations', 1) - isset = self.prefs.get('Calendar')['PreventInvitations'] - self.assertEqual(isset, 1) - - def testPreventInvitationsWhiteList(self): - """Add to the PreventInvitations Whitelist""" - self.prefs.set("PreventInvitationsWhitelist", white_listed_attendee) - whitelist = self.prefs.get('Calendar')['PreventInvitationsWhitelist'] - self.assertEqual(whitelist, white_listed_attendee) - - - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-prevent-invitations.py b/Tests/Integration/test-prevent-invitations.py deleted file mode 100755 index c359d5369..000000000 --- a/Tests/Integration/test-prevent-invitations.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/python -from config import hostname, port, username, password, \ - superuser, superuser_password, \ - attendee1, attendee1_username, \ - attendee1_password, \ - attendee1_delegate, attendee1_delegate_username, \ - attendee1_delegate_password, \ - resource_no_overbook, resource_can_overbook, \ - white_listed_attendee - -import preferences -import simplejson -import sogotests -import unittest -import utilities -import datetime -import dateutil.tz -import vobject -import vobject.base -import vobject.icalendar -import webdavlib -import StringIO - - -class preventInvitationsTest(unittest.TestCase): - def setUp(self): - self.prefs = preferences.preferences(attendee1, attendee1_password) - self.caldav = CalDAVSchedulingTest(self) - cal = self.prefs.get("Calendar") - if "PreventInvitationsWhitelist" not in cal: - cal["PreventInvitationsWhitelist"] = None - self.prefs.set("PreventInvitationsWhitelist", None) - if "PreventInvitations" not in cal: - cal["PreventInvitations"] = 0 - self.prefs.set("PreventInvitations", 0) - - def tearDown(self): - #self.prefs.set("autoReplyText", "") - self.prefs.set('PreventInvitations', 0) - self.prefs.set("PreventInvitationsWhitelist", None) - #- Manual Cleanup, not called because classs is not derived from unittest - self.caldav.tearDown() - - def not_test_empty_string_instead_of_null(self): - self.prefs.set('PreventInvitationsWhitelist', "") - - def testDontPreventInvitation(self): - """ Set/get the PreventInvitation pref""" - #- First accept the invitation - self.prefs.set('PreventInvitations', 0) - notset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] - self.assertEqual(notset, 0) - self.caldav.AddAttendee() - self.caldav.VerifyEvent() - - def testPreventInvitation(self): - """ Set PreventInvitation and don't accept the Invitation""" - #- Second, enable PreventInviation and refuse it - self.prefs.set('PreventInvitations', 1) - isset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] - self.assertEqual(isset, 1) - self.caldav.AddAttendee(409) - self.caldav.VerifyEvent(404) - - def testPreventInvitationWhiteList(self): - """ Set PreventInvitation add to WhiteList and accept the Invitation""" - #- First, add the Organiser to the Attendee's whitelist - self.prefs.set('PreventInvitations', 1) - self.prefs.set("PreventInvitationsWhitelist", simplejson.dumps(white_listed_attendee)) - whitelist = self.prefs.get_settings('Calendar')['PreventInvitationsWhitelist'] - self.assertEqual(whitelist, white_listed_attendee) - - #- Second, try again to invite, it should work - self.prefs.set('PreventInvitations', 1) - isset = self.prefs.get_settings('')['Calendar']['PreventInvitations'] - self.assertEqual(isset, 1) - self.caldav.AddAttendee() - self.caldav.VerifyEvent() - - -class CalDAVSchedulingTest(object): - def __init__(self, parent_self): - self.test = parent_self # used for utilities - self.setUp() - - def setUp(self): - self.superuser_client = webdavlib.WebDAVClient(hostname, port, - superuser, superuser_password) - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - self.attendee1_client = webdavlib.WebDAVClient(hostname, port, - attendee1_username, attendee1_password) - self.attendee1_delegate_client = webdavlib.WebDAVClient(hostname, port, - attendee1_delegate_username, attendee1_delegate_password) - - utility = utilities.TestUtility(self.test, self.client) - (self.user_name, self.user_email) = utility.fetchUserInfo(username) - (self.attendee1_name, self.attendee1_email) = utility.fetchUserInfo(attendee1) - (self.attendee1_delegate_name, self.attendee1_delegate_email) = utility.fetchUserInfo(attendee1_delegate) - - self.user_calendar = "/SOGo/dav/%s/Calendar/personal/" % username - self.attendee1_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1 - self.attendee1_delegate_calendar = "/SOGo/dav/%s/Calendar/personal/" % attendee1_delegate - - # fetch non existing event to let sogo create the calendars in the db - self._getEvent(self.client, "%snonexistent" % self.user_calendar, exp_status=404) - self._getEvent(self.attendee1_client, "%snonexistent" % self.attendee1_calendar, exp_status=404) - self._getEvent(self.attendee1_delegate_client, "%snonexistent" % - self.attendee1_delegate_calendar, exp_status=404) - - # list of ics used by the test. - # tearDown will loop over this and wipe them in all users' calendar - self.ics_list = [] - - - def tearDown(self): - # delete all created events from all users' calendar - for ics in self.ics_list: - self._deleteEvent(self.superuser_client, - "%s%s" % (self.user_calendar, ics), None) - self._deleteEvent(self.superuser_client, - "%s%s" % (self.attendee1_calendar, ics), None) - self._deleteEvent(self.superuser_client, - "%s%s" % (self.attendee1_delegate_calendar, ics), None) - - def _newEvent(self, summary="test event", uid="test", transp=0): - transparency = ("OPAQUE", "TRANSPARENT") - - newCal = vobject.iCalendar() - vevent = newCal.add('vevent') - vevent.add('summary').value = summary - vevent.add('transp').value = transparency[transp] - - now = datetime.datetime.now(dateutil.tz.gettz("America/Montreal")) - startdate = vevent.add('dtstart') - startdate.value = now - enddate = vevent.add('dtend') - enddate.value = now + datetime.timedelta(0, 3600) - vevent.add('uid').value = uid - vevent.add('dtstamp').value = now - vevent.add('last-modified').value = now - vevent.add('created').value = now - vevent.add('class').value = "PUBLIC" - vevent.add('sequence').value = "0" - - return newCal - - def _putEvent(self, client, filename, event, exp_status = 201): - put = webdavlib.HTTPPUT(filename, event.serialize()) - put.content_type = "text/calendar; charset=utf-8" - client.execute(put) - if exp_status is not None: - self.test.assertEquals(put.response["status"], exp_status) - - def _getEvent(self, client, filename, exp_status = 200): - get = webdavlib.HTTPGET(filename) - client.execute(get) - - if exp_status is not None: - self.test.assertEquals(get.response["status"], exp_status) - - if get.response["headers"]["content-type"].startswith("text/calendar"): - stream = StringIO.StringIO(get.response["body"]) - event = vobject.base.readComponents(stream).next() - else: - event = None - - return event - - def _deleteEvent(self, client, filename, exp_status = 204): - delete = webdavlib.WebDAVDELETE(filename) - client.execute(delete) - if exp_status is not None: - self.test.assertEquals(delete.response["status"], exp_status) - - def AddAttendee(self, exp_status=204): - """ add attendee after event creation """ - - # make sure the event doesn't exist - ics_name = "test-add-attendee.ics" - self.ics_list += [ics_name] - - self._deleteEvent(self.client, - "%s%s" % (self.user_calendar,ics_name), None) - self._deleteEvent(self.attendee1_client, - "%s%s" % (self.attendee1_calendar,ics_name), None) - - # 1. create an event in the organiser's calendar - event = self._newEvent(summary="Test add attendee", uid="Test add attendee") - organizer = event.vevent.add('organizer') - organizer.cn_param = self.user_name - organizer.value = self.user_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event) - - # 2. add an attendee - event.add("method").value = "REQUEST" - attendee = event.vevent.add('attendee') - attendee.cn_param = self.attendee1_name - attendee.rsvp_param = "TRUE" - attendee.partstat_param = "NEEDS-ACTION" - attendee.value = self.attendee1_email - self._putEvent(self.client, "%s%s" % (self.user_calendar, ics_name), event, - exp_status=exp_status) - - #- save event for VerifyEvent - self.event = event - self.ics_name = ics_name - - def VerifyEvent(self, exp_status=200): - # 1. verify that the attendee has the event - attendee_event = self._getEvent(self.attendee1_client, "%s%s" % (self.attendee1_calendar, self.ics_name), exp_status) - - # 2. make sure the received event match the original one - if attendee_event: - self.test.assertEquals(self.event.vevent.uid, attendee_event.vevent.uid) - - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-sieve.py b/Tests/Integration/test-sieve.py deleted file mode 100755 index f8d82d888..000000000 --- a/Tests/Integration/test-sieve.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/python -from config import hostname, port, username, password, sieve_port, sieve_server - -import managesieve -import preferences -import sogotests -import unittest -import utilities -import webdavlib - -sieve_simple_vacation="""require ["vacation"];\r\nvacation :days %(days)d :addresses ["%(mailaddr)s"] text:\r\n%(vacation_msg)s\r\n.\r\n;\r\n""" -sieve_vacation_ignoreLists="""require ["vacation"];\r\nif allof ( not exists ["list-help", "list-unsubscribe", "list-subscribe", "list-owner", "list-post", "list-archive", "list-id", "Mailing-List"], not header :comparator "i;ascii-casemap" :is "Precedence" ["list", "bulk", "junk"], not header :comparator "i;ascii-casemap" :matches "To" "Multiple recipients of*" ) { vacation :days %(days)d :addresses ["%(mailaddr)s"] text:\r\n%(vacation_msg)s\r\n.\r\n;\r\n}\r\n""" -sieve_simple_forward="""redirect "%(redirect_mailaddr)s";\r\n""" -sieve_forward_keep="""redirect "%(redirect_mailaddr)s";\r\nkeep;\r\n""" -sieve_simple_filter="""require ["fileinto"];\r\nif anyof (header :contains "subject" "%(subject)s") {\r\n fileinto "%(folderName)s";\r\n}\r\n""" - -class sieveTest(unittest.TestCase): - def _killFilters(self): - self.prefs=preferences.preferences() - # kill existing filters - self.prefs.set_or_create("SOGoSieveFilters", [], ["defaults"]) - # vacation filters - self.prefs.set_or_create("autoReplyText", "", ["defaults", "Vacation"]) - self.prefs.set_or_create("autoReplyEmailAddresses", [], ["defaults", "Vacation"]) - self.prefs.set_or_create("daysBetweenResponse", 7, ["defaults", "Vacation"]) - self.prefs.set_or_create("ignoreLists", 0, ["defaults", "Vacation"]) - self.prefs.set_or_create("enabled", 0, ["defaults", "Vacation"]) - # forwarding filters - self.prefs.set_or_create("forwardAddress", [], ["defaults", "Forward"]) - self.prefs.set_or_create("keepCopy", 0, ["defaults", "Forward"]) - - def setUp(self): - ret = "" - - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - utility = utilities.TestUtility(self, self.client) - (self.user_name, self.user_email) = utility.fetchUserInfo(username) - self.user_email = self.user_email.replace("mailto:", "") - - self.ms = managesieve.MANAGESIEVE(sieve_server, sieve_port) - self.assertEqual(self.ms.login("", username, password), "OK", - "Couldn't login") - - self._killFilters() - - def tearDown(self): - self._killFilters() - - def _getSogoSieveScript(self): - sieveFoundsogo=0 - createdSieveScript="" - (ret, sieveScriptList) = self.ms.listscripts() - self.assertEqual(ret, "OK", "Couldn't get sieve script list") - - for (script, isActive) in sieveScriptList: - if (script == "sogo"): - sieveFoundsogo=1 - self.assertEqual(isActive, True, "sogo sieve script is not active!") - (ret, createdSieveScript) = self.ms.getscript(script) - self.assertEqual(ret, "OK", "Couldn't get sogo sieve script") - - self.assertEqual(sieveFoundsogo, 1, "sogo sieve script not found!") - - return createdSieveScript - - def testSieveSimpleVacation(self): - """ enable simple vacation script """ - vacation_msg="vacation test" - daysSelect=3 - - sieveScript = sieve_simple_vacation % { "mailaddr": self.user_email, - "vacation_msg": vacation_msg, - "days": preferences.daysBetweenResponseList[daysSelect], - } - - # Enabling Vacation now is an 'enabled' setting in the subdict Vacation - # We need to get that subdict first -- next save/set will also save this - vacation = self.prefs.get("Vacation") - vacation['enabled'] = 1 - - self.prefs.set_nosave("autoReplyText", vacation_msg) - self.prefs.set_nosave("daysBetweenResponse", "%d" % preferences.daysBetweenResponseList[daysSelect]) - self.prefs.set_nosave("autoReplyEmailAddresses", [self.user_email]) - self.prefs.save() - - createdSieveScript=self._getSogoSieveScript() - - self.assertEqual(sieveScript, createdSieveScript) - - def testSieveVacationIgnoreLists(self): - """ enable vacation script - ignore lists""" - vacation_msg="vacation test - ignore list" - daysSelect=2 - - sieveScript = sieve_vacation_ignoreLists % { "mailaddr": self.user_email, - "vacation_msg": vacation_msg, - "days": preferences.daysBetweenResponseList[daysSelect], - } - - # Enabling Vacation now is an 'enabled' setting in the subdict Vacation - # We need to get that subdict first -- next save/set will also save this - vacation = self.prefs.get("Vacation") - vacation['enabled'] = 1 - - self.prefs.set_nosave("autoReplyText", vacation_msg) - self.prefs.set_nosave("daysBetweenResponse", "%d" % preferences.daysBetweenResponseList[daysSelect]) - self.prefs.set_nosave("autoReplyEmailAddresses", [self.user_email]) - self.prefs.set_nosave("ignoreLists", 1) - self.prefs.save() - - createdSieveScript=self._getSogoSieveScript() - - self.assertEqual(sieveScript, createdSieveScript) - - def testSieveSimpleForward(self): - """ enable simple forwarding """ - redirect_mailaddr="nonexistent@inverse.com" - - sieveScript = sieve_simple_forward % { "redirect_mailaddr": redirect_mailaddr } - - # Enabling Forward now is an 'enabled' setting in the subdict Forward - # We need to get that subdict first -- next save/set will also save this - forward = self.prefs.get("Forward") - forward['enabled'] = 1 - - self.prefs.set("forwardAddress", [redirect_mailaddr]) - - createdSieveScript = self._getSogoSieveScript() - self.assertEqual(sieveScript, createdSieveScript) - - def testSieveForwardKeepCopy(self): - """ enable email forwarding - keep a copy """ - redirect_mailaddr="nonexistent@inverse.com" - - sieveScript = sieve_forward_keep % { "redirect_mailaddr": redirect_mailaddr } - - # Enabling Forward now is an 'enabled' setting in the subdict Forward - # We need to get that subdict first -- next save/set will also save this - forward = self.prefs.get("Forward") - forward['enabled'] = 1 - - self.prefs.set_nosave("forwardAddress", [redirect_mailaddr]) - self.prefs.set_nosave("keepCopy", 1) - self.prefs.save() - - createdSieveScript = self._getSogoSieveScript() - self.assertEqual(sieveScript, createdSieveScript) - - def testSieveSimpleFilter(self): - """ add simple sieve filter """ - folderName="Sent" - subject=__name__ - - sieveScript=sieve_simple_filter % { "subject": subject, "folderName": folderName } - - self.prefs.set("SOGoSieveFilters", [{"active": True, "actions": [{"method": "fileinto", "argument": "Sent"}], "rules": [{"operator": "contains", "field": "subject", "value": subject}], "match": "any", "name": folderName}]) - - createdSieveScript = self._getSogoSieveScript() - self.assertEqual(sieveScript, createdSieveScript) - - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-sogo-tool.py b/Tests/Integration/test-sogo-tool.py deleted file mode 100755 index edd902d72..000000000 --- a/Tests/Integration/test-sogo-tool.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/python - -# XXX this script has to be run as root because it su to sogo_user -# in order to use its .GNUstepDefaults prefs -# Would be much better to have another way to specify which Defaults to use - -from config import sogo_user, sogo_tool_path - -import os -import pwd -import shutil -import sogotests -import tempfile -import unittest - -class sogoToolTest(unittest.TestCase): - - def setUp(self): - self.backupdir = tempfile.mkdtemp() - - def tearDown(self): - os.chdir("/") - shutil.rmtree(self.backupdir) - - def testBackupAll(self): - """ sogo-tool backup ALL """ - - (uid, gid) = pwd.getpwnam(sogo_user)[2:4] - - # We need to run as root since there's no way - # of using another user's GNUstepDefaults - self.assertEqual(os.getuid(), 0, "this test must run as root...") - - os.chown(self.backupdir, uid, gid) - cmd = "sudo -u %s bash -c \"(cd %s && %s backup . ALL >/dev/null 2>&1)\"" % (sogo_user, self.backupdir, sogo_tool_path) - #print "sogo-tool cmd to execute %s" % cmd - status = os.system(cmd) - #print "Exit status of os.system(): %d" % status - rc = os.WEXITSTATUS(status) - #self.assertEqual(rc, 0, "sogo-tool failed RC=%d" % rc) - - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-ui-posts.py b/Tests/Integration/test-ui-posts.py deleted file mode 100644 index b745dccc7..000000000 --- a/Tests/Integration/test-ui-posts.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python - - -from config import hostname, port, username, password, \ - webCalendarURL - -import simplejson -import sogoLogin -import sogotests -import unittest -import utilities -import webdavlib -import httplib - - -class UIPostsTests(unittest.TestCase): - - def setUp(self): - self.client = webdavlib.WebDAVClient(hostname, port) - self.gcClient = webdavlib.WebDAVClient(hostname, port) - self.cookie = sogoLogin.getAuthCookie(hostname, port, username, password) - - def _urlPostData(self, client, url, data, exp_status=200): - post = webdavlib.HTTPPOST(url, simplejson.dumps(data)) - post.content_type = "application/json" - post.cookie = self.cookie - - client.execute(post) - if (exp_status is not None): - self.assertEquals(post.response["status"], exp_status) - return post.response - - def _urlGet(self, client, url, exp_status=200): - get = webdavlib.HTTPGET(url) - get.cookie = self.cookie - - client.execute(get) - if (exp_status is not None): - self.assertEquals(get.response["status"], exp_status) - return get.response - - def testAddWebCalendar(self): - """ Add Web Calendar """ - - ret=True - data = {"url":"%s" % webCalendarURL} - calendarBaseURL="/SOGo/so/%s/Calendar" % username - addWebCalendarURL = "%s/addWebCalendar" % calendarBaseURL - response = self._urlPostData(self.client, addWebCalendarURL, data) - - respJSON = simplejson.loads(response['body']) - calID = respJSON['id'] - - self.assertNotEqual(calID, None) - - # reload the cal - calURL = "%s/%s" % (calendarBaseURL, calID) - try: - response = self._urlGet(self.client, "%s/reload" % calURL, exp_status=None) - except httplib.BadStatusLine: - # that's bad, the server probably reset the connection. fake a 502 - response['status'] = 502 - - # cleanup our trash - self._urlPostData(self.gcClient, "%s/delete" % calURL, "", exp_status=None) - - # delayed assert to allow cal deletion on failure - self.assertEqual(response['status'], 200) - - - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-webdav.py b/Tests/Integration/test-webdav.py deleted file mode 100755 index f77e7ce1f..000000000 --- a/Tests/Integration/test-webdav.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password, subscriber_username - -import sogotests -import unittest -import utilities -import webdavlib - -class WebDAVTest(unittest.TestCase): - def __init__(self, arg): - unittest.TestCase.__init__(self, arg) - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - self.dav_utility = utilities.TestUtility(self, self.client) - - def testPrincipalCollectionSet(self): - """property: 'principal-collection-set' on collection object""" - resource = '/SOGo/dav/%s/' % username - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}principal-collection-set"], - 0) - self.client.execute(propfind) - self.assertEquals(propfind.response["status"], 207) - nodes = propfind.response["document"] \ - .findall('{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}principal-collection-set/{DAV:}href') - responseHref = nodes[0].text - if responseHref[0:4] == "http": - self.assertEquals("http://%s/SOGo/dav/" % hostname, responseHref, - "{DAV:}principal-collection-set returned %s instead of 'http../SOGo/dav/'" - % ( responseHref, resource )) - else: - self.assertEquals("/SOGo/dav/", responseHref, - "{DAV:}principal-collection-set returned %s instead of '/SOGo/dav/'" - % responseHref) - - def testPrincipalCollectionSet2(self): - """property: 'principal-collection-set' on non-collection object""" - resource = '/SOGo/dav/%s/freebusy.ifb' % username - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}principal-collection-set"], - 0) - self.client.execute(propfind) - self.assertEquals(propfind.response["status"], 207) - node = propfind.response["document"] \ - .find('{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}principal-collection-set/{DAV:}href') - responseHref = node.text - expectedHref = '/SOGo/dav/' - if responseHref[0:4] == "http": - self.assertEquals("http://%s%s" % (hostname, expectedHref), responseHref, - "{DAV:}principal-collection-set returned %s instead of '%s'" - % ( responseHref, expectedHref )) - else: - self.assertEquals(expectedHref, responseHref, - "{DAV:}principal-collection-set returned %s instead of '%s'" - % ( responseHref, expectedHref )) - - def _testPropfindURL(self, resource): - resourceWithSlash = resource[-1] == '/' - propfind = webdavlib.WebDAVPROPFIND(resource, - ["{DAV:}displayname", "{DAV:}resourcetype"], - 1) - self.client.execute(propfind) - self.assertEquals(propfind.response["status"], 207) - - nodes = propfind.response["document"].findall('{DAV:}response') - for node in nodes: - responseHref = node.find('{DAV:}href').text - hasSlash = responseHref[-1] == '/' - resourcetype = node.find('{DAV:}propstat/{DAV:}prop/{DAV:}resourcetype') - isCollection = len(resourcetype.getchildren()) > 0 - if isCollection: - self.assertEquals(hasSlash, resourceWithSlash, - "failure with href '%s' while querying '%s'" - % (responseHref, resource)) - else: - self.assertEquals(hasSlash, False, - "failure with href '%s' while querying '%s'" - % (responseHref, resource)) - - def testPropfindURL(self): - """propfind: ensure various NSURL work-arounds""" - # a collection without / - self._testPropfindURL('/SOGo/dav/%s' % username) - # a collection with / - self._testPropfindURL('/SOGo/dav/%s/' % username) - # a non-collection - self._testPropfindURL('/SOGo/dav/%s/freebusy.ifb' % username) - - ## REPORT - def testPrincipalPropertySearch(self): - """principal-property-search""" - resource = '/SOGo/dav' - userInfo = self.dav_utility.fetchUserInfo(username) - # subscriber_userInfo = self.dav_utility.fetchUserInfo(subscriber_username) - matches = [["{urn:ietf:params:xml:ns:caldav}calendar-home-set", - "/SOGo/dav/%s/Calendar" % username]] - ## the SOGo implementation does not support more than one - ## property-search at a time: - # ["{urn:ietf:params:xml:ns:caldav}calendar-home-set", - # "/SOGo/dav/%s/Calendar" % subscriber_username]] - query = webdavlib.WebDAVPrincipalPropertySearch(resource, - ["displayname"], matches) - self.client.execute(query) - self.assertEquals(query.response["status"], 207) - response = query.response["document"].findall('{DAV:}response')[0] - href = response.find('{DAV:}href').text - self.assertEquals("/SOGo/dav/%s/" % username, href) - displayname = response.find('{DAV:}propstat/{DAV:}prop/{DAV:}displayname') - value = displayname.text - if value is None: - value = "" - self.assertEquals(userInfo[0], value) - - # http://tools.ietf.org/html/rfc3253.html#section-3.8 - def testExpandProperty(self): - """expand-property""" - resource = '/SOGo/dav/%s/' % username - userInfo = self.dav_utility.fetchUserInfo(username) - - query_props = {"{DAV:}owner": { "{DAV:}href": resource, - "{DAV:}displayname": userInfo[0]}, - "{DAV:}principal-collection-set": { "{DAV:}href": "/SOGo/dav/", - "{DAV:}displayname": "SOGo"}} - query = webdavlib.WebDAVExpandProperty(resource, query_props.keys(), - ["displayname"]) - self.client.execute(query) - self.assertEquals(query.response["status"], 207) - - topResponse = query.response["document"].find('{DAV:}response') - topHref = topResponse.find('{DAV:}href') - self.assertEquals(resource, topHref.text) - for query_prop in query_props.keys(): - propResponse = topResponse.find('{DAV:}propstat/{DAV:}prop/%s' - % query_prop) - propHref = propResponse.find('{DAV:}response/{DAV:}href') - self.assertEquals(query_props[query_prop]["{DAV:}href"], - propHref.text, - "'%s', href mismatch: exp. '%s', got '%s'" - % (query_prop, - query_props[query_prop]["{DAV:}href"], - propHref.text)) - propDisplayname = propResponse.find('{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}displayname') - displayName = propDisplayname.text - if displayName is None: - displayName = "" - self.assertEquals(query_props[query_prop]["{DAV:}displayname"], - displayName, - "'%s', displayname mismatch: exp. '%s', got '%s'" - % (query_prop, - query_props[query_prop]["{DAV:}displayname"], - propDisplayname)) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-webdavlib.py b/Tests/Integration/test-webdavlib.py deleted file mode 100755 index 781448d14..000000000 --- a/Tests/Integration/test-webdavlib.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/python - -import sogotests -import unittest - -from webdavlib import * - -class HTTPUnparsedURLTest(unittest.TestCase): - def testURLParse(self): - fullURL = "http://username:password@hostname:123/folder/folder/object?param1=value1¶m2=value2" - testURL = HTTPUnparsedURL(fullURL) - self.assertEquals(testURL.protocol, "http") - self.assertEquals(testURL.username, "username") - self.assertEquals(testURL.password, "password") - self.assertEquals(testURL.hostname, "hostname") - self.assertEquals(testURL.port, "123") - self.assertEquals(testURL.path, "/folder/folder/object") - - exp_params = { "param1": "value1", - "param2": "value2" } - self.assertEquals(exp_params, testURL.parameters) - - pathURL = "/folder/folder/simplereference" - testURL = HTTPUnparsedURL(pathURL) - self.assertEquals(testURL.protocol, None) - self.assertEquals(testURL.username, None) - self.assertEquals(testURL.password, None) - self.assertEquals(testURL.hostname, None) - self.assertEquals(testURL.port, None) - self.assertEquals(testURL.path, "/folder/folder/simplereference") - - pathURL = "http://user:secret@bla.com/hooray" - testURL = HTTPUnparsedURL(pathURL) - self.assertEquals(testURL.protocol, "http") - self.assertEquals(testURL.username, "user") - self.assertEquals(testURL.password, "secret") - self.assertEquals(testURL.hostname, "bla.com") - self.assertEquals(testURL.port, None) - self.assertEquals(testURL.path, "/hooray") - - pathURL = "http://user@bla.com:80/hooray" - testURL = HTTPUnparsedURL(pathURL) - self.assertEquals(testURL.protocol, "http") - self.assertEquals(testURL.username, "user") - self.assertEquals(testURL.password, None) - self.assertEquals(testURL.hostname, "bla.com") - self.assertEquals(testURL.port, "80") - self.assertEquals(testURL.path, "/hooray") - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/test-webdavsync.py b/Tests/Integration/test-webdavsync.py deleted file mode 100755 index e84288136..000000000 --- a/Tests/Integration/test-webdavsync.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/python - -from config import hostname, port, username, password - -import math -import sys -import sogotests -import time -import unittest -import webdavlib - -resource = '/SOGo/dav/%s/Calendar/test-webdavsync/' % username - -class WebdavSyncTest(unittest.TestCase): - def setUp(self): - self.client = webdavlib.WebDAVClient(hostname, port, - username, password) - - def tearDown(self): - delete = webdavlib.WebDAVDELETE(resource) - self.client.execute(delete) - - def test(self): - """webdav sync""" - # missing tests: - # invalid tokens: negative, non-numeric, > current timestamp - # non-empty collections: token validity, status codes for added, - # modified and removed elements - - # preparation - mkcol = webdavlib.WebDAVMKCOL(resource) - self.client.execute(mkcol) - self.assertEquals(mkcol.response["status"], 201, - "preparation: failure creating collection (code != 201)") - - # test queries: - # empty collection: - # without a token (query1) - # with a token (query2) - # (when done, non-empty collection: - # without a token (query3) - # with a token (query4)) - - query1 = webdavlib.WebDAVSyncQuery(resource, None, [ "getetag" ]) - self.client.execute(query1) - self.assertEquals(query1.response["status"], 207, - ("query1: invalid status code: %d (!= 207)" - % query1.response["status"])) - token_node = query1.response["document"].find("{DAV:}sync-token") - # Implicit "assertion": we expect SOGo to return a token node, with a - # non-empty numerical value. Anything else will trigger an exception - token = int(token_node.text) - - self.assertTrue(token >= 0) - query1EndTime = int(math.ceil(query1.start + query1.duration)) - self.assertTrue(token <= query1EndTime, - "token = %d > query1EndTime = %d" % (token, query1EndTime)) - - # we make sure that any token is accepted when the collection is - # empty, but that the returned token differs - query2 = webdavlib.WebDAVSyncQuery(resource, "1234", [ "getetag" ]) - self.client.execute(query2) - self.assertEquals(query2.response["status"], 207) - token_node = query2.response["document"].find("{DAV:}sync-token") - self.assertTrue(token_node is not None, - "expected 'sync-token' tag") - token = int(token_node.text) - self.assertTrue(token > 0) - -if __name__ == "__main__": - sogotests.runTests() diff --git a/Tests/Integration/teststrings.m b/Tests/Integration/teststrings.m deleted file mode 100644 index 0e5bdd0c8..000000000 --- a/Tests/Integration/teststrings.m +++ /dev/null @@ -1,82 +0,0 @@ -/* teststrings.m - this file is part of SOGO - * - * Copyright (C) 2010 Inverse inc. - * - * Author: Wolfgang Sourdeau - * - * This file 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, or (at your option) - * any later version. - * - * This file 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; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include - -static int -performTest (char *filename) -{ - NSDictionary *testDict; - NSString *nsFilename; - int rc; - - nsFilename = [NSString stringWithFormat: @"%s", filename]; - NS_DURING - { - testDict = [NSDictionary dictionaryFromStringsFile: nsFilename]; - if ([testDict count] == 0) - { - NSLog (@"Bad or empty strings file"); - rc = 2; - testDict = nil; - } - else - rc = 0; - } - NS_HANDLER - { - NSLog (@"An exception was caught: %@", localException); - rc = 1; - testDict = nil; - } - NS_ENDHANDLER; - - return rc; -} - -int -main (int argc, char *argv[]) -{ - NSAutoreleasePool *pool; - int rc; - - pool = [NSAutoreleasePool new]; - - if (argc == 2) - { - rc = performTest (argv[1]); - } - else - { - NSLog (@"Usage: %s file.strings", argv[0]); - rc = 1; - } - - [pool release]; - - return rc; -} diff --git a/Tests/Integration/teststrings.sh b/Tests/Integration/teststrings.sh deleted file mode 100755 index 6d7c2b9c6..000000000 --- a/Tests/Integration/teststrings.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -TOPDIR=../.. -RC=0 - -if [ ! -f teststrings ] -then - make -fi - -for stringfile in ${TOPDIR}/*/*/*.lproj/Localizable.strings -do - ./obj/teststrings "$stringfile" > /dev/null - code=$? - if test $code -eq 0; - then - echo "$stringfile: passed"; - else - echo "$stringfile: FAILED (code: $code)"; - RC=$(($RC+$code)) - fi -done - -exit $RC diff --git a/Tests/Integration/utilities.py b/Tests/Integration/utilities.py deleted file mode 100644 index 850cb07a8..000000000 --- a/Tests/Integration/utilities.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/python - -import StringIO -import sys -import unittest -import vobject -import vobject.ics_diff -import webdavlib -import xml.sax.saxutils - -class ics_compare(): - - def __init__(self, event1, event2): - self.event1 = event1 - self.event2 = event2 - self.diffs = None - - def _vcalendarComponent(self, event): - event_component = None - for item in vobject.readComponents(event): - if item.name == "VCALENDAR": - event_component = item - return event_component - - def areEqual(self): - s_event1 = StringIO.StringIO(self.event1) - s_event2 = StringIO.StringIO(self.event2) - - event1_vcalendar = self._vcalendarComponent(s_event1) - if event1_vcalendar is None: - raise Exception("No VCALENDAR component in event1") - - event2_vcalendar = self._vcalendarComponent(s_event2) - if event2_vcalendar is None: - raise Exception("No VCALENDAR component in event2") - - self.diffs = vobject.ics_diff.diff(event1_vcalendar, event2_vcalendar) - if not self.diffs: - return True - else: - return False - - def textDiff(self): - saved_stdout = sys.stdout - out = StringIO.StringIO() - sys.stdout = out - try : - if self.diffs is not None: - for (left, right) in self.diffs: - left.prettyPrint() - right.prettyPrint() - finally: - sys.stdout = saved_stdout - - return out.getvalue().strip() - - -class TestUtility(): - def __init__(self, test, client, resource = None): - self.test = test - self.client = client - self.userInfo = {} - - def fetchUserInfo(self, login): - if not self.userInfo.has_key(login): - resource = "/SOGo/dav/%s/" % login - propfind = webdavlib.WebDAVPROPFIND(resource, - ["displayname", - "{urn:ietf:params:xml:ns:caldav}calendar-user-address-set"], - 0) - self.client.execute(propfind) - self.test.assertEquals(propfind.response["status"], 207) - common_tree = "{DAV:}response/{DAV:}propstat/{DAV:}prop" - name_nodes = propfind.response["document"] \ - .findall('%s/{DAV:}displayname' % common_tree) - email_nodes = propfind.response["document"] \ - .findall('%s/{urn:ietf:params:xml:ns:caldav}calendar-user-address-set/{DAV:}href' - % common_tree) - - if len(name_nodes[0].text) > 0: - displayName = name_nodes[0].text - else: - displayName = "" - self.userInfo[login] = (displayName, email_nodes[0].text) - - return self.userInfo[login] - -class TestACLUtility(TestUtility): - def __init__(self, test, client, resource): - TestUtility.__init__(self, test, client, resource) - self.resource = resource - - def _subscriptionOperation(self, subscribers, operation): - subscribeQuery = ("\n" - + "<%s" % operation - + " xmlns=\"urn:inverse:params:xml:ns:inverse-dav\"") - if (subscribers is not None): - subscribeQuery = (subscribeQuery - + " users=\"%s\"" % ",".join(subscribers)) - subscribeQuery = subscribeQuery + "/>" - post = webdavlib.HTTPPOST(self.resource, subscribeQuery) - post.content_type = "application/xml; charset=\"utf-8\"" - self.client.execute(post) - self.test.assertEquals(post.response["status"], 200, - "subscribtion failure to '%s' for '%s' (status: %d)" - % (self.resource, "', '".join(subscribers), - post.response["status"])) - - def subscribe(self, subscribers=None): - self._subscriptionOperation(subscribers, "subscribe") - - def unsubscribe(self, subscribers=None): - self._subscriptionOperation(subscribers, "unsubscribe") - - def rightsToSOGoRights(self, rights): - self.fail("subclass must implement this method") - - def setupRights(self, username, rights = None): - if rights is not None: - rights_str = "".join(["<%s/>" - % x for x in self.rightsToSOGoRights(rights) ]) - aclQuery = ("\n" - + "" - + "%s" % (xml.sax.saxutils.escape(username), - rights_str) - + "") - else: - aclQuery = ("\n" - + "" - + "" % xml.sax.saxutils.escape(username) - + "") - - post = webdavlib.HTTPPOST(self.resource, aclQuery) - post.content_type = "application/xml; charset=\"utf-8\"" - self.client.execute(post) - - if rights is None: - err_msg = ("rights modification: failure to remove entry (status: %d)" - % post.response["status"]) - else: - err_msg = ("rights modification: failure to set '%s' (status: %d)" - % (rights_str, post.response["status"])) - self.test.assertEquals(post.response["status"], 204, err_msg) - -# Calendar: -# rights: -# v: view all -# d: view date and time -# m: modify -# r: respond -# short rights notation: { "c": create, -# "d": delete, -# "pu": public, -# "pr": private, -# "co": confidential } -class TestCalendarACLUtility(TestACLUtility): - def rightsToSOGoRights(self, rights): - sogoRights = [] - if rights.has_key("c") and rights["c"]: - sogoRights.append("ObjectCreator") - if rights.has_key("d") and rights["d"]: - sogoRights.append("ObjectEraser") - - classes = { "pu": "Public", - "pr": "Private", - "co": "Confidential" } - rights_table = { "v": "Viewer", - "d": "DAndTViewer", - "m": "Modifier", - "r": "Responder" } - for k in classes.keys(): - if rights.has_key(k): - right = rights[k] - sogo_right = "%s%s" % (classes[k], rights_table[right]) - sogoRights.append(sogo_right) - - return sogoRights - -# Addressbook: -# short rights notation: { "c": create, -# "d": delete, -# "e": edit, -# "v": view } -class TestAddressBookACLUtility(TestACLUtility): - def rightsToSOGoRights(self, rights): - sogoRightsTable = { "c": "ObjectCreator", - "d": "ObjectEraser", - "v": "ObjectViewer", - "e": "ObjectEditor" } - - sogoRights = [] - for k in rights.keys(): - sogoRights.append(sogoRightsTable[k]) - - return sogoRights - - diff --git a/Tests/Integration/webdavlib.py b/Tests/Integration/webdavlib.py deleted file mode 100644 index 1fb4a6820..000000000 --- a/Tests/Integration/webdavlib.py +++ /dev/null @@ -1,580 +0,0 @@ -# webdavlib.py - A versatile WebDAV Python Library -# -# Copyright (C) 2009, 2010 Inverse inc. -# -# Author: Wolfgang Sourdeau -# -# webdavlib is free software; you can redistribute it and/or modify it under -# the terms of the GNU Lesser General Public License as published by the Free -# Software Foundation; either version 2, or (at your option) any later -# version. -# -# webdavlib 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 Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with webdavlib; see the file COPYING. If not, write to the Free -# Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, -# USA. - -import httplib -import re -import time -import xml.dom.expatbuilder -import xml.etree.cElementTree -import xml.sax.saxutils -import sys - -xmlns_dav = "DAV:" -xmlns_caldav = "urn:ietf:params:xml:ns:caldav" -xmlns_carddav = "urn:ietf:params:xml:ns:carddav" -xmlns_inversedav = "urn:inverse:params:xml:ns:inverse-dav" - -url_re = None - -class HTTPUnparsedURL: - def __init__(self, url): - self._parse(url) - - def _parse(self, url): - # ((proto)://((username(:(password)?)@)?hostname(:(port))))(path)? -# if url_re is None: - url_parts = url.split("?") - alpha_match = "[a-zA-Z0-9%\._-]+" - num_match = "[0-9]+" - pattern = ("((%s)://(((%s)(:(%s))?@)?(%s)(:(%s))?))?(/.*)" - % (alpha_match, alpha_match, alpha_match, - alpha_match, num_match)) - url_re = re.compile(pattern) - re_match = url_re.match(url_parts[0]) - if re_match is None: - raise Exception, "URL expression could not be parsed: %s" % url - - (trash, self.protocol, trash, trash, self.username, trash, - self.password, self.hostname, trash, self.port, self.path) = re_match.groups() - - self.parameters = {} - if len(url_parts) > 1: - param_elms = url_parts[1].split("&") - for param_pair in param_elms: - parameter = param_pair.split("=") - self.parameters[parameter[0]] = parameter[1] - -class WebDAVClient: - user_agent = "Mozilla/5.0" - - def __init__(self, hostname, port, username = None, password = "", - forcessl = False): - if int(port) == 443 or forcessl: - import M2Crypto.httpslib - self.conn = M2Crypto.httpslib.HTTPSConnection(hostname, int(port), - True) - else: - self.conn = httplib.HTTPConnection(hostname, port, True) - - if username is None: - self.simpleauth_hash = None - else: - self.simpleauth_hash = (("%s:%s" % (username, password)) - .encode('base64')[:-1]) - - def prepare_headers(self, query, body): - headers = { "User-Agent": self.user_agent } - if self.simpleauth_hash is not None: - headers["authorization"] = "Basic %s" % self.simpleauth_hash - if body is not None: - headers["content-length"] = len(body) - if query.__dict__.has_key("depth") and query.depth is not None: - headers["depth"] = query.depth - if query.__dict__.has_key("content_type"): - headers["content-type"] = query.content_type - if not query.__dict__.has_key("accept-language"): - headers["accept-language"] = 'en-us,en;q=0.5' - - query_headers = query.prepare_headers() - if query_headers is not None: - for key in query_headers.keys(): - headers[key] = query_headers[key] - - return headers - - def execute(self, query): - body = query.render() - - query.start = time.time() - self.conn.request(query.method, query.url, - body, self.prepare_headers(query, body)) - try: - query.set_response(self.conn.getresponse()) - except httplib.BadStatusLine, e: - print e - query.set_response(self.conn.getresponse()) - query.duration = time.time() - query.start - -class HTTPSimpleQuery: - method = None - - def __init__(self, url): - self.url = url - self.response = None - self.start = -1 - self.duration = -1 - - def prepare_headers(self): - return {} - - def render(self): - return None - - def set_response(self, http_response): - headers = {} - for rk, rv in http_response.getheaders(): - k = rk.lower() - headers[k] = rv - self.response = { "headers": headers, - "status": http_response.status, - "version": http_response.version, - "body": http_response.read() } - -class HTTPGET(HTTPSimpleQuery): - method = "GET" - cookie = None - - def prepare_headers (self): - headers = HTTPSimpleQuery.prepare_headers(self) - if self.cookie: - headers["Cookie"] = self.cookie - return headers - - -class HTTPOPTIONS(HTTPSimpleQuery): - method = "OPTIONS" - -class HTTPQuery(HTTPSimpleQuery): - def __init__(self, url): - HTTPSimpleQuery.__init__(self, url) - self.content_type = "application/octet-stream" - -class HTTPPUT(HTTPQuery): - method = "PUT" - - def __init__(self, url, content, - content_type="application/octet-stream", - exclusive=False): - HTTPQuery.__init__(self, url) - self.content = content - self.content_type = content_type - self.exclusive = exclusive - - def render(self): - return self.content - - def prepare_headers(self): - headers = HTTPQuery.prepare_headers(self) - if self.exclusive: - headers["if-none-match"] = "*" - - return headers - -class HTTPPOST(HTTPPUT): - method = "POST" - cookie = None - - def prepare_headers (self): - headers = HTTPPUT.prepare_headers(self) - if self.cookie: - headers["Cookie"] = self.cookie - return headers - - - -class WebDAVQuery(HTTPQuery): - method = None - - def __init__(self, url, depth = None): - HTTPQuery.__init__(self, url) - self.content_type = "application/xml; charset=\"utf-8\"" - self.depth = depth - self.ns_mgr = _WD_XMLNS_MGR() - self.top_node = None - - # helper for PROPFIND and REPORT (only) - def _initProperties(self, properties): - props = _WD_XMLTreeElement("prop") - self.top_node.append(props) - for prop in properties: - prop_tag = self.render_tag(prop) - props.append(_WD_XMLTreeElement(prop_tag)) - - def render(self): - if self.top_node is not None: - text = ("\n%s" - % self.top_node.render(self.ns_mgr.render())) - else: - text = "" - - return text - - def render_tag(self, tag): - cb = tag.find("}") - if cb > -1: - ns = tag[1:cb] - real_tag = tag[cb+1:] - new_tag = self.ns_mgr.register(real_tag, ns) - else: - new_tag = tag - - return new_tag - - def set_response(self, http_response): - HTTPQuery.set_response(self, http_response) - headers = self.response["headers"] - if (headers.has_key("content-type") - and headers.has_key("content-length") - and (headers["content-type"].startswith("application/xml") - or headers["content-type"].startswith("text/xml")) - and int(headers["content-length"]) > 0): - document = xml.etree.cElementTree.fromstring(self.response["body"]) - self.response["document"] = document - -class WebDAVMKCOL(WebDAVQuery): - method = "MKCOL" - -class WebDAVDELETE(WebDAVQuery): - method = "DELETE" - -class WebDAVREPORT(WebDAVQuery): - method = "REPORT" - -class WebDAVGET(WebDAVQuery): - method = "GET" - -class WebDAVPROPFIND(WebDAVQuery): - method = "PROPFIND" - - def __init__(self, url, properties, depth = None): - WebDAVQuery.__init__(self, url, depth) - self.top_node = _WD_XMLTreeElement("propfind") - if properties is not None and len(properties) > 0: - self._initProperties(properties) - -class WebDAVPROPPATCH(WebDAVQuery): - method = "PROPPATCH" - -# - - def __init__(self, url, properties): - WebDAVQuery.__init__(self, url, None) - self.top_node = _WD_XMLTreeElement("propertyupdate") - set_node = _WD_XMLTreeElement("set") - self.top_node.append(set_node) - prop_node = _WD_XMLTreeElement("prop") - set_node.append(prop_node) - - prop_node.appendSubtree(self, properties) - -class WebDAVMOVE(WebDAVQuery): - method = "MOVE" - destination = None - host = None - - def prepare_headers(self): - headers = WebDAVQuery.prepare_headers(self) - print "DESTINATION", self.destination - if self.destination is not None: - headers["Destination"] = self.destination - if self.host is not None: - headers["Host"] = self.host - return headers - -class WebDAVPrincipalPropertySearch(WebDAVREPORT): - def __init__(self, url, properties, matches): - WebDAVQuery.__init__(self, url) - ppsearch_tag = self.ns_mgr.register("principal-property-search", - xmlns_dav) - self.top_node = _WD_XMLTreeElement(ppsearch_tag) - self._initMatches(matches) - if properties is not None and len(properties) > 0: - self._initProperties(properties) - - def _initMatches(self, matches): - for match in matches: - psearch = _WD_XMLTreeElement("property-search") - self.top_node.append(psearch) - prop = _WD_XMLTreeElement("prop") - psearch.append(prop) - match_tag = self.render_tag(match[0]) - prop.append(_WD_XMLTreeElement(match_tag)) - match_tag = _WD_XMLTreeElement("match") - psearch.append(match_tag) - match_tag.appendSubtree(self, match[1]) - -class WebDAVSyncQuery(WebDAVREPORT): - def __init__(self, url, token, properties): - WebDAVQuery.__init__(self, url) - self.top_node = _WD_XMLTreeElement("sync-collection") - - sync_token = _WD_XMLTreeElement("sync-token") - self.top_node.append(sync_token) - if token is not None: - sync_token.append(_WD_XMLTreeTextNode(token)) - - if properties is not None and len(properties) > 0: - self._initProperties(properties) - -class WebDAVExpandProperty(WebDAVREPORT): - def _parseTag(self, tag): - result = [] - - cb = tag.find("}") - if cb > -1: - result.append(tag[cb+1:]) - result.append(tag[1:cb]) - else: - result.append(tag) - result.append("DAV:") - - return result; - - def _propElement(self, tag): - parsedTag = self._parseTag(tag) - parameters = { "name": parsedTag[0] } - if len(parsedTag) > 1: - parameters["namespace"] = parsedTag[1] - - return _WD_XMLTreeElement("property", parameters) - - def __init__(self, url, query_properties, properties): - WebDAVQuery.__init__(self, url) - self.top_node = _WD_XMLTreeElement("expand-property") - - for query_tag in query_properties: - property_query = self._propElement(query_tag) - self.top_node.append(property_query) - for tag in properties: - property = self._propElement(tag) - property_query.append(property) - -class CalDAVPOST(WebDAVQuery): - method = "POST" - - def __init__(self, url, content, - originator = None, recipients = None): - WebDAVQuery.__init__(self, url) - self.content_type = "text/calendar; charset=utf-8" - self.originator = originator - self.recipients = recipients - self.content = content - - def prepare_headers(self): - headers = WebDAVQuery.prepare_headers(self) - - if self.originator is not None: - headers["originator"] = self.originator - - if self.recipients is not None: - headers["recipient"] = ",".join(self.recipients) - - return headers - - def render(self): - return self.content - -class CalDAVCalendarMultiget(WebDAVREPORT): - def __init__(self, url, properties, hrefs, depth = None): - WebDAVQuery.__init__(self, url, depth) - multiget_tag = self.ns_mgr.register("calendar-multiget", xmlns_caldav) - self.top_node = _WD_XMLTreeElement(multiget_tag) - if properties is not None and len(properties) > 0: - self._initProperties(properties) - - for href in hrefs: - href_node = _WD_XMLTreeElement("href") - self.top_node.append(href_node) - href_node.append(_WD_XMLTreeTextNode(href)) - -class CalDAVCalendarQuery(WebDAVREPORT): - def __init__(self, url, properties, component = None, timerange = None): - WebDAVQuery.__init__(self, url) - multiget_tag = self.ns_mgr.register("calendar-query", xmlns_caldav) - self.top_node = _WD_XMLTreeElement(multiget_tag) - if properties is not None and len(properties) > 0: - self._initProperties(properties) - - if component is not None: - filter_tag = self.ns_mgr.register("filter", - xmlns_caldav) - compfilter_tag = self.ns_mgr.register("comp-filter", - xmlns_caldav) - filter_node = _WD_XMLTreeElement(filter_tag) - cal_filter_node = _WD_XMLTreeElement(compfilter_tag, - { "name": "VCALENDAR" }) - comp_node = _WD_XMLTreeElement(compfilter_tag, - { "name": component }) - ## TODO - # if timerange is not None: - cal_filter_node.append(comp_node) - filter_node.append(cal_filter_node) - self.top_node.append(filter_node) - -class CardDAVAddressBookQuery(WebDAVREPORT): - def __init__(self, url, properties, searchProperty = None, searchValue = None): - WebDAVQuery.__init__(self, url) - query_tag = self.ns_mgr.register("addressbook-query", xmlns_carddav) - ns_key = self.ns_mgr.xmlns[xmlns_carddav] - self.top_node = _WD_XMLTreeElement(query_tag) - if properties is not None and len(properties) > 0: - self._initProperties(properties) - - if searchProperty is not None: - filter_node = _WD_XMLTreeElement("%s:filter" % ns_key) - self.top_node.append(filter_node) - propfilter_node = _WD_XMLTreeElement("%s:prop-filter" % ns_key, { "name": searchProperty }) - filter_node.append(propfilter_node) - match_node = _WD_XMLTreeElement("%s:text-match" % ns_key, - { "collation": "i;unicasemap", "match-type": "starts-with" }) - propfilter_node.append(match_node) - match_node.appendSubtree(None, searchValue) - -class MailDAVMailQuery(WebDAVREPORT): - def __init__(self, url, properties, filters = None, - sort = None, ascending = True): - WebDAVQuery.__init__(self, url) - mailquery_tag = self.ns_mgr.register("mail-query", - xmlns_inversedav) - self.top_node = _WD_XMLTreeElement(mailquery_tag) - if properties is not None and len(properties) > 0: - self._initProperties(properties) - - if filters is not None and len(filters) > 0: - self._initFilters(filters) - - if sort is not None and len(sort) > 0: - self._initSort(sort, ascending) - - def _initFilters(self, filters): - mailfilter_tag = self.ns_mgr.register("mail-filters", - xmlns_inversedav) - mailfilter_node = _WD_XMLTreeElement(mailfilter_tag) - self.top_node.append(mailfilter_node) - for filterk in filters.keys(): - filter_tag = self.ns_mgr.register(filterk, - xmlns_inversedav) - filter_node = _WD_XMLTreeElement(filter_tag, - filters[filterk]) - mailfilter_node.append(filter_node) - - def _initSort(self, sort, ascending): - sort_tag = self.ns_mgr.register("sort", xmlns_inversedav) - if ascending: - sort_attrs = { "order": "ascending" } - else: - sort_attrs = { "order": "descending" } - sort_node = _WD_XMLTreeElement(sort_tag, sort_attrs) - self.top_node.append(sort_node) - - for item in sort: - sort_subnode = _WD_XMLTreeElement(self.render_tag(item)) - sort_node.append(sort_subnode) - -# private classes to handle XML stuff -class _WD_XMLNS_MGR: - def __init__(self): - self.xmlns = {} - self.counter = 0 - - def render(self): - text = " xmlns=\"DAV:\"" - for k in self.xmlns: - text = text + " xmlns:%s=\"%s\"" % (self.xmlns[k], k) - - return text - - def create_key(self, namespace): - new_nssym = "n%d" % self.counter - self.counter = self.counter + 1 - self.xmlns[namespace] = new_nssym - - return new_nssym - - def register(self, tag, namespace): - if namespace != xmlns_dav: - if self.xmlns.has_key(namespace): - key = self.xmlns[namespace] - else: - key = self.create_key(namespace) - else: - key = None - - if key is not None: - newTag = "%s:%s" % (key, tag) - else: - newTag = tag - - return newTag - -class _WD_XMLTreeElement: - typeNum = type(0) - typeStr = type("") - typeUnicode = type(u"") - typeList = type([]) - typeDict = type({}) - - def __init__(self, tag, attributes = {}): - self.tag = tag - self.children = [] - self.attributes = attributes - - def append(self, child): - self.children.append(child) - - def appendSubtree(self, query, subtree): - if type(subtree) == self.typeNum: - strValue = "%d" % subtree - textNode = _WD_XMLTreeTextNode(strValue) - self.append(textNode) - elif type(subtree) == self.typeUnicode: - textNode = _WD_XMLTreeTextNode(subtree.encode("utf-8")) - self.append(textNode) - elif type(subtree) == self.typeStr: - textNode = _WD_XMLTreeTextNode(subtree) - self.append(textNode) - elif type(subtree) == self.typeList: - for x in subtree: - self.appendSubtree(query, x) - elif type(subtree) == self.typeDict: - for x in subtree.keys(): - tag = query.render_tag(x) - node = _WD_XMLTreeElement(tag) - node.appendSubtree(query, subtree[x]) - self.append(node) - else: - None - - def render(self, ns_text = None): - text = "<" + self.tag - - if ns_text is not None: - text = text + ns_text - - for k in self.attributes: - text = text + " %s=\"%s\"" % (k, self.attributes[k]) - - if len(self.children) > 0: - text = text + ">" - for child in self.children: - text = text + child.render() - text = text + "" - else: - text = text + "/>" - - return text - -class _WD_XMLTreeTextNode: - def __init__(self, text): - self.text = xml.sax.saxutils.escape(text) - - def render(self): - return self.text diff --git a/Tests/Integration/webdavsync-tool.py b/Tests/Integration/webdavsync-tool.py deleted file mode 100644 index 8aa2a2729..000000000 --- a/Tests/Integration/webdavsync-tool.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/python - -import getopt -import sys -import urlparse -import webdavlib -import xml.dom.minidom - -def usage() : - msg ="""Usage: - %s [-h] [-s sync-token] -u uri\n""" % sys.argv[0] - - sys.stderr.write(msg); - -def getAllCollections(client, uri): - collections = [] - depth = 1 - - propfind = webdavlib.WebDAVPROPFIND(uri, ["allprop"], depth) - client.execute(propfind) - client.conn.close() - doc = propfind.response["document"] - for response in doc.iter("{DAV:}response"): - propstat = response.find("{DAV:}propstat") - if propstat is not None: - prop = propstat.find("{DAV:}prop") - if prop is not None: - resourcetype = prop.find("{DAV:}resourcetype") - if resourcetype.find("{DAV:}collection") is not None: - href = prop.find("{DAV:}href") - if href is not None and href.text != uri: - collections.append(href.text) - return collections - -def changedItemsFromCollection(client, collection, synctoken=None): - # get all changed hrefs since synctoken - hrefs = [] - syncquery = webdavlib.WebDAVSyncQuery(collection, synctoken, [ "getcontenttype", "getetag" ]) - client.execute(syncquery) - client.conn.close() - if (syncquery.response["status"] != 207): - raise Exception("Bad http response code: %d" % syncquery.response["status"]) - doc = syncquery.response["document"] - - # extract all hrefs - for syncResponse in doc.iter("{DAV:}response"): - href = syncResponse.find("{DAV:}href") - if href is not None: - hrefs.append(href.text) - - return hrefs - - -def main(): - depth = 1 - synctoken = "1" - url = None - - try: - opts, args = getopt.getopt (sys.argv[1:], "hs:u:", \ - ("sync-token=", "url=")); - except getopt.GetoptError: - usage() - exit(1) - - for o, v in opts : - if o == "-h" : - usage() - exit(1) - elif o == "-s" or o == "--sync-token" : - synctoken = v - elif o == "-u" or o == "--url" : - url = v - - if url is None: - usage() - exit(1) - - o = urlparse.urlparse(url) - hostname = o.hostname - port = o.port - username = o.username - password = o.password - uri = o.path - - client = webdavlib.WebDAVClient(hostname, port, username, password) - - collections = getAllCollections(client, uri) - if len(collections) == 0: - print "No collections found!" - sys.exit(1) - - for collection in collections: - changedItems = changedItemsFromCollection(client, collection) - # fetch the href data - if len(changedItems) > 0: - multiget = webdavlib.CalDAVCalendarMultiget(collection, - ["getetag", "{%s}calendar-data" % webdavlib.xmlns_caldav], - changedItems, depth) - client.execute(multiget) - client.conn.close() - if (multiget.response["status"] != 207): - raise Exception("Bad http response code: %d" % multiget.response["status"]) - -if __name__ == "__main__": - main() diff --git a/Tests/README b/Tests/README deleted file mode 100644 index 43988570b..000000000 --- a/Tests/README +++ /dev/null @@ -1,8 +0,0 @@ -This directory holds automated tests for SOGo. - -We currrently have: - - - Intregation holds all interated tests that are used to - validate overall DAV functionality right now - - - Unit holds all unit tests diff --git a/Tests/README.md b/Tests/README.md new file mode 100644 index 000000000..6fe1ee86d --- /dev/null +++ b/Tests/README.md @@ -0,0 +1,14 @@ +# Tests + +This directory holds automated tests for SOGo. + + - `spec` and `lib`: hold JavaScript driven interated tests that are used to validate overall DAV functionality + - `Unit`: holds all unit tests + +## Tools + +* [Jasmin](https://jasmine.github.io/) - testing framework +* [tsdav](https://tsdav.vercel.app/) - webdav request helper +* [ical.js](https://github.com/mozilla-comm/ical.js) - ics and vcard parser +* [cross-fetch](https://github.com/lquixada/cross-fetch) - fetch API +* [xml-js](https://github.com/nashwaan/xml-js) - convert JS object to XML diff --git a/packaging/debian/rules b/packaging/debian/rules index df577bd8d..7b1ed5f27 100755 --- a/packaging/debian/rules +++ b/packaging/debian/rules @@ -74,9 +74,6 @@ clean: dh_testdir dh_testroot rm -f build-arch-stamp - ( cd Tests/Integration; make clean ) - rm -f Tests/Integration/config.py - -find Tests -name "*.pyc" -exec rm -f {} \; if [ -f config.make ]; \ then \ $(MAKE) clean; \