Example #1
0
    def doRequest(self, txn):

        # Generate an HTTP client request
        try:
            if "xpod" not in txn.logItems:
                txn.logItems["xpod"] = 0
            txn.logItems["xpod"] += 1

            response = (yield self._processRequest())

            if accountingEnabledForCategory("xPod"):
                self.loggedResponse = yield self.logResponse(response)
                emitAccounting("xPod", "",
                               self.loggedRequest + "\n" + self.loggedResponse,
                               "POST")

            if response.code in (responsecode.OK, ):
                data = (yield allDataFromStream(response.stream))
                data = json.loads(data)
            else:
                raise ValueError(
                    "Incorrect cross-pod response status code: {}".format(
                        response.code))

        except Exception as e:
            # Request failed
            log.error("Could not do cross-pod request : {request} {ex}",
                      request=self,
                      ex=e)
            raise ValueError("Failed cross-pod request: {}".format(e))

        returnValue(data)
Example #2
0
    def doRequest(self, txn):

        # Generate an HTTP client request
        try:
            if "xpod" not in txn.logItems:
                txn.logItems["xpod"] = 0
            txn.logItems["xpod"] += 1

            response = (yield self._processRequest())

            if accountingEnabledForCategory("xPod"):
                self.loggedResponse = yield self.logResponse(response)
                emitAccounting("xPod", "", self.loggedRequest + "\n" + self.loggedResponse, "POST")

            if response.code in (responsecode.OK,):
                data = (yield allDataFromStream(response.stream))
                data = json.loads(data)
            else:
                raise ValueError("Incorrect cross-pod response status code: {}".format(response.code))

        except Exception as e:
            # Request failed
            log.error("Could not do cross-pod request : {request} {ex}", request=self, ex=e)
            raise ValueError("Failed cross-pod request: {}".format(e))

        returnValue(data)
Example #3
0
 def doAccounting(self):
     #
     # Accounting
     #
     # Note that we associate logging with the organizer, not the
     # originator, which is good for looking for why something
     # shows up in a given principal's calendars, rather than
     # tracking the activities of a specific user.
     #
     if isinstance(self.organizer, LocalCalendarUser):
         accountingType = "iTIP-VFREEBUSY" if self.calendar.mainType(
         ) == "VFREEBUSY" else "iTIP"
         if accountingEnabled(accountingType, self.organizer.record):
             emitAccounting(
                 accountingType, self.organizer.record,
                 "Originator: {o}\nRecipients:\n{r}Method:{method}\n\n{cal}"
                 .format(
                     o=str(self.originator),
                     r=str("".join([
                         "    {}\n".format(recipient, )
                         for recipient in self.recipients
                     ])),
                     method=str(self.method),
                     cal=str(self.calendar),
                 ))
Example #4
0
    def finish(self):

        super(HTTPLoggingChannelRequest, self).finish()

        if self.logData is not None:
            doneTime = time.time()
            self.logData.response.append("\r\n\r\n<<<< Response complete at: %.3f (elapsed: %.1f ms)\r\n" % (doneTime, 1000 * (doneTime - self.startTime),))
            accounting.emitAccounting("HTTP", "", "".join(self.logData.request) + "".join(self.logData.response), self.command)
Example #5
0
    def test_file_instead_of_directory(self):
        """
        Test permissions when creating accounting
        """

        # Make log root a file
        config.AccountingLogRoot = "other"
        open(config.AccountingLogRoot, "w").close()
        emitAccounting("iTIP", self._Record("1234-5678"), "bogus")
Example #6
0
    def test_permissions_makedirs(self):
        """
        Test permissions when creating accounting
        """

        # Make log root non-writeable
        os.chmod(config.AccountingLogRoot, stat.S_IRUSR)

        emitAccounting("iTIP", self._Record("1234-5678"), "bogus")
 def test_file_instead_of_directory(self):
     """
     Test permissions when creating accounting
     """
     
     # Make log root a file
     config.AccountingLogRoot = os.path.abspath(self.mktemp())
     open(config.AccountingLogRoot, "w").close()
     emitAccounting("iTIP", self._Principal("1234-5678"), "bogus")
Example #8
0
    def doRequest(self, txn):

        # Generate an HTTP client request
        try:
            if "xpod" not in txn.logItems:
                txn.logItems["xpod"] = 0
            txn.logItems["xpod"] += 1

            response = (yield self._processRequest())

            if accountingEnabledForCategory("xPod"):
                self.loggedResponse = yield self.logResponse(response)
                emitAccounting("xPod", "",
                               self.loggedRequest + "\n" + self.loggedResponse,
                               "POST")

            if response.code == responsecode.OK:
                if self.writeStream is None:
                    data = (yield allDataFromStream(response.stream))
                    data = json.loads(data)
                else:
                    yield readStream(response.stream, self.writeStream.write)
                    content_type = response.headers.getHeader("content-type")
                    if content_type is None:
                        content_type = MimeType("application", "octet-stream")
                    content_disposition = response.headers.getHeader(
                        "content-disposition")
                    if content_disposition is None or "filename" not in content_disposition.params:
                        filename = ""
                    else:
                        filename = content_disposition.params["filename"]
                    self.writeStream.resetDetails(content_type, filename)
                    yield self.writeStream.loseConnection()
                    data = {
                        "result": "ok",
                        "content-type": content_type,
                        "name": filename,
                    }
            elif response.code == responsecode.BAD_REQUEST:
                data = (yield allDataFromStream(response.stream))
                data = json.loads(data)
            else:
                raise ValueError(
                    "Incorrect cross-pod response status code: {}".format(
                        response.code))

        except Exception as e:
            # Request failed
            log.error("Could not do cross-pod request : {request} {ex}",
                      request=self,
                      ex=e)
            raise ValueError("Failed cross-pod request: {}".format(e))

        returnValue(data)
Example #9
0
    def _logDiffError(self, title):

        strcal1 = str(self.oldcalendar)
        strcal2 = str(self.newcalendar)
        strdiff = "\n".join(unified_diff(
            strcal1.split("\n"),
            strcal2.split("\n"),
            fromfile='Existing Calendar Object',
            tofile='New Calendar Object',
        ))

        logstr = """%s

------ Existing Calendar Data ------
%s
------ New Calendar Data ------
%s
------ Diff ------
%s
""" % (title, strcal1, strcal2, strdiff,)

        loggedUID = self.oldcalendar.resourceUID()
        if loggedUID:
            loggedUID = loggedUID.encode("base64")[:-1]
        else:
            loggedUID = "Unknown"
        loggedName = accounting.emitAccounting("Implicit Errors", loggedUID, logstr)
        if loggedName:
            log.error("Generating Implicit Error accounting at path: %s" % (loggedName,))
Example #10
0
    def finish(self):

        super(HTTPLoggingChannelRequest, self).finish()

        if self.logData is not None:
            doneTime = time.time()
            self.logData.response.append(
                "\r\n\r\n<<<< Response complete at: %.3f (elapsed: %.1f ms)\r\n"
                % (
                    doneTime,
                    1000 * (doneTime - self.startTime),
                ))
            accounting.emitAccounting(
                "HTTP", "",
                "".join(self.logData.request) + "".join(self.logData.response),
                self.command)
Example #11
0
    def _logDiffError(self, title):

        strcal1 = str(self.oldcalendar)
        strcal2 = str(self.newcalendar)
        strdiff = "\n".join(unified_diff(
            strcal1.split("\n"),
            strcal2.split("\n"),
            fromfile='Existing Calendar Object',
            tofile='New Calendar Object',
        ))

        logstr = """%s

------ Existing Calendar Data ------
%s
------ New Calendar Data ------
%s
------ Diff ------
%s
""" % (title, strcal1, strcal2, strdiff,)

        loggedUID = self.oldcalendar.resourceUID()
        if loggedUID:
            loggedUID = loggedUID.encode("base64")[:-1]
        else:
            loggedUID = "Unknown"
        loggedName = accounting.emitAccounting("Implicit Errors", loggedUID, logstr)
        if loggedName:
            log.error("Generating Implicit Error accounting at path: {name}", name=loggedName)
Example #12
0
    def doRequest(self, txn):

        # Generate an HTTP client request
        try:
            if "xpod" not in txn.logItems:
                txn.logItems["xpod"] = 0
            txn.logItems["xpod"] += 1

            response = (yield self._processRequest())

            if accountingEnabledForCategory("xPod"):
                self.loggedResponse = yield self.logResponse(response)
                emitAccounting("xPod", "", self.loggedRequest + "\n" + self.loggedResponse, "POST")

            if response.code == responsecode.OK:
                if self.writeStream is None:
                    data = (yield allDataFromStream(response.stream))
                    data = json.loads(data)
                else:
                    yield readStream(response.stream, self.writeStream.write)
                    content_type = response.headers.getHeader("content-type")
                    if content_type is None:
                        content_type = MimeType("application", "octet-stream")
                    content_disposition = response.headers.getHeader("content-disposition")
                    if content_disposition is None or "filename" not in content_disposition.params:
                        filename = ""
                    else:
                        filename = content_disposition.params["filename"]
                    self.writeStream.resetDetails(content_type, filename)
                    yield self.writeStream.loseConnection()
                    data = {
                        "result": "ok",
                        "content-type": content_type,
                        "name": filename,
                    }
            elif response.code == responsecode.BAD_REQUEST:
                data = (yield allDataFromStream(response.stream))
                data = json.loads(data)
            else:
                raise ValueError("Incorrect cross-pod response status code: {}".format(response.code))

        except Exception as e:
            # Request failed
            log.error("Could not do cross-pod request : {request} {ex}", request=self, ex=e)
            raise ValueError("Failed cross-pod request: {}".format(e))

        returnValue(data)
Example #13
0
 def doAccounting(self):
     #
     # Accounting
     #
     # Note that we associate logging with the organizer, not the
     # originator, which is good for looking for why something
     # shows up in a given principal's calendars, rather than
     # tracking the activities of a specific user.
     #
     if isinstance(self.organizer, LocalCalendarUser):
         accountingType = "iTIP-VFREEBUSY" if self.calendar.mainType() == "VFREEBUSY" else "iTIP"
         if accountingEnabled(accountingType, self.organizer.record):
             emitAccounting(
                 accountingType,
                 self.organizer.record,
                 "Originator: {o}\nRecipients:\n{r}Method:{method}\n\n{cal}".format(
                     o=str(self.originator),
                     r=str("".join(["    {}\n".format(recipient,) for recipient in self.recipients])),
                     method=str(self.method),
                     cal=str(self.calendar),
                 )
             )
 def doAccounting(self):
     #
     # Accounting
     #
     # Note that we associate logging with the organizer, not the
     # originator, which is good for looking for why something
     # shows up in a given principal's calendars, rather than
     # tracking the activities of a specific user.
     #
     if isinstance(self.organizer, LocalCalendarUser):
         accountingType = "iTIP-VFREEBUSY" if self.calendar.mainType() == "VFREEBUSY" else "iTIP"
         if accountingEnabled(accountingType, self.organizer.principal):
             emitAccounting(
                 accountingType, self.organizer.principal,
                 "Originator: %s\nRecipients:\n%sServer Instance:%s\nMethod:%s\n\n%s"
                 % (
                     str(self.originator),
                     str("".join(["    %s\n" % (recipient,) for recipient in self.recipients])),
                     str(self.request.serverInstance),
                     str(self.method),
                     str(self.calendar),
                 )
             )
Example #15
0
    def doImplicitAttendeeCancel(self):
        """
        An iTIP CANCEL message has been sent to an attendee. If there is no existing resource, we will simply
        ignore the message. If there is an existing resource we need to reconcile the changes between it and the
        iTIP message.

        @return: C{tuple} of (processed, auto-processed, store inbox item, changes)
        """

        # If there is no existing copy, then ignore
        if self.recipient_calendar is None:
            log.debug("ImplicitProcessing - originator '%s' to recipient '%s' ignoring METHOD:CANCEL, UID: '%s' - attendee has no copy" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
            result = (True, True, True, None)
        else:
            # Need to check for auto-respond attendees. These need to suppress the inbox message
            # if the cancel is processed. However, if the principal is a user we always force the
            # inbox item on them even if auto-schedule is true so that they get a notification
            # of the cancel.
            organizer = normalizeCUAddr(self.message.getOrganizer())
            autoprocessed = yield self.recipient.record.canAutoSchedule(organizer=organizer)
            store_inbox = not autoprocessed or self.recipient.record.getCUType() == "INDIVIDUAL"

            # Check to see if this is a cancel of the entire event
            processed_message, delete_original, rids = iTipProcessing.processCancel(self.message, self.recipient_calendar, autoprocessing=autoprocessed)
            if processed_message:
                if autoprocessed and accountingEnabled("AutoScheduling", self.recipient.record):
                    accounting = {
                        "action": "cancel",
                        "when": DateTime.getNowUTC().getText(),
                        "deleting": delete_original,
                    }
                    emitAccounting(
                        "AutoScheduling",
                        self.recipient.record,
                        json.dumps(accounting) + "\r\n",
                        filename=self.uid.encode("base64")[:-1] + ".txt"
                    )

                if delete_original:

                    # Delete the attendee's copy of the event
                    log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:CANCEL, UID: '%s' - deleting entire event" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
                    yield self.deleteCalendarResource(self.recipient_calendar_resource)

                    # Build the schedule-changes XML element
                    changes = customxml.ScheduleChanges(
                        customxml.DTStamp(),
                        customxml.Action(
                            customxml.Cancel(),
                        ),
                    )
                    result = (True, autoprocessed, store_inbox, changes,)

                else:

                    # Update the attendee's copy of the event
                    log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:CANCEL, UID: '%s' - updating event" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
                    yield self.writeCalendarResource(None, self.recipient_calendar_resource, self.recipient_calendar)

                    # Build the schedule-changes XML element
                    if rids:
                        action = customxml.Cancel(
                            *[customxml.Recurrence(customxml.RecurrenceID.fromString(rid.getText())) for rid in sorted(rids)]
                        )
                    else:
                        action = customxml.Cancel()
                    changes = customxml.ScheduleChanges(
                        customxml.DTStamp(),
                        customxml.Action(action),
                    )
                    result = (True, autoprocessed, store_inbox, changes)
            else:
                log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:CANCEL, UID: '%s' - ignoring" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
                result = (True, True, False, None)

        returnValue(result)
Example #16
0
    def doImplicitAttendeeRequest(self):
        """
        An iTIP REQUEST message has been sent to an attendee. If there is no existing resource, we will simply
        create a new one. If there is an existing resource we need to reconcile the changes between it and the
        iTIP message.

        @return: C{tuple} of (processed, auto-processed, store inbox item, changes)
        """

        # If there is no existing copy, then look for default calendar and copy it here
        if self.new_resource:

            # Check if the incoming data has the recipient declined in all instances. In that case we will not create
            # a new resource as chances are the recipient previously deleted the resource and we want to keep it deleted.
            attendees = self.message.getAttendeeProperties((self.recipient.cuaddr,))
            if all([attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "DECLINED" for attendee in attendees]):
                log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:REQUEST, UID: '%s' - ignoring all declined" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
                returnValue((True, False, False, None,))

            # Check for default calendar
            default = (yield self.recipient.inbox.viewerHome().defaultCalendar(self.message.mainType()))
            if default is None:
                log.error("No default calendar for recipient: '%s'." % (self.recipient.cuaddr,))
                raise ImplicitProcessorException(iTIPRequestStatus.NO_USER_SUPPORT)

            log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:REQUEST, UID: '%s' - new processed" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
            new_calendar = iTipProcessing.processNewRequest(self.message, self.recipient.cuaddr, creating=True)

            # Handle auto-reply behavior
            organizer = normalizeCUAddr(self.message.getOrganizer())
            if (yield self.recipient.record.canAutoSchedule(organizer=organizer)):
                # auto schedule mode can depend on who the organizer is
                mode = yield self.recipient.record.getAutoScheduleMode(organizer=organizer)
                send_reply, store_inbox, partstat, accounting = (yield self.checkAttendeeAutoReply(new_calendar, mode))
                if accounting is not None:
                    accounting["action"] = "create"
                    emitAccounting(
                        "AutoScheduling",
                        self.recipient.record,
                        json.dumps(accounting) + "\r\n",
                        filename=self.uid.encode("base64")[:-1] + ".txt"
                    )

                # Only store inbox item when reply is not sent or always for users
                store_inbox = store_inbox or self.recipient.record.getCUType() == "INDIVIDUAL"
            else:
                send_reply = False
                store_inbox = True

            new_resource = (yield self.writeCalendarResource(default, None, new_calendar))

            if send_reply:
                # Track outstanding auto-reply processing
                log.debug("ImplicitProcessing - recipient '%s' processing UID: '%s' - auto-reply queued" % (self.recipient.cuaddr, self.uid,))
                ScheduleAutoReplyWork.autoReply(self.txn, new_resource, partstat)

            # Build the schedule-changes XML element
            changes = customxml.ScheduleChanges(
                customxml.DTStamp(),
                customxml.Action(
                    customxml.Create(),
                ),
            )
            result = (True, send_reply, store_inbox, changes,)
        else:
            # Processing update to existing event
            new_calendar, rids = iTipProcessing.processRequest(self.message, self.recipient_calendar, self.recipient.cuaddr)
            if new_calendar:

                # Handle auto-reply behavior
                organizer = normalizeCUAddr(self.message.getOrganizer())
                if (yield self.recipient.record.canAutoSchedule(organizer=organizer)) and not hasattr(self.txn, "doing_attendee_refresh"):
                    # auto schedule mode can depend on who the organizer is
                    mode = yield self.recipient.record.getAutoScheduleMode(organizer=organizer)
                    send_reply, store_inbox, partstat, accounting = (yield self.checkAttendeeAutoReply(new_calendar, mode))
                    if accounting is not None:
                        accounting["action"] = "modify"
                        emitAccounting(
                            "AutoScheduling",
                            self.recipient.record,
                            json.dumps(accounting) + "\r\n",
                            filename=self.uid.encode("base64")[:-1] + ".txt"
                        )

                    # Only store inbox item when reply is not sent or always for users
                    store_inbox = store_inbox or self.recipient.record.getCUType() == "INDIVIDUAL"
                else:
                    send_reply = False
                    store_inbox = True

                # Let the store know that no time-range info has changed for a refresh (assuming that
                # no auto-accept changes were made)
                if hasattr(self.txn, "doing_attendee_refresh"):
                    new_calendar.noInstanceIndexing = not send_reply

                # Update the attendee's copy of the event
                log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:REQUEST, UID: '%s' - updating event" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
                new_resource = (yield self.writeCalendarResource(None, self.recipient_calendar_resource, new_calendar))

                if send_reply:
                    # Track outstanding auto-reply processing
                    log.debug("ImplicitProcessing - recipient '%s' processing UID: '%s' - auto-reply queued" % (self.recipient.cuaddr, self.uid,))
                    ScheduleAutoReplyWork.autoReply(self.txn, new_resource, partstat)

                # Build the schedule-changes XML element
                update_details = []
                for rid, props_changed in sorted(rids.iteritems(), key=lambda x: x[0]):
                    recurrence = []
                    if rid is None:
                        recurrence.append(customxml.Master())
                    else:
                        recurrence.append(customxml.RecurrenceID.fromString(rid.getText()))
                    changes = []
                    for propName, paramNames in sorted(props_changed.iteritems(), key=lambda x: x[0]):
                        params = tuple([customxml.ChangedParameter(name=param) for param in paramNames])
                        changes.append(customxml.ChangedProperty(*params, **{"name": propName}))
                    recurrence.append(customxml.Changes(*changes))
                    update_details += (customxml.Recurrence(*recurrence),)

                changes = customxml.ScheduleChanges(
                    customxml.DTStamp(),
                    customxml.Action(
                        customxml.Update(*update_details),
                    ),
                )

                # Refresh from another Attendee should not have Inbox item
                if hasattr(self.txn, "doing_attendee_refresh"):
                    store_inbox = False

                result = (True, send_reply, store_inbox, changes,)

            else:
                # Request needs to be ignored
                log.debug("ImplicitProcessing - originator '%s' to recipient '%s' processing METHOD:REQUEST, UID: '%s' - ignoring" % (self.originator.cuaddr, self.recipient.cuaddr, self.uid))
                result = (True, True, False, None,)

        returnValue(result)