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 #if defined(__APPLE__)
55 SetConnectionObject(conn);
59 struct curl_slist *connhd = NULL;
60 struct curl_slist *connhd2 = NULL;
61 struct curl_slist *connhd3 = NULL;
63 connhd = curl_slist_append(connhd, "Depth: 0");
64 connhd = curl_slist_append(connhd, "Prefer: return-minimal");
65 connhd = curl_slist_append(connhd, "Content-Type: application/xml; charset=utf-8");
67 connhd2 = curl_slist_append(connhd2, "Depth: 0");
68 connhd2 = curl_slist_append(connhd2, "Prefer: return-minimal");
69 connhd2 = curl_slist_append(connhd2, "Content-Type: application/xml; charset=utf-8");
71 connhd3 = curl_slist_append(connhd3, "Depth: 1");
72 connhd3 = curl_slist_append(connhd3, "Prefer: return-minimal");
73 connhd3 = curl_slist_append(connhd3, "Content-Type: application/xml; charset=utf-8");
75 struct CardDAVCURLPasser {
78 bool HeaderMode = TRUE;
80 } CardDAVHeader, CardDAVFooter;
82 CardDAVHeader.Data = this;
83 CardDAVHeader.HeaderMode = TRUE;
85 CardDAVFooter.Data = this;
86 CardDAVFooter.HeaderMode = FALSE;
92 wxString ETagOriginal;
95 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
96 ServerAddressSSL = wxT("https://") + ServerAddressURL;
97 ServerAddressNormal = wxT("http://") + ServerAddressURL;
99 ServerAuth = ServerUser + wxT(":") + ServerPass;
101 wxString SAURLPrincipals;
102 wxString SAURLPrincipalURL;
103 wxString SAURLAddressURL;
107 SAURLPrincipals = ServerAddressSSL + wxT("principals/");
108 SAURLPrincipalURL = ServerAddressSSL;
109 SAURLAddressURL = ServerAddressSSL;
113 SAURLPrincipals = ServerAddressNormal + wxT("principals/");
114 SAURLPrincipalURL = ServerAddressNormal;
115 SAURLAddressURL = ServerAddressNormal;
119 wxString FinalPrefix;
121 struct UploadDataStruc UploadData;
123 // Setup the first query finding out where the principal URL is.
125 const char* query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
126 "<D:propfind xmlns:D=\"DAV:\">\n"
128 " <D:current-user-principal/>\n"
132 // Setup the second query finding out where the address book home URL is.
134 const char* query2 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
135 "<D:propfind xmlns:D=\"DAV:\""
136 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
138 " <C:addressbook-home-set/>\n"
142 // Setup the third query finding out where the default address book URL is.
144 const char* query3 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
145 "<D:propfind xmlns:D=\"DAV:\""
146 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
148 " <C:default-addressbook-URL/>\n"
154 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
155 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
156 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
157 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
158 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
159 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
160 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
161 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
162 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
163 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
164 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
165 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
166 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
167 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
168 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
170 if (AllowSelfSign == TRUE){
171 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
172 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
175 #if defined(__APPLE__)
177 SetConnectionObject(conn);
181 conncode = (curl_easy_perform(conn));
183 if (conncode == CURLE_OK){
185 *ServerResult = TRUE;
187 ValidResponse = TRUE;
192 fprintf(stderr, "curl_easy_perform() failed: %s\n",
193 curl_easy_strerror(conncode));
195 *ServerResult = FALSE;
197 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
207 // Do an initial connection (incase of Digest authentication).
212 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
213 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
214 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
215 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
216 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
217 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
218 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
219 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
220 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
221 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
222 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
223 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
224 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
225 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
226 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
227 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
228 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
230 conncode = (curl_easy_perform(conn));
232 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
233 // then bring up the conflict resolution form.
235 if (EditMode == TRUE){
239 if (conncode == CURLE_OK){
241 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
243 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
245 fprintf(stderr, "curl_easy_perform() failed: %s\n",
246 curl_easy_strerror(conncode));
248 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
255 fprintf(stderr, "curl_easy_perform() failed: %s\n",
256 curl_easy_strerror(conncode));
263 // Process the XML data from the application.
265 xmlDocPtr xmlCardDAVDoc;
266 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
268 xmlNodePtr nodeLevel1;
269 xmlNodePtr nodeLevel2;
270 xmlNodePtr nodeLevel3;
271 xmlNodePtr nodeLevel4;
272 xmlNodePtr nodeLevel5;
273 xmlNodePtr nodeLevel6;
274 xmlNodePtr nodeLevel7;
276 for (nodeLevel1 = xmlCardDAVDoc->children;
278 nodeLevel1 = nodeLevel1->next)
281 for (nodeLevel2 = nodeLevel1->children;
283 nodeLevel2 = nodeLevel2->next)
287 for (nodeLevel3 = nodeLevel2->children;
289 nodeLevel3 = nodeLevel3->next)
292 for (nodeLevel4 = nodeLevel3->children;
294 nodeLevel4 = nodeLevel4->next)
297 for (nodeLevel5 = nodeLevel4->children;
299 nodeLevel5 = nodeLevel5->next)
302 for (nodeLevel6 = nodeLevel5->children;
304 nodeLevel6 = nodeLevel6->next)
307 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
308 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
309 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
312 // Found the <href> part so extract the principal URL address.
314 for (nodeLevel7 = nodeLevel6->children;
316 nodeLevel7 = nodeLevel7->next)
319 SAURLPrincipalURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
337 xmlFreeDoc(xmlCardDAVDoc);
341 // Second: Get the addressbook-home-set
343 curl_easy_reset(conn);
347 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
348 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
349 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
350 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
351 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
352 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
353 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
354 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
355 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
356 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
357 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
358 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
359 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
360 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
361 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
363 if (AllowSelfSign == TRUE){
364 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
365 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
368 #if defined(__APPLE__)
370 SetConnectionObject(conn);
374 conncode = (curl_easy_perform(conn));
376 if (conncode == CURLE_OK){
378 *ServerResult = TRUE;
384 fprintf(stderr, "curl_easy_perform() failed: %s\n",
385 curl_easy_strerror(conncode));
387 *ServerResult = FALSE;
388 ValidResponse = FALSE;
390 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
400 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
401 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
402 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
403 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
404 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
405 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
406 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
407 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
408 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
409 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
410 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
411 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
412 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
413 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
414 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
415 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
416 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
418 conncode = (curl_easy_perform(conn));
420 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
421 // then bring up the conflict resolution form.
423 if (EditMode == TRUE){
427 if (conncode == CURLE_OK){
429 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
431 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
433 fprintf(stderr, "curl_easy_perform() failed: %s\n",
434 curl_easy_strerror(conncode));
436 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
439 ValidResponse = FALSE;
445 fprintf(stderr, "curl_easy_perform() failed: %s\n",
446 curl_easy_strerror(conncode));
448 ValidResponse = FALSE;
456 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
458 for (nodeLevel1 = xmlCardDAVDoc->children;
460 nodeLevel1 = nodeLevel1->next)
463 for (nodeLevel2 = nodeLevel1->children;
465 nodeLevel2 = nodeLevel2->next)
469 for (nodeLevel3 = nodeLevel2->children;
471 nodeLevel3 = nodeLevel3->next)
474 for (nodeLevel4 = nodeLevel3->children;
476 nodeLevel4 = nodeLevel4->next)
479 for (nodeLevel5 = nodeLevel4->children;
481 nodeLevel5 = nodeLevel5->next)
484 for (nodeLevel6 = nodeLevel5->children;
486 nodeLevel6 = nodeLevel6->next)
489 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
490 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
491 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
494 // Found the <href> part so extract the principal URL address.
496 for (nodeLevel7 = nodeLevel6->children;
498 nodeLevel7 = nodeLevel7->next)
501 SAURLAddressURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
519 xmlFreeDoc(xmlCardDAVDoc);
523 // Finally: Get the default-addressbook-URL from the addressbook-home-set address.
525 curl_easy_reset(conn);
529 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
530 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
531 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
532 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
533 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
534 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
535 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
536 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
537 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
538 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
539 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
540 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
541 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
542 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
543 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
545 if (AllowSelfSign == TRUE){
546 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
547 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
550 #if defined(__APPLE__)
552 SetConnectionObject(conn);
556 conncode = (curl_easy_perform(conn));
558 if (conncode == CURLE_OK){
560 *ServerResult = TRUE;
566 fprintf(stderr, "curl_easy_perform() failed: %s\n",
567 curl_easy_strerror(conncode));
569 *ServerResult = FALSE;
570 ValidResponse = FALSE;
572 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
582 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
583 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
584 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
585 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
586 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
587 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
588 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
589 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
590 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
591 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
592 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
593 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
594 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
595 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
596 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
597 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
598 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
600 conncode = (curl_easy_perform(conn));
602 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
603 // then bring up the conflict resolution form.
605 if (EditMode == TRUE){
609 if (conncode == CURLE_OK){
611 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
613 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
615 fprintf(stderr, "curl_easy_perform() failed: %s\n",
616 curl_easy_strerror(conncode));
618 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
621 ValidResponse = FALSE;
627 fprintf(stderr, "curl_easy_perform() failed: %s\n",
628 curl_easy_strerror(conncode));
630 ValidResponse = FALSE;
638 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
640 for (nodeLevel1 = xmlCardDAVDoc->children;
642 nodeLevel1 = nodeLevel1->next)
645 for (nodeLevel2 = nodeLevel1->children;
647 nodeLevel2 = nodeLevel2->next)
651 for (nodeLevel3 = nodeLevel2->children;
653 nodeLevel3 = nodeLevel3->next)
656 for (nodeLevel4 = nodeLevel3->children;
658 nodeLevel4 = nodeLevel4->next)
661 for (nodeLevel5 = nodeLevel4->children;
663 nodeLevel5 = nodeLevel5->next)
666 for (nodeLevel6 = nodeLevel5->children;
668 nodeLevel6 = nodeLevel6->next)
671 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
672 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
673 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
676 // Found the <href> part so extract the principal URL address.
678 for (nodeLevel7 = nodeLevel6->children;
680 nodeLevel7 = nodeLevel7->next)
683 FinalPrefix = wxString::FromUTF8((const char*)nodeLevel7->content);
701 xmlFreeDoc(xmlCardDAVDoc);