vtodo.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <memory>
  19. #include "vtodo.h"
  20. #include <mapiutil.h>
  21. #include <kopano/mapiext.h>
  22. #include "nameids.h"
  23. #include <iostream>
  24. #include "icalmem.hpp"
  25. using namespace std;
  26. namespace KC {
  27. /**
  28. * VTodo constructor, implements VConverter
  29. */
  30. VTodoConverter::VTodoConverter(LPADRBOOK lpAdrBook, timezone_map *mapTimeZones, LPSPropTagArray lpNamedProps, const std::string& strCharset, bool blCensor, bool bNoRecipients, IMailUser *lpMailUser)
  31. : VConverter(lpAdrBook, mapTimeZones, lpNamedProps, strCharset, blCensor, bNoRecipients, lpMailUser)
  32. {
  33. }
  34. /**
  35. * Entrypoint to convert an ical object to MAPI object.
  36. *
  37. * @param[in] lpEventRoot The root component (VCALENDAR top object)
  38. * @param[in] lpEvent The VTODO object to convert
  39. * @param[in] lpPrevItem Optional previous (top) item to use when lpEvent describes an exception
  40. * @param[out] lppRet The icalitem struct to finalize into a MAPI object
  41. *
  42. * @return MAPI error code
  43. */
  44. HRESULT VTodoConverter::HrICal2MAPI(icalcomponent *lpEventRoot, icalcomponent *lpEvent, icalitem *lpPrevItem, icalitem **lppRet)
  45. {
  46. HRESULT hr = VConverter::HrICal2MAPI(lpEventRoot, lpEvent,
  47. lpPrevItem, lppRet);
  48. if (hr != hrSuccess)
  49. return hr;
  50. (*lppRet)->eType = VTODO;
  51. return hrSuccess;
  52. }
  53. /**
  54. * The properties set here are all required base properties for
  55. * different todo items and task requests.
  56. *
  57. * Finds the status of the message (eg. complete, cancelled) according
  58. * to the matching properties, or possebly the completion (in percent)
  59. * of the task. Lastly, the icon index (outlook icon displayed in list
  60. * view) is set.
  61. *
  62. * @param[in] icMethod Method of the ical event
  63. * @param[in] lpicEvent The ical VEVENT to convert
  64. * @param[in] base Used for the 'base' pointer for memory allocations
  65. * @param[in] bisException Weather we're handling an exception or not
  66. * @param[in,out] lstMsgProps
  67. *
  68. * @return MAPI error code
  69. */
  70. HRESULT VTodoConverter::HrAddBaseProperties(icalproperty_method icMethod, icalcomponent *lpicEvent, void *base, bool bIsException, std::list<SPropValue> *lplstMsgProps)
  71. {
  72. SPropValue sPropVal;
  73. icalproperty *lpicProp = NULL;
  74. bool bComplete = false;
  75. ULONG ulStatus = 0;
  76. // @todo fix exception message class
  77. HRESULT hr = HrCopyString(base, L"IPM.Task", &sPropVal.Value.lpszW);
  78. if (hr != hrSuccess)
  79. return hr;
  80. sPropVal.ulPropTag = PR_MESSAGE_CLASS_W;
  81. lplstMsgProps->push_back(sPropVal);
  82. // STATUS == COMPLETED
  83. // 0: olTaskNotStarted
  84. // 1: olTaskInProgress
  85. // 2: olTaskComplete
  86. // 3: olTaskWaiting (on someone else)
  87. // 4: olTaskDeferred
  88. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_STATUS_PROPERTY);
  89. if (lpicProp) {
  90. switch (icalproperty_get_status(lpicProp)) {
  91. case ICAL_STATUS_NEEDSACTION:
  92. ulStatus = 3;
  93. break;
  94. case ICAL_STATUS_INPROCESS:
  95. ulStatus = 1;
  96. break;
  97. case ICAL_STATUS_COMPLETED:
  98. bComplete = true;
  99. ulStatus = 2;
  100. break;
  101. case ICAL_STATUS_CANCELLED:
  102. ulStatus = 4;
  103. break;
  104. default:
  105. break;
  106. }
  107. }
  108. // ical.app 3.0.4 does not set status property, it only sets completed property
  109. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_COMPLETED_PROPERTY);
  110. if (lpicProp) {
  111. ulStatus = 2;
  112. bComplete = true;
  113. }
  114. // find PERCENT-COMPLETE
  115. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_PERCENTCOMPLETE_PROPERTY);
  116. if (lpicProp)
  117. sPropVal.Value.dbl = ((double)icalproperty_get_percentcomplete(lpicProp) / 100.0);
  118. else
  119. sPropVal.Value.dbl = 0.0;
  120. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_PERCENTCOMPLETE], PT_DOUBLE);
  121. lplstMsgProps->push_back(sPropVal);
  122. if (sPropVal.Value.dbl == 1.0) {
  123. bComplete = true;
  124. ulStatus = 2;
  125. }
  126. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_COMPLETE], PT_BOOLEAN);
  127. sPropVal.Value.b = bComplete;
  128. lplstMsgProps->push_back(sPropVal);
  129. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_STATUS], PT_LONG);
  130. sPropVal.Value.ul = ulStatus;
  131. lplstMsgProps->push_back(sPropVal);
  132. sPropVal.ulPropTag = PR_ICON_INDEX;
  133. // 1280: task
  134. // 1281: recurring task
  135. // 1282: delegate accepted task (?)
  136. // 1283: delegated task
  137. sPropVal.Value.ul = 1280;
  138. lplstMsgProps->push_back(sPropVal);
  139. // TODO: task delegation
  140. //if (icalcomponent_get_first_property(lpicEvent, ICAL_ATTENDEE_PROPERTY) == NULL)
  141. return hrSuccess;
  142. }
  143. /**
  144. * Set time properties in icalitem from the ical data. Sets the start, due and completed dates.
  145. *
  146. * @param[in] lpicEventRoot ical VCALENDAR component to set the timezone
  147. * @param[in] lpicEvent ical VTODO component
  148. * @param[in] bIsAllday set times for normal or allday event (unused for tasks)
  149. * @param[out] lpIcalItem icalitem structure in which mapi properties are set
  150. * @return MAPI error code
  151. * @retval MAPI_E_INVALID_PARAMETER start time or timezone not present in ical data
  152. */
  153. HRESULT VTodoConverter::HrAddTimes(icalproperty_method icMethod, icalcomponent *lpicEventRoot, icalcomponent *lpicEvent, bool bIsAllday, icalitem *lpIcalItem)
  154. {
  155. HRESULT hr = hrSuccess;
  156. SPropValue sPropVal;
  157. icalproperty* lpicProp = NULL;
  158. time_t timeDTStart = 0;
  159. time_t timeDue = 0;
  160. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_DTSTART_PROPERTY);
  161. if (lpicProp) {
  162. // Take the timezone from DTSTART and set that as the item timezone
  163. hr = HrAddTimeZone(lpicProp, lpIcalItem);
  164. if (hr != hrSuccess)
  165. return hr;
  166. // localStartTime
  167. timeDTStart = icaltime_as_timet(icalproperty_get_dtstart(lpicProp));
  168. // Set 0x820D / TaskStartDate
  169. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_STARTDATE], PT_SYSTIME);
  170. UnixTimeToFileTime(timeDTStart, &sPropVal.Value.ft);
  171. lpIcalItem->lstMsgProps.push_back(sPropVal);
  172. // utc starttime
  173. timeDTStart = ICalTimeTypeToUTC(lpicEventRoot, lpicProp);
  174. // Set 0x8516 / CommonStart
  175. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_COMMONSTART], PT_SYSTIME);
  176. UnixTimeToFileTime(timeDTStart, &sPropVal.Value.ft);
  177. lpIcalItem->lstMsgProps.push_back(sPropVal);
  178. }
  179. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_DUE_PROPERTY);
  180. if (lpicProp) {
  181. // Take the timezone from DUE and set that as the item timezone
  182. hr = HrAddTimeZone(lpicProp, lpIcalItem);
  183. if (hr != hrSuccess)
  184. return hr;
  185. // localduetime
  186. timeDue = icaltime_as_timet(icalproperty_get_due(lpicProp));
  187. // Set 0x820D / TaskDueDate
  188. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_DUEDATE], PT_SYSTIME);
  189. UnixTimeToFileTime(timeDue, &sPropVal.Value.ft);
  190. lpIcalItem->lstMsgProps.push_back(sPropVal);
  191. // utc duetime
  192. timeDue = ICalTimeTypeToUTC(lpicEventRoot, lpicProp);
  193. // Set 0x8516 / CommonEnd
  194. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_COMMONEND], PT_SYSTIME);
  195. UnixTimeToFileTime(timeDue, &sPropVal.Value.ft);
  196. lpIcalItem->lstMsgProps.push_back(sPropVal);
  197. }
  198. lpicProp = icalcomponent_get_first_property(lpicEvent, ICAL_COMPLETED_PROPERTY);
  199. if (lpicProp) {
  200. sPropVal.ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_COMPLETED_DATE], PT_SYSTIME);
  201. UnixTimeToFileTime(timeDue, &sPropVal.Value.ft);
  202. lpIcalItem->lstMsgProps.push_back(sPropVal);
  203. }
  204. return hrSuccess;
  205. }
  206. /**
  207. * Create a new ical VTODO component and set all ical properties in
  208. * the returned object.
  209. *
  210. * @param[in] lpMessage The message to convert
  211. * @param[out] lpicMethod The ical method of the top VCALENDAR object (hint, can differ when mixed methods are present in one VCALENDAR)
  212. * @param[out] lppicTZinfo ical timezone struct, describes all times used in this ical component
  213. * @param[out] lpstrTZid The name of the timezone
  214. * @param[out] lppEvent The ical task event
  215. *
  216. * @return MAPI error code
  217. */
  218. HRESULT VTodoConverter::HrMAPI2ICal(LPMESSAGE lpMessage, icalproperty_method *lpicMethod, icaltimezone **lppicTZinfo, std::string *lpstrTZid, icalcomponent **lppEvent)
  219. {
  220. icalcomp_ptr lpEvent(icalcomponent_new(ICAL_VTODO_COMPONENT));
  221. HRESULT hr = VConverter::HrMAPI2ICal(lpMessage, lpicMethod, lppicTZinfo,
  222. lpstrTZid, lpEvent.get());
  223. if (hr != hrSuccess)
  224. return hr;
  225. if (lppEvent)
  226. *lppEvent = lpEvent.release();
  227. return hrSuccess;
  228. }
  229. /**
  230. * Extends the VConverter version to set 'common start', 'common end'
  231. * and 'task completed' named properties.
  232. *
  233. * @param[in] lpMsgProps All (required) properties from the message to convert time properties
  234. * @param[in] ulMsgProps number of properties in lpMsgProps
  235. * @param[in] lpicTZinfo ical timezone object to set times in
  236. * @param[in] strTZid name of the given ical timezone
  237. * @param[in,out] lpEvent The Ical object to modify
  238. *
  239. * @return MAPI error code.
  240. */
  241. HRESULT VTodoConverter::HrSetTimeProperties(LPSPropValue lpMsgProps, ULONG ulMsgProps, icaltimezone *lpicTZinfo, const std::string &strTZid, icalcomponent *lpEvent)
  242. {
  243. HRESULT hr = VConverter::HrSetTimeProperties(lpMsgProps, ulMsgProps,
  244. lpicTZinfo, strTZid, lpEvent);
  245. if (hr != hrSuccess)
  246. return hr;
  247. // vtodo extra
  248. // Uses CommonStart/CommonEnd as its stores UTC time
  249. // Set start time / DTSTART
  250. auto lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_COMMONSTART], PT_SYSTIME));
  251. if (lpPropVal != NULL) {
  252. time_t ttTime = FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime);
  253. hr = HrSetTimeProperty(ttTime, false, lpicTZinfo, strTZid, ICAL_DTSTART_PROPERTY, lpEvent);
  254. if (hr != hrSuccess)
  255. return hr;
  256. }
  257. // Set end time / DUE
  258. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_COMMONEND], PT_SYSTIME));
  259. if (lpPropVal) {
  260. time_t ttTime = FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime);
  261. hr = HrSetTimeProperty(ttTime, false, lpicTZinfo, strTZid, ICAL_DUE_PROPERTY, lpEvent);
  262. if (hr != hrSuccess)
  263. return hr;
  264. }
  265. // else duration ?
  266. // Set Completion time
  267. lpPropVal = PCpropFindProp(lpMsgProps, ulMsgProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_COMPLETED_DATE], PT_SYSTIME));
  268. if (lpPropVal) {
  269. time_t ttTime = FileTimeToUnixTime(lpPropVal->Value.ft.dwHighDateTime, lpPropVal->Value.ft.dwLowDateTime);
  270. hr = HrSetTimeProperty(ttTime, false, lpicTZinfo, strTZid, ICAL_COMPLETED_PROPERTY, lpEvent);
  271. if (hr != hrSuccess)
  272. return hr;
  273. }
  274. return hrSuccess;
  275. }
  276. /**
  277. * Sets some task only specific properties in the ical object. Sets
  278. * the following ical properties:
  279. * - PERCENT-COMPLETE
  280. * - STATUS
  281. *
  282. * @param[in] ulProps Number of properties in lpProps
  283. * @param[in] lpProps Properties of the message to convert
  284. * @param[in,out] lpicEvent The ical object to modify
  285. *
  286. * @return Always return hrSuccess
  287. */
  288. HRESULT VTodoConverter::HrSetItemSpecifics(ULONG ulProps, LPSPropValue lpProps, icalcomponent *lpicEvent)
  289. {
  290. HRESULT hr = hrSuccess;
  291. double pc = 0.0;
  292. ULONG ulStatus = 0;
  293. auto lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_PERCENTCOMPLETE], PT_DOUBLE));
  294. if (lpPropVal)
  295. pc = lpPropVal->Value.dbl;
  296. icalcomponent_add_property(lpicEvent, icalproperty_new_percentcomplete(pc * 100));
  297. lpPropVal = PCpropFindProp(lpProps, ulProps, CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TASK_STATUS], PT_LONG));
  298. if (lpPropVal)
  299. ulStatus = lpPropVal->Value.ul;
  300. switch (ulStatus) {
  301. case 1: // olTaskInProgress
  302. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_INPROCESS));
  303. break;
  304. case 2: // olTaskCompleted
  305. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_COMPLETED));
  306. break;
  307. case 3: // olTaskNeedsAction
  308. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_NEEDSACTION));
  309. break;
  310. case 4: // olTaskDeferred
  311. icalcomponent_add_property(lpicEvent, icalproperty_new_status(ICAL_STATUS_CANCELLED));
  312. break;
  313. }
  314. return hr;
  315. }
  316. } /* namespace */