summaryrefslogtreecommitdiff
path: root/src/backends/tdepim/TDEPIMCalendarSource.cpp
blob: 128881601c68ee90c2977999b0a7421ee73c5c71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/*
 * Copyright (C) 2016 Emanoil Kotsev emanoil.kotsev@fincom.at
 *
 * This library 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.1 of the License, or (at your option) version 3.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 *
 *
 * $Id: TDEPIMCalendarSource.cpp,v 1.12 2016/09/12 19:57:27 emanoil Exp $
 *
 */


#include "TDEPIMCalendarSource.h"

#ifdef ENABLE_TDEPIMCAL

#include <syncevo/util.h>
#include <syncevo/Exception.h>
#include <syncevo/Logging.h>

#include <libkcal/icalformat.h>
//#include <libkcal/vcalformat.h>
#include <libkcal/calendarlocal.h>

SE_BEGIN_CXX

// "PRODID:-//K Desktop Environment//NONSGML libkcal 3.5//EN"
// "VERSION:2.0"

TDEPIMCalendarSource::TDEPIMCalendarSource( TDEPIMCalendarSourceType type, const SyncSourceParams &params ) : 
	TrackingSyncSource(params,1),
	m_type(type),
	calendarResPtr(0),
	calendarPtr(0)
{
 //NOTE m_typeName not really used right now
	switch (m_type) {
		case TDEPIM_TASKS:
			m_typeName = "calendar";
		break;
		case TDEPIM_TODO:
			m_typeName = "task list";
		break;
		case TDEPIM_JOURNAL:
			m_typeName = "memo list";
		break;
		default:
			Exception::throwError(SE_HERE, "internal init error, invalid calendar type");
		break;
	}
 //NOTE not really used
	m_contentMimeType = "text/calendar";

	app = new TDEPIMSyncSource("syncevo-tdepim-cal");

	calendarResPtr = new KCal::CalendarResources( "UTC" );
	if (!calendarResPtr) {
		Exception::throwError(SE_HERE, "internal error, can not open the default calendar");
	}

	calendarResPtr->readConfig();
	calendarResPtr->setModified(false);

	SyncSourceLogging::init(InitList<std::string>("SUMMARY") + "LOCATION", " ", m_operations);
// 	SE_LOG_DEBUG(getDisplayName(), "TDE calendar for %s (mime type: %s)", 
// 		m_typeName.latin1(), m_contentMimeType.latin1());
}

TDEPIMCalendarSource::~TDEPIMCalendarSource() {
	delete app;
}

TQString TDEPIMCalendarSource::lastModifiedNormalized(const KCal::Incidence *e)
{
	TQDateTime d = e->lastModified();
	// if no modification date is available, always return the same 0-time stamp
	// to avoid that 2 calls deliver different times which would be treated as changed entry
	// this would result in 1.1.1970
	if (!d.isValid())
		d.setTime_t(0);

	// We pass UTC, because we open the calendar in UTC
// 	return d.toString(TQt::ISODate); 
	return d.toString("yyyyMMddThhmmssZ");
}

TDEPIMCalendarSource::Databases TDEPIMCalendarSource::getDatabases()
{

	Databases result;
	bool first = true;

	KCal::CalendarResourceManager * mgr = calendarResPtr->resourceManager();
	/*
	 * we pull only active resources so the user has some freedom to decide 
	 * what he wants to be visible for sync this will result in setting
	 */
	for (KRES::Manager<KCal::ResourceCalendar>::ActiveIterator i = mgr->activeBegin(); i != mgr->activeEnd(); i++) {

		std::string name_str(( *i )->resourceName().utf8(),( *i )->resourceName().utf8().length());
		std::string path_str(( *i )->identifier().utf8(),( *i )->identifier().utf8().length());
// 		std::string info_str(( *i )->infoText( ).utf8(),( *i )->infoText( ).utf8().length());
// 		SE_LOG_DEBUG(getDisplayName(), "resource: NAME(%s), ID(%s), INFO(%s)", 
// 					name_str.c_str(), path_str.c_str(), info_str.c_str());

		result.push_back (
			Database ( 
				name_str,		// the name of the resource
				path_str,		// the path - (we use the resource uid)
				first,			// default or not
				( *i )->readOnly()	// read only or not
			)
		);
		first = false;
	}
	return result;
}

void TDEPIMCalendarSource::open()
{

	std::string id = getDatabaseID();
// 	SE_LOG_DEBUG(getDisplayName(), "Search for resource id: %s ", id.c_str() );

	KCal::CalendarResourceManager * mgr = calendarResPtr->resourceManager();
	/*
	 * we pull only active resources thus user has freedom to decide what wants to be visible for sync
	 */
	for (KRES::Manager<KCal::ResourceCalendar>::ActiveIterator i = mgr->activeBegin(); i != mgr->activeEnd(); i++) {
		std::string path_str(( *i )->identifier().utf8(),( *i )->identifier().utf8().length());
		if ( path_str == id ) {
// 			SE_LOG_DEBUG(getDisplayName(), "Resource id: %s found", path_str.c_str() );
			calendarPtr = ( *i ) ;
			break;
		}
	}

	if ( ! calendarPtr )
		Exception::throwError(SE_HERE, "internal error, calendar not found");

	if ( ! calendarPtr->load()  ) // TODO this fails on vcf resource
		Exception::throwError(SE_HERE, "internal error, calendar failed loading");
// 	SE_LOG_DEBUG(getDisplayName(), "Resource id: %s open OK", id.c_str() );
}

bool TDEPIMCalendarSource::isEmpty()
{

	bool status = true;
	switch (m_type) {
		case TDEPIM_TASKS:
			status =  calendarPtr->rawEvents( KCal::EventSortUnsorted , KCal::SortDirectionAscending ).isEmpty();
			break;
		case TDEPIM_TODO:	
			status =  calendarPtr->rawTodos( KCal::TodoSortUnsorted , KCal::SortDirectionAscending ).isEmpty();
			break;
		case TDEPIM_JOURNAL:
			status =  calendarPtr->rawJournals( KCal::JournalSortUnsorted , KCal::SortDirectionAscending ).isEmpty();
			break;
		default:
			Exception::throwError(SE_HERE, "internal error, invalid calendar type");
			break;
	}
	return status;
}

void TDEPIMCalendarSource::close()
{
	calendarResPtr->save();
	calendarPtr->close();
	delete calendarPtr;
	calendarPtr = 0;
}

void TDEPIMCalendarSource::listAllItems(SyncSourceRevisions::RevisionMap_t &revisions)
{
	KCal::Event::List e;
	KCal::Todo::List t;
	KCal::Journal::List j;
	TQString id;
	TQString lm;

	switch (m_type) {
	  case TDEPIM_TASKS:
		e = calendarPtr->rawEvents( KCal::EventSortUnsorted , KCal::SortDirectionAscending );
		for (KCal::Event::List::ConstIterator i = e.begin(); i != e.end(); i++) {
			id = (*i)->uid();
			lm = lastModifiedNormalized((*i));
			std::string id_str(id.utf8(),id.utf8().length());
			std::string lm_str(lm.utf8(),lm.utf8().length());
			revisions[id_str] = lm_str;
        		SE_LOG_DEBUG(getDisplayName(), "Event UID: %s last changed( %s )", id_str.c_str(), lm_str.c_str());
		}
		break;
	  case TDEPIM_TODO:
		t = calendarPtr->rawTodos( KCal::TodoSortUnsorted , KCal::SortDirectionAscending );
		for (KCal::Todo::List::ConstIterator i = t.begin(); i != t.end(); i++) {
			id = (*i)->uid();
			lm = lastModifiedNormalized((*i));
			std::string id_str(id.utf8(),id.utf8().length());
			std::string lm_str(lm.utf8(),lm.utf8().length());
			revisions[id_str] = lm_str;
        		SE_LOG_DEBUG(getDisplayName(), "Todos UID: %s last changed( %s )", id_str.c_str(), lm_str.c_str());
		}
		break;
	  case TDEPIM_JOURNAL:
		j = calendarPtr->rawJournals( KCal::JournalSortUnsorted , KCal::SortDirectionAscending );
		for (KCal::Journal::List::ConstIterator i = j.begin(); i != j.end(); i++) {
			id = (*i)->uid();
			lm = lastModifiedNormalized((*i));
			std::string id_str(id.utf8(),id.utf8().length());
			std::string lm_str(lm.utf8(),lm.utf8().length());
			revisions[id_str] = lm_str;
        		SE_LOG_DEBUG(getDisplayName(), "Journal UID: %s last changed( %s )", id_str.c_str(), lm_str.c_str());
		}
		break;
	  default:
		Exception::throwError(SE_HERE, "internal error, invalid calendar type");
		break;
	}
}

TrackingSyncSource::InsertItemResult TDEPIMCalendarSource::insertItem(const std::string &luid, const std::string &item, bool raw)
{

	InsertItemResultState state = ITEM_OKAY;
	TrackingSyncSource::InsertItemResult result;
	KCal::ICalFormat format;
	bool replaced = false;

	TQString uid  = TQString::fromUtf8(luid.data(), luid.size());
	TQString data = TQString::fromUtf8(item.data(), item.size());

	/*
	* Check if item already exists. If yes notify the engine and do nothing here
	*/
	KCal::Incidence *oldinc = calendarPtr->incidence(uid);
	if (oldinc) {
		if ( ! calendarPtr->deleteIncidence(oldinc))
				Exception::throwError(SE_HERE, "internal error, unable to delete item from calendar");
// If it was deleted we add it below no need to save now
// 		if ( ! calendarPtr->save() ) 
// 				Exception::throwError(SE_HERE, "internal error, unable to save calendar");
		SE_LOG_DEBUG(getDisplayName(), "Item deleted for merge: ( %s )", uid.latin1() );
		replaced = true;
// FIXME ufortunately the ITEM_NEEDS_MERGE does not work well with updated items
// 		std::string ret_uid(uid.utf8(), uid.utf8().length());
// 		return InsertItemResult(ret_uid, "", ITEM_NEEDS_MERGE);
	}

	/*
	 * Add to local calendar, so that we may set the uid
	 */
	KCal::CalendarLocal cal(TQString::fromLatin1( "UTC" ));
	if (! format.fromString(&cal,data) )
		Exception::throwError(SE_HERE, "internal error, unable to convert calendar data");

	/*
	 * Add the events from the temporary calendar
	 * We iterate over the list, but it should have only one event.
	 */
	KCal::Incidence::List itemList = cal.incidences();
	for (KCal::Incidence::List::ConstIterator i = itemList.begin(); i != itemList.end(); i++) {
		KCal::Incidence *e = (*i)->clone();
		if ( replaced )
			e->setUid( uid );
		else
			uid = e->uid();
		if ( ! calendarPtr->addIncidence(e))
			Exception::throwError(SE_HERE, "internal error, unable to add item to calendar");
		if ( ! calendarPtr->save(e) )
			Exception::throwError(SE_HERE, "internal error, unable to save item to calendar");
		SE_LOG_DEBUG(getDisplayName(), "Item saved: ( %s )", uid.latin1() );
	}
	calendarResPtr->setModified(true);

	KCal::Incidence *newinc = calendarPtr->incidence(uid);
	if ( ! newinc )
		Exception::throwError(SE_HERE, "internal error, unable to get item from calendar");

	TQString lm=lastModifiedNormalized(newinc);
	std::string ret_uid(newinc->uid().utf8(), newinc->uid().utf8().length());
	std::string ret_rev(lm.utf8(), lm.utf8().length());
	SE_LOG_DEBUG(getDisplayName(), "Item ( %s : %s ) done.", ret_uid.c_str() , ret_rev.c_str() );
   return InsertItemResult(ret_uid, ret_rev, state);
}

void TDEPIMCalendarSource::readItem(const std::string &luid, std::string &item, bool raw)
{
	KCal::ICalFormat iCalFmt;
//	NOTE: The libkcal vCal (v.1.0) does not work pretty well - the support is disabled for now
//	KCal::VCalFormat vCalFmt;
//	TQString data = "";

	TQString uid = TQString::fromUtf8(luid.data(),luid.size());
	
	/* Build a local calendar for the incidence data */
	KCal::CalendarLocal cal(TQString::fromLatin1( "UTC" ) /*calendarResPtr->timeZoneId()*/);

	switch (m_type) {
	  case TDEPIM_TASKS:
		cal.addIncidence(calendarPtr->event(uid)->clone());
	  break;
	  case TDEPIM_TODO:
		cal.addIncidence(calendarPtr->todo(uid)->clone());
	  break;
	  case TDEPIM_JOURNAL:
		cal.addIncidence(calendarPtr->journal(uid)->clone());
	  break;
	  default:
		Exception::throwError(SE_HERE, "internal error, invalid calendar type");
	  break;
	}

	// Convert the data to icalendar 
	TQString data = iCalFmt.toString( &cal );

// it should be possible to use data.utf8() directly, as it returns char array
// but no time to test so far
	std::string data_str( data.utf8(), data.utf8().length() );
	item.assign(data_str.c_str());
	SE_LOG_DEBUG(getDisplayName(), "Item id ( %s )", luid.c_str() );
// 	SE_LOG_DEBUG(getDisplayName(), "TDE calendar Data: %s\n", data_str.c_str() );
}


void TDEPIMCalendarSource::removeItem(const std::string &luid)
{
	KCal::Incidence *inc = calendarPtr->incidence(TQString::fromUtf8(luid.data(),luid.size()));
	if (inc) {
		calendarPtr->deleteIncidence(inc);
// 			Q: do we really need to save it here?
// 			A: yes definitely
// 			TODO implement save via ticket in future
		calendarPtr->save(); 
		calendarResPtr->setModified(true);
	}
	else
		SE_LOG_DEBUG(getDisplayName(), "Item not found: id=%s", luid.c_str() );
}


std::string TDEPIMCalendarSource::getDescription(const std::string &luid)
{
	KCal::Incidence *inc = calendarPtr->incidence(TQString::fromUtf8(luid.data(),luid.size()));
	if ( inc ) {
		std::string sum_str(inc->summary().utf8(),inc->summary().utf8().length());
		return sum_str;
	}
        SE_LOG_DEBUG(getDisplayName(), "Resource id(%s) not found", luid.c_str() );
	return "";
}


void TDEPIMCalendarSource::getSynthesisInfo(SynthesisInfo &info, XMLConfigFragments &fragments)
{
	TrackingSyncSource::getSynthesisInfo(info, fragments);
	info.m_backendRule = "TDE";
//	info.m_backendRule = "LOCALSTORAGE";
	info.m_beforeWriteScript = "";
//	info.m_profile = "\"vCalendar\", 2";
}


std::string TDEPIMCalendarSource::getMimeType() const
{
     return "text/calendar";
}

std::string TDEPIMCalendarSource::getMimeVersion() const
{
    return "2.0";
}

SE_END_CXX

#endif /* ENABLE_TDEPIMCAL */

#ifdef ENABLE_MODULES
# include "TDEPIMCalendarSourceRegister.cpp"
#endif