1 // carddav-defaultadrurl.cpp - CardDAV Object - Default Address URL subroutines.
3 // (c) 2012-2015 Xestia Software Development.
5 // This file is part of Xestia Address Book.
7 // Xestia Address Book is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by the
9 // Free Software Foundation, version 3 of the license.
11 // Xestia Address Book is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with Xestia Address Book. If not, see <http://www.gnu.org/licenses/>
20 #include "../version.h"
22 #include <wx/tokenzr.h>
24 #include <libxml/parser.h>
25 #include <libxml/tree.h>
28 #include "../vcard/vcard.h"
29 #include "../common/dirs.h"
31 wxString CardDAV::GetDefaultAddressBookURL(){
33 // Get the default address book URL.
40 AbortConnection = FALSE;
44 wxString ServerAddressURL;
46 wxString ServerAddressSSL;
47 wxString ServerAddressNormal;
49 // First: Get the principal UID address.
51 conn = curl_easy_init();
53 SetConnectionObject(conn);
55 struct curl_slist *connhd = NULL;
56 struct curl_slist *connhd2 = NULL;
57 struct curl_slist *connhd3 = NULL;
59 connhd = curl_slist_append(connhd, "Depth: 0");
60 connhd = curl_slist_append(connhd, "Prefer: return-minimal");
61 connhd = curl_slist_append(connhd, "Content-Type: application/xml; charset=utf-8");
63 connhd2 = curl_slist_append(connhd2, "Depth: 0");
64 connhd2 = curl_slist_append(connhd2, "Prefer: return-minimal");
65 connhd2 = curl_slist_append(connhd2, "Content-Type: application/xml; charset=utf-8");
67 connhd3 = curl_slist_append(connhd3, "Depth: 1");
68 connhd3 = curl_slist_append(connhd3, "Prefer: return-minimal");
69 connhd3 = curl_slist_append(connhd3, "Content-Type: application/xml; charset=utf-8");
71 struct CardDAVCURLPasser {
74 bool HeaderMode = TRUE;
76 } CardDAVHeader, CardDAVFooter;
78 CardDAVHeader.Data = this;
79 CardDAVHeader.HeaderMode = TRUE;
81 CardDAVFooter.Data = this;
82 CardDAVFooter.HeaderMode = FALSE;
88 wxString ETagOriginal;
91 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
92 ServerAddressSSL = wxT("https://") + ServerAddressURL;
93 ServerAddressNormal = wxT("http://") + ServerAddressURL;
95 ServerAuth = ServerUser + wxT(":") + ServerPass;
97 wxString SAURLPrincipals;
98 wxString SAURLPrincipalURL;
99 wxString SAURLAddressURL;
103 SAURLPrincipals = ServerAddressSSL + wxT("principals/");
104 SAURLPrincipalURL = ServerAddressSSL;
105 SAURLAddressURL = ServerAddressSSL;
109 SAURLPrincipals = ServerAddressNormal + wxT("principals/");
110 SAURLPrincipalURL = ServerAddressNormal;
111 SAURLAddressURL = ServerAddressNormal;
115 wxString FinalPrefix;
117 struct UploadDataStruc UploadData;
119 // Setup the first query finding out where the principal URL is.
121 const char* query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
122 "<D:propfind xmlns:D=\"DAV:\">\n"
124 " <D:current-user-principal/>\n"
128 // Setup the second query finding out where the address book home URL is.
130 const char* query2 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
131 "<D:propfind xmlns:D=\"DAV:\""
132 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
134 " <C:addressbook-home-set/>\n"
138 // Setup the third query finding out where the default address book URL is.
140 const char* query3 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
141 "<D:propfind xmlns:D=\"DAV:\""
142 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
144 " <C:default-addressbook-URL/>\n"
150 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
151 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
152 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
153 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
154 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
155 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
156 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
157 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
158 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
159 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
160 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
161 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
162 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
163 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
164 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
166 if (AllowSelfSign == TRUE){
167 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
168 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
171 SetConnectionObject(conn);
173 conncode = (curl_easy_perform(conn));
175 if (conncode == CURLE_OK){
177 *ServerResult = TRUE;
179 ValidResponse = TRUE;
184 fprintf(stderr, "curl_easy_perform() failed: %s\n",
185 curl_easy_strerror(conncode));
187 *ServerResult = FALSE;
189 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
199 // Do an initial connection (incase of Digest authentication).
204 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
205 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
206 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
207 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
208 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
209 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
210 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
211 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
212 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
213 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
214 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
215 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
216 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
217 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
218 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
219 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
220 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
222 conncode = (curl_easy_perform(conn));
224 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
225 // then bring up the conflict resolution form.
227 if (EditMode == TRUE){
231 if (conncode == CURLE_OK){
233 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
235 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
237 fprintf(stderr, "curl_easy_perform() failed: %s\n",
238 curl_easy_strerror(conncode));
240 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
247 fprintf(stderr, "curl_easy_perform() failed: %s\n",
248 curl_easy_strerror(conncode));
255 // Process the XML data from the application.
257 xmlDocPtr xmlCardDAVDoc;
258 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
260 xmlNodePtr nodeLevel1;
261 xmlNodePtr nodeLevel2;
262 xmlNodePtr nodeLevel3;
263 xmlNodePtr nodeLevel4;
264 xmlNodePtr nodeLevel5;
265 xmlNodePtr nodeLevel6;
266 xmlNodePtr nodeLevel7;
268 for (nodeLevel1 = xmlCardDAVDoc->children;
270 nodeLevel1 = nodeLevel1->next)
273 for (nodeLevel2 = nodeLevel1->children;
275 nodeLevel2 = nodeLevel2->next)
279 for (nodeLevel3 = nodeLevel2->children;
281 nodeLevel3 = nodeLevel3->next)
284 for (nodeLevel4 = nodeLevel3->children;
286 nodeLevel4 = nodeLevel4->next)
289 for (nodeLevel5 = nodeLevel4->children;
291 nodeLevel5 = nodeLevel5->next)
294 for (nodeLevel6 = nodeLevel5->children;
296 nodeLevel6 = nodeLevel6->next)
299 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
300 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
301 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
304 // Found the <href> part so extract the principal URL address.
306 for (nodeLevel7 = nodeLevel6->children;
308 nodeLevel7 = nodeLevel7->next)
311 SAURLPrincipalURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
329 xmlFreeDoc(xmlCardDAVDoc);
333 // Second: Get the addressbook-home-set
335 curl_easy_reset(conn);
339 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
340 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
341 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
342 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
343 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
344 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
345 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
346 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
347 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
348 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
349 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
350 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
351 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
352 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
353 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
355 if (AllowSelfSign == TRUE){
356 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
357 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
360 SetConnectionObject(conn);
362 conncode = (curl_easy_perform(conn));
364 if (conncode == CURLE_OK){
366 *ServerResult = TRUE;
372 fprintf(stderr, "curl_easy_perform() failed: %s\n",
373 curl_easy_strerror(conncode));
375 *ServerResult = FALSE;
376 ValidResponse = FALSE;
378 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
388 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
389 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
390 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
391 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
392 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
393 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
394 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
395 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
396 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
397 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
398 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
399 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
400 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
401 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
402 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
403 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
404 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
406 conncode = (curl_easy_perform(conn));
408 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
409 // then bring up the conflict resolution form.
411 if (EditMode == TRUE){
415 if (conncode == CURLE_OK){
417 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
419 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
421 fprintf(stderr, "curl_easy_perform() failed: %s\n",
422 curl_easy_strerror(conncode));
424 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
427 ValidResponse = FALSE;
433 fprintf(stderr, "curl_easy_perform() failed: %s\n",
434 curl_easy_strerror(conncode));
436 ValidResponse = FALSE;
444 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
446 for (nodeLevel1 = xmlCardDAVDoc->children;
448 nodeLevel1 = nodeLevel1->next)
451 for (nodeLevel2 = nodeLevel1->children;
453 nodeLevel2 = nodeLevel2->next)
457 for (nodeLevel3 = nodeLevel2->children;
459 nodeLevel3 = nodeLevel3->next)
462 for (nodeLevel4 = nodeLevel3->children;
464 nodeLevel4 = nodeLevel4->next)
467 for (nodeLevel5 = nodeLevel4->children;
469 nodeLevel5 = nodeLevel5->next)
472 for (nodeLevel6 = nodeLevel5->children;
474 nodeLevel6 = nodeLevel6->next)
477 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
478 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
479 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
482 // Found the <href> part so extract the principal URL address.
484 for (nodeLevel7 = nodeLevel6->children;
486 nodeLevel7 = nodeLevel7->next)
489 SAURLAddressURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
507 xmlFreeDoc(xmlCardDAVDoc);
511 // Finally: Get the default-addressbook-URL from the addressbook-home-set address.
513 curl_easy_reset(conn);
517 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
518 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
519 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
520 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
521 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
522 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
523 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
524 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
525 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
526 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
527 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
528 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
529 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
530 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
531 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
533 if (AllowSelfSign == TRUE){
534 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
535 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
538 SetConnectionObject(conn);
540 conncode = (curl_easy_perform(conn));
542 if (conncode == CURLE_OK){
544 *ServerResult = TRUE;
550 fprintf(stderr, "curl_easy_perform() failed: %s\n",
551 curl_easy_strerror(conncode));
553 *ServerResult = FALSE;
554 ValidResponse = FALSE;
556 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
566 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
567 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
568 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
569 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
570 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
571 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
572 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
573 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
574 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
575 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
576 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
577 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
578 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
579 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
580 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
581 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
582 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
584 conncode = (curl_easy_perform(conn));
586 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
587 // then bring up the conflict resolution form.
589 if (EditMode == TRUE){
593 if (conncode == CURLE_OK){
595 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
597 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
599 fprintf(stderr, "curl_easy_perform() failed: %s\n",
600 curl_easy_strerror(conncode));
602 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
605 ValidResponse = FALSE;
611 fprintf(stderr, "curl_easy_perform() failed: %s\n",
612 curl_easy_strerror(conncode));
614 ValidResponse = FALSE;
622 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
624 for (nodeLevel1 = xmlCardDAVDoc->children;
626 nodeLevel1 = nodeLevel1->next)
629 for (nodeLevel2 = nodeLevel1->children;
631 nodeLevel2 = nodeLevel2->next)
635 for (nodeLevel3 = nodeLevel2->children;
637 nodeLevel3 = nodeLevel3->next)
640 for (nodeLevel4 = nodeLevel3->children;
642 nodeLevel4 = nodeLevel4->next)
645 for (nodeLevel5 = nodeLevel4->children;
647 nodeLevel5 = nodeLevel5->next)
650 for (nodeLevel6 = nodeLevel5->children;
652 nodeLevel6 = nodeLevel6->next)
655 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
656 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
657 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
660 // Found the <href> part so extract the principal URL address.
662 for (nodeLevel7 = nodeLevel6->children;
664 nodeLevel7 = nodeLevel7->next)
667 FinalPrefix = wxString::FromUTF8((const char*)nodeLevel7->content);
685 xmlFreeDoc(xmlCardDAVDoc);