OwlCyberSecurity - MANAGER
Edit File: validcalendardata.py
#!/usr/bin/env python ## # Copyright (c) 2012-2017 Apple Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ## from __future__ import print_function """ This tool takes data from stdin and validates it as iCalendar data suitable for the server. """ from calendarserver.tools.cmdline import utilityMain, WorkerService from twisted.internet.defer import succeed from twisted.python.text import wordWrap from twisted.python.usage import Options from twistedcaldav.config import config from twistedcaldav.ical import Component, InvalidICalendarDataError from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE import os import sys def usage(e=None): if e: print(e) print("") try: ValidOptions().opt_help() except SystemExit: pass if e: sys.exit(64) else: sys.exit(0) description = '\n'.join( wordWrap( """ Usage: validcalendardata [options] [input specifiers]\n """, int(os.environ.get('COLUMNS', '80')) ) ) class ValidOptions(Options): """ Command-line options for 'validcalendardata' """ synopsis = description optFlags = [ ['verbose', 'v', "Verbose logging."], ['debug', 'D', "Debug logging."], ['parse-only', 'p', "Only validate parsing of the data."], ] optParameters = [ ['config', 'f', DEFAULT_CONFIG_FILE, "Specify caldavd.plist configuration path."], ] def __init__(self): super(ValidOptions, self).__init__() self.outputName = '-' self.inputName = '-' def opt_output(self, filename): """ Specify output file path (default: '-', meaning stdout). """ self.outputName = filename opt_o = opt_output def openOutput(self): """ Open the appropriate output file based on the '--output' option. """ if self.outputName == '-': return sys.stdout else: return open(self.outputName, "wb") def opt_input(self, filename): """ Specify output file path (default: '-', meaning stdin). """ self.inputName = filename opt_i = opt_input def openInput(self): """ Open the appropriate output file based on the '--input' option. """ if self.inputName == '-': return sys.stdin else: return open(os.path.expanduser(self.inputName), "rb") errorPrefix = "Calendar data had unfixable problems:\n " class ValidService(WorkerService, object): """ Service which runs, exports the appropriate records, then stops the reactor. """ def __init__(self, store, options, output, input, reactor, config): super(ValidService, self).__init__(store) self.options = options self.output = output self.input = input self.reactor = reactor self.config = config self._directory = None def doWork(self): """ Start the service. """ super(ValidService, self).startService() if self.options["parse-only"]: result, message = self.parseCalendarData() else: result, message = self.validCalendarData() if result: print("Calendar data OK") else: print(message) return succeed(None) def parseCalendarData(self): """ Check the calendar data for valid iCalendar data. """ result = True message = "" try: component = Component.fromString(self.input.read()) # Do underlying iCalendar library validation with data fix fixed, unfixed = component._pycalendar.validate(doFix=True) if unfixed: raise InvalidICalendarDataError("Calendar data had unfixable problems:\n %s" % ("\n ".join(unfixed),)) if fixed: print("Calendar data had fixable problems:\n %s" % ("\n ".join(fixed),)) except ValueError, e: result = False message = str(e) if message.startswith(errorPrefix): message = message[len(errorPrefix):] return (result, message,) def validCalendarData(self): """ Check the calendar data for valid iCalendar data. """ result = True message = "" truncated = False try: component = Component.fromString(self.input.read()) if getattr(self.config, "MaxInstancesForRRULE", 0) != 0: truncated = component.truncateRecurrence(config.MaxInstancesForRRULE) component.validCalendarData(doFix=False, validateRecurrences=True) component.validCalendarForCalDAV(methodAllowed=True) component.validOrganizerForScheduling(doFix=False) except ValueError, e: result = False message = str(e) if message.startswith(errorPrefix): message = message[len(errorPrefix):] if truncated: message = "Calendar data RRULE truncated\n" + message return (result, message,) def main(argv=sys.argv, stderr=sys.stderr, reactor=None): """ Do the export. """ if reactor is None: from twisted.internet import reactor options = ValidOptions() options.parseOptions(argv[1:]) try: output = options.openOutput() except IOError, e: stderr.write("Unable to open output file for writing: %s\n" % (e)) sys.exit(1) try: input = options.openInput() except IOError, e: stderr.write("Unable to open input file for reading: %s\n" % (e)) sys.exit(1) def makeService(store): return ValidService(store, options, output, input, reactor, config) utilityMain(options["config"], makeService, reactor, verbose=options["debug"]) if __name__ == "__main__": main()