Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use FreeBusyQuery with caldavcollection? #86

Closed
signalarun opened this issue Aug 30, 2018 · 27 comments
Closed

How to use FreeBusyQuery with caldavcollection? #86

signalarun opened this issue Aug 30, 2018 · 27 comments

Comments

@signalarun
Copy link

Before submitting an issue, read the FAQ
then describe the issue using the following template

Reproduce

Steps to reproduce the issue:

  • caldav4j version 0.9.1
  • server name SOGO
  • link to code, including:
  • gists with proper highlight/indent, avoid inline code without highlight/indent
  • pull requests or git patches useful to reproduce the issue
  • link to HTTP trace (it's useful because not all servers implement ALL the specs)

I expect

What do you expect
No example found to use freebusyquery with caldavcollection

Instead

What happens instead?

Notes

Further notes you want to attach

@TheAntimist
Copy link
Member

Here's a quick example of how to use FreeQueryReport, without CalDAVCollection:

	HttpClient httpClient = new HttpClient();

	// Initialize the Query Report
	FreeBusyQuery fb = new FreeBusyQuery();
	Date start = new DateTime("20000101T000000Z"); // ical4j Date objects
	Date end = new DateTime("20000201T000000Z");
	fb.setTimeRange(new TimeRange(start, end)); // Set the time range

	// Print the XML to STDOUT
	System.out.println(XMLUtils.toPrettyXML(fb.createNewDocument()));

	// Initialize the method, with the report, query
	CalDAVReportMethod method = new CalDAVReportMethod("http://path/to/my/caldav/repo", fb);
	method.setCalendarBuilder(new CalendarBuilder()); // Set the CalendarBuilder, to build the response

	//Execute
	httpClient.executeMethod(method);

	// Return Response as iCal4j Calendar
	Calendar calendar = method.getResponseBodyAsCalendar();

Currently the CalDAVCollection does not have any support for freebusyqueries. I can though, add it in our next release as an enhancement.

TheAntimist added a commit that referenced this issue Aug 31, 2018
* Added a method to execute a Freebusyquery through CalDAVCollection. Enhancement for #86
* Fixed EnsureTrailingSlash, and used in CalDAVCredential
@ioggstream
Copy link
Member

Hi @signalarun, it would be great if you can find some time to make a pull request with the FreeBusy query example!

If your patch will be merged (after the code review/QA) you'll become a Caldav4j Contributor and could add that to your resume :)

@signalarun
Copy link
Author

@TheAntimist
Tried the above on servers SOGO and Baikal. Following are the details

Using Baikal
Resource url

/cal.php/calendars/<username>/<collection_name>
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<C:free-busy-query xmlns:C="urn:ietf:params:xml:ns:caldav">
  <C:time-range end="20181101T000000Z" start="20180801T000000Z"/>
</C:free-busy-query>

But the server returned http 500 status.

Using SOGO
Resource url

/SOGo/dav/<username>/Calendar/<collection_name>
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<C:free-busy-query xmlns:C="urn:ietf:params:xml:ns:caldav">
  <C:time-range end="20181101T000000Z" start="20180801T000000Z"/>
</C:free-busy-query>

But the server returned http 501 status.

@signalarun
Copy link
Author

@ioggstream " it would be great if you can find some time to make a pull request with the FreeBusy query example!" ? You meant to try with the given FreeBusyQueryFunctionalTest.java

@TheAntimist TheAntimist reopened this Sep 3, 2018
@TheAntimist
Copy link
Member

TheAntimist commented Sep 3, 2018

@signalrun, have you authenticated yourself? That could be an issue, but as far as I see it's a server issue. Does the server return anything other than the status code?

Does SoGo, support Free busy?

EDIT: Tested this against Baikal, another CalDAV server, and it works perfectly fine. Here's a quick sample of the calendar returned from my calendar:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Sabre//Sabre VObject 4.1.1//EN
CALSCALE:GREGORIAN
BEGIN:VFREEBUSY
DTSTART:20050101T000000Z
DTEND:20061231T000000Z
DTSTAMP:20180903T090926Z
FREEBUSY:20051103T170054Z/20051103T210054Z
FREEBUSY:20060101T000000Z/20060102T000000Z
FREEBUSY:20060102T220000Z/20060102T230000Z
FREEBUSY:20060103T220000Z/20060103T230000Z
END:VFREEBUSY
END:VCALENDAR

@signalarun
Copy link
Author

@TheAntimist authenticated using httpClient. Should I do anything additional to handle cookies.
I just initialised httpClient using name and password.

@TheAntimist
Copy link
Member

@signalarun I have been able to reproduce your issue, and have got successful responses in different scenarios. let me get back to you, when I have more info.

@TheAntimist
Copy link
Member

The main issue as it turned out was that we for some reason have to do a MKCALENDAR, and it works, without it it doesn't. In the code below, if instead of testcalendar, I used the default calendar it would not work. Seems to a server side issue.

@signalarun Here's a more comprehensive code which works in Baikal, the latest release:

import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.util.UidGenerator;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.osaf.caldav4j.methods.*;
import org.osaf.caldav4j.model.request.FreeBusyQuery;
import org.osaf.caldav4j.model.request.MkCalendar;
import org.osaf.caldav4j.model.request.TimeRange;

import java.net.SocketException;

public class Test {

	public static DateTime getToday() {
		return new DateTime();
	}

	public static Date getTomorrow() {
		java.util.Calendar calendar = java.util.Calendar.getInstance();
		calendar.add(java.util.Calendar.DATE, +1);
		return new DateTime(calendar.getTime());
	}

	public static Date getYesterday() {
		java.util.Calendar calendar = java.util.Calendar.getInstance();
		calendar.add(java.util.Calendar.DATE, -1);
		return new DateTime(calendar.getTime());
	}

	public static Calendar createiCalendar(String uid) throws SocketException {

		VEvent test = new VEvent(getToday(), getTomorrow(), "TEST EVENT");
		UidGenerator generator = new UidGenerator("123");
		test.getProperties().add(new Uid(uid));
		Calendar c = new Calendar();
		c.getComponents().add(test);
		return c;
	}
	public static void main(String[] args) throws Exception {
		String uri = "http://localhost:8080/cal.php/calendars/username/testcalendar/";

		HttpClient client = new HttpClient();
		// Set the Credentials for the calendar
		Credentials creds = new UsernamePasswordCredentials("username", "password"); 
		client.getState().setCredentials(AuthScope.ANY, creds);

		CalDAV4JMethodFactory factory = new CalDAV4JMethodFactory();
		String uid = "TESTUID";
		Calendar cal = createiCalendar(uid);

		//Create Calendar on server
		MkCalendarMethod mkCalendarMethod = new MkCalendarMethod(uri, "TEST Calendar", "TEST DESCRIPTION");
		client.executeMethod(mkCalendarMethod);
		System.out.println("Got Status for MKCALENDAR: " + mkCalendarMethod.getStatusLine());


		// Create iCalendar on server
		PutMethod putMethod = factory.createPutMethod();
		putMethod.setRequestBody(cal);
		putMethod.setPath(uri + uid + ".ics");

		client.executeMethod(putMethod);
		System.out.println("Got PUT Status: " + putMethod.getStatusCode());

		// Check if on server
		GetMethod getMethod = factory.createGetMethod();
		getMethod.setPath(uri + uid + ".ics");
		client.executeMethod(getMethod);
		System.out.println("Got response calendar as: ");
		Calendar resp = getMethod.getResponseBodyAsCalendar();
		System.out.println(resp);


		// TEST FreebusyQuery on server for the time period
		FreeBusyQuery query = new FreeBusyQuery();
		query.setTimeRange(new TimeRange(getYesterday(), getTomorrow()));


		CalDAVReportMethod method = factory.createCalDAVReportMethod(uri, query);

		client.executeMethod(method);
		System.out.println("For FreeBusyQuery got Status: " + method.getStatusLine());
		System.out.println("For FreeBusyQuery got Response calendar:\n" + method.getResponseBodyAsCalendar());

		// Delete iCal from server
		DeleteMethod deleteMethod = new DeleteMethod(uri + uid + ".ics");
		client.executeMethod(deleteMethod);
		System.out.println("For Delete iCalendar got response code: " + deleteMethod.getStatusLine());

		// Delete CalDAV Collection from server
		deleteMethod = new DeleteMethod(uri);
		client.executeMethod(deleteMethod);
		System.out.println("For Delete CalDAV Collection got response code: " + deleteMethod.getStatusLine());
	}
}

@ioggstream I have added a similar test to FreeBusyQueryTest, as well.

@signalarun
Copy link
Author

@TheAntimist
Getting the following

[main] INFO org.apache.commons.httpclient.auth.AuthChallengeProcessor - digest authentication scheme selected
Got Status for MKCALENDAR: HTTP/1.1 201 Created
[main] INFO net.fortuna.ical4j.util.Configurator - ical4j.properties not found.
[main] WARN org.apache.commons.httpclient.HttpMethodBase - Cookie rejected: "$Version=0; PHPSESSID=dfj82rfmie66fq2no75qi34363; $Path=/". Illegal path attribute "/". Path of origin: "http://localhost:8800/cal.php/calendars/joy/testcalendar3/TESTUID3.ics"
[main] INFO org.apache.commons.httpclient.auth.AuthChallengeProcessor - digest authentication scheme selected
[main] WARN org.apache.commons.httpclient.HttpMethodBase - Cookie rejected: "$Version=0; PHPSESSID=3pp2qjgcfah355rfue1hrs4db6; $Path=/". Illegal path attribute "/". Path of origin: "http://localhost:8800/cal.php/calendars/joy/testcalendar3/TESTUID3.ics"
Got PUT Status: 500

@TheAntimist
Copy link
Member

That's seems to be a authentication issue, the Cookie should have your path, so no idea where that issue is popping in from. Can't help you there.

@TheAntimist
Copy link
Member

@signalarun You could try:

httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY,
        CookiePolicy.BROWSER_COMPATIBILITY);

Based on here: https://stackoverflow.com/questions/7459279/httpclient-warning-cookie-rejected-illegal-domain-attribute

@signalarun
Copy link
Author

Tried with the same code as above. I'am using Baikal docker image.May be because of port mapping from 80 to 8800.
The above works fine for SOGO server. But freebusy query return 501, i.e. server may not support it.

@signalarun
Copy link
Author

@TheAntimist shall try with that.

@TheAntimist
Copy link
Member

@signalarun Sogo, could have a different address for Freebusy. From what I see it does implement FreeBusy, but it could be some other issue. For example, I was getting the Http 5xx, even when I was running the command from cURL or Python, so, it was something else. I suppose some trial and error is the only solution to find what works.

@signalarun
Copy link
Author

Working for Baikal after adding
For httpclient 3.1

		httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);

But parser exception

Got Status for MKCALENDAR: HTTP/1.1 201 Created
[main] INFO net.fortuna.ical4j.util.Configurator - ical4j.properties not found.
Got PUT Status: 201
Got response calendar as: 
Exception in thread "main" net.fortuna.ical4j.data.ParserException: Error at line 5:Expected [-3], read [58]
	at net.fortuna.ical4j.data.CalendarParserImpl.assertToken(CalendarParserImpl.java:462)
	at net.fortuna.ical4j.data.CalendarParserImpl.access$700(CalendarParserImpl.java:56)
	at net.fortuna.ical4j.data.CalendarParserImpl$ParameterParser.parse(CalendarParserImpl.java:328)
	at net.fortuna.ical4j.data.CalendarParserImpl$ParameterParser.access$1800(CalendarParserImpl.java:322)
	at net.fortuna.ical4j.data.CalendarParserImpl$ParameterListParser.parse(CalendarParserImpl.java:310)
	at net.fortuna.ical4j.data.CalendarParserImpl$PropertyParser.parse(CalendarParserImpl.java:247)
	at net.fortuna.ical4j.data.CalendarParserImpl$PropertyParser.access$1100(CalendarParserImpl.java:229)
	at net.fortuna.ical4j.data.CalendarParserImpl$PropertyListParser.parse(CalendarParserImpl.java:210)
	at net.fortuna.ical4j.data.CalendarParserImpl$ComponentParser.parse(CalendarParserImpl.java:422)
	at net.fortuna.ical4j.data.CalendarParserImpl$ComponentParser.access$900(CalendarParserImpl.java:406)
	at net.fortuna.ical4j.data.CalendarParserImpl$PropertyListParser.parse(CalendarParserImpl.java:208)
	at net.fortuna.ical4j.data.CalendarParserImpl.parseCalendar(CalendarParserImpl.java:115)
	at net.fortuna.ical4j.data.CalendarParserImpl.parseCalendarList(CalendarParserImpl.java:180)
	at net.fortuna.ical4j.data.CalendarParserImpl.parse(CalendarParserImpl.java:149)
	at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:198)
	at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:178)
	at net.fortuna.ical4j.data.CalendarBuilder.build(CalendarBuilder.java:165)
	at org.osaf.caldav4j.methods.GetMethod.getResponseBodyAsCalendar(GetMethod.java:89)
	at org.inst.calendar.InstCalendar.getFreeBusyTimeOfUser(InstCalendar.java:697)
	at CalendarApp.main(CalendarApp.java:222)

at line Calendar resp = getMethod.getResponseBodyAsCalendar();

@TheAntimist
Copy link
Member

TheAntimist commented Sep 5, 2018

Hmm, that's weird. Can you provide the output after adding these lines to your pom.xml, under dependencies:

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
			<version>1.7.25</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>

Add the following code snippet as log4j.xml, under the resources folder:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern"
             value="%p - %C{1}.%M(%L) | %m%n"/>
    </layout>
  </appender>

  <logger name="org.apache">
    <level value="WARN"/>
  </logger>

  <logger name="org.osaf">
    <level value="INFO"/>
  </logger>
  <logger name="httpclient.wire">
    <level value="DEBUG"/>
  </logger>
  <root>
    <level value="WARN"/>
    <appender-ref ref="CONSOLE"/>
  </root>

</log4j:configuration>

EDIT: I have updated this snippet to reflect the actual one.

@TheAntimist TheAntimist reopened this Sep 5, 2018
@signalarun
Copy link
Author

Shall do it.

For httpclient 3.1. this didnt helped me

		httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);

but worked after removing http://localhost from the method

putMethod.setPath("/SOGo/dav/joy/Calendar/testcalendarf/"+ uid + ".ics"); or
putMethod.setPath("/cal.php/calendars/shobana/testcalendard/"+ uid + ".ics");

@TheAntimist
Copy link
Member

That's certainly odd, because the host configuration hasn't been set in my code.

@signalarun
Copy link
Author

I tried after setting it up by

	
	private static void configure(HttpClient httpClient, Credential credential) {
		httpClient.getHostConfiguration().setHost(credential.getHost(), credential.getPort(), credential.getProtocol());
		Credentials httpCredentials = new UsernamePasswordCredentials(credential.getUser(), credential.getPassword());
		httpClient.getState().setCredentials(AuthScope.ANY, httpCredentials);
		httpClient.getParams().setAuthenticationPreemptive(true);
		
		// TODO Added for supporting Baikal
		//httpClient.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
		
	}
	

@TheAntimist
Copy link
Member

Okay, then, can I close this issue? Or do you have more concerns regarding this?

@signalarun
Copy link
Author

Yes, free busy query working fine for Baikal but not for SOGO as said above(may be server issue).
But when querying for FreeBusy I expected that the server will return freebusy of all the collections for the specific user in the server but it returns only the free busy of the connected single collection.

@TheAntimist
Copy link
Member

That seems to be server specific, so can't help there. Closing this issue, since it seems to be complete.

You could change the URI from collection to just the user, but not sure how that would work.

@signalarun
Copy link
Author

One thing which I found for Baikal is that if Collection is created from their UI Admin panel for a particular user then our FreeBusy query wont work for that particular collection. But if the same collection is created from a client then free busy query works. May be because of permission assignment issue.

@TheAntimist
Copy link
Member

Yes, I was able to reproduce the same on cURL and HttpClient and Python's Requests.

@signalarun
Copy link
Author

Is it normal that Caldav server return only the free busy of the connected collection only, when there are freebusy information in other collection of same user ?

@TheAntimist
Copy link
Member

Not sure, could be a server specific feature as I mentioned. Also, regarding Baikal's Http 500 status, could this help

@signalarun
Copy link
Author

Had tried this<You could change the URI from collection to just the user, but not sure how that would work.> but doesnt work as expected.

TheAntimist added a commit that referenced this issue Sep 21, 2018
* Added a method to execute a Freebusyquery through CalDAVCollection. Enhancement for #86
* Fixed EnsureTrailingSlash, and used in CalDAVCredential
TheAntimist pushed a commit that referenced this issue Jan 21, 2020
TheAntimist added a commit that referenced this issue Jan 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants