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 // First: Get the principal UID address.
40 AbortConnection = FALSE;
44 wxString ServerAddressURL;
46 wxString ServerAddressSSL;
47 wxString ServerAddressNormal;
49 conn = curl_easy_init();
51 struct curl_slist *connhd = NULL;
52 struct curl_slist *connhd2 = NULL;
53 struct curl_slist *connhd3 = NULL;
55 connhd = curl_slist_append(connhd, "Depth: 0");
56 connhd = curl_slist_append(connhd, "Prefer: return-minimal");
57 connhd = curl_slist_append(connhd, "Content-Type: application/xml; charset=utf-8");
59 connhd2 = curl_slist_append(connhd2, "Depth: 0");
60 connhd2 = curl_slist_append(connhd2, "Prefer: return-minimal");
61 connhd2 = curl_slist_append(connhd2, "Content-Type: application/xml; charset=utf-8");
63 connhd3 = curl_slist_append(connhd3, "Depth: 1");
64 connhd3 = curl_slist_append(connhd3, "Prefer: return-minimal");
65 connhd3 = curl_slist_append(connhd3, "Content-Type: application/xml; charset=utf-8");
67 struct CardDAVCURLPasser {
70 bool HeaderMode = TRUE;
72 } CardDAVHeader, CardDAVFooter;
74 CardDAVHeader.Data = this;
75 CardDAVHeader.HeaderMode = TRUE;
77 CardDAVFooter.Data = this;
78 CardDAVFooter.HeaderMode = FALSE;
84 wxString ETagOriginal;
87 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
88 ServerAddressSSL = wxT("https://") + ServerAddressURL;
89 ServerAddressNormal = wxT("http://") + ServerAddressURL;
91 ServerAuth = ServerUser + wxT(":") + ServerPass;
93 wxString SAURLPrincipals;
94 wxString SAURLPrincipalURL;
95 wxString SAURLAddressURL;
99 SAURLPrincipals = ServerAddressSSL + wxT("principals/");
100 SAURLPrincipalURL = ServerAddressSSL;
101 SAURLAddressURL = ServerAddressSSL;
105 SAURLPrincipals = ServerAddressNormal + wxT("principals/");
106 SAURLPrincipalURL = ServerAddressNormal;
107 SAURLAddressURL = ServerAddressNormal;
111 wxString FinalPrefix;
113 struct UploadDataStruc UploadData;
115 // Setup the first query finding out where the principal URL is.
117 const char* query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
118 "<D:propfind xmlns:D=\"DAV:\">\n"
120 " <D:current-user-principal/>\n"
124 // Setup the second query finding out where the address book home URL is.
126 const char* query2 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
127 "<D:propfind xmlns:D=\"DAV:\""
128 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
130 " <C:addressbook-home-set/>\n"
134 // Setup the third query finding out where the default address book URL is.
136 const char* query3 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
137 "<D:propfind xmlns:D=\"DAV:\""
138 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
140 " <C:default-addressbook-URL/>\n"
146 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
147 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
148 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
149 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
150 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
151 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
152 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
153 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
154 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
155 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
156 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
157 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
158 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
159 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
160 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
162 if (AllowSelfSign == TRUE){
163 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
164 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
167 conncode = (curl_easy_perform(conn));
169 if (conncode == CURLE_OK){
171 *ServerResult = TRUE;
173 ValidResponse = TRUE;
178 fprintf(stderr, "curl_easy_perform() failed: %s\n",
179 curl_easy_strerror(conncode));
181 *ServerResult = FALSE;
183 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
193 // Do an initial connection (incase of Digest authentication).
198 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
199 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
200 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
201 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
202 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
203 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
204 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
205 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
206 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
207 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
208 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
209 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
210 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
211 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
212 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
213 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
214 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
216 conncode = (curl_easy_perform(conn));
218 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
219 // then bring up the conflict resolution form.
221 if (EditMode == TRUE){
225 if (conncode == CURLE_OK){
227 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
229 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
231 fprintf(stderr, "curl_easy_perform() failed: %s\n",
232 curl_easy_strerror(conncode));
234 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
241 fprintf(stderr, "curl_easy_perform() failed: %s\n",
242 curl_easy_strerror(conncode));
249 // Process the XML data from the application.
251 xmlDocPtr xmlCardDAVDoc;
252 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
254 xmlNodePtr nodeLevel1;
255 xmlNodePtr nodeLevel2;
256 xmlNodePtr nodeLevel3;
257 xmlNodePtr nodeLevel4;
258 xmlNodePtr nodeLevel5;
259 xmlNodePtr nodeLevel6;
260 xmlNodePtr nodeLevel7;
262 for (nodeLevel1 = xmlCardDAVDoc->children;
264 nodeLevel1 = nodeLevel1->next)
267 for (nodeLevel2 = nodeLevel1->children;
269 nodeLevel2 = nodeLevel2->next)
273 for (nodeLevel3 = nodeLevel2->children;
275 nodeLevel3 = nodeLevel3->next)
278 for (nodeLevel4 = nodeLevel3->children;
280 nodeLevel4 = nodeLevel4->next)
283 for (nodeLevel5 = nodeLevel4->children;
285 nodeLevel5 = nodeLevel5->next)
288 for (nodeLevel6 = nodeLevel5->children;
290 nodeLevel6 = nodeLevel6->next)
293 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
294 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
295 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
298 // Found the <href> part so extract the principal URL address.
300 for (nodeLevel7 = nodeLevel6->children;
302 nodeLevel7 = nodeLevel7->next)
305 SAURLPrincipalURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
323 xmlFreeDoc(xmlCardDAVDoc);
327 // Second: Get the addressbook-home-set
329 curl_easy_reset(conn);
333 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
334 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
335 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
336 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
337 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
338 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
339 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
340 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
341 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
342 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
343 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
344 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
345 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
346 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
347 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
349 if (AllowSelfSign == TRUE){
350 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
351 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
354 conncode = (curl_easy_perform(conn));
356 if (conncode == CURLE_OK){
358 *ServerResult = TRUE;
364 fprintf(stderr, "curl_easy_perform() failed: %s\n",
365 curl_easy_strerror(conncode));
367 *ServerResult = FALSE;
368 ValidResponse = FALSE;
370 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
380 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
381 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
382 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
383 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
384 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
385 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
386 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
387 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
388 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
389 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
390 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
391 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
392 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
393 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
394 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
395 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
396 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
398 conncode = (curl_easy_perform(conn));
400 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
401 // then bring up the conflict resolution form.
403 if (EditMode == TRUE){
407 if (conncode == CURLE_OK){
409 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
411 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
413 fprintf(stderr, "curl_easy_perform() failed: %s\n",
414 curl_easy_strerror(conncode));
416 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
419 ValidResponse = FALSE;
425 fprintf(stderr, "curl_easy_perform() failed: %s\n",
426 curl_easy_strerror(conncode));
428 ValidResponse = FALSE;
436 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
438 for (nodeLevel1 = xmlCardDAVDoc->children;
440 nodeLevel1 = nodeLevel1->next)
443 for (nodeLevel2 = nodeLevel1->children;
445 nodeLevel2 = nodeLevel2->next)
449 for (nodeLevel3 = nodeLevel2->children;
451 nodeLevel3 = nodeLevel3->next)
454 for (nodeLevel4 = nodeLevel3->children;
456 nodeLevel4 = nodeLevel4->next)
459 for (nodeLevel5 = nodeLevel4->children;
461 nodeLevel5 = nodeLevel5->next)
464 for (nodeLevel6 = nodeLevel5->children;
466 nodeLevel6 = nodeLevel6->next)
469 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
470 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
471 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
474 // Found the <href> part so extract the principal URL address.
476 for (nodeLevel7 = nodeLevel6->children;
478 nodeLevel7 = nodeLevel7->next)
481 SAURLAddressURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
499 xmlFreeDoc(xmlCardDAVDoc);
503 // Finally: Get the default-addressbook-URL from the addressbook-home-set address.
505 curl_easy_reset(conn);
509 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
510 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
511 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
512 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
513 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
514 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
515 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
516 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
517 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
518 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
519 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
520 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
521 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
522 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
523 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
525 if (AllowSelfSign == TRUE){
526 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
527 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
530 conncode = (curl_easy_perform(conn));
532 if (conncode == CURLE_OK){
534 *ServerResult = TRUE;
540 fprintf(stderr, "curl_easy_perform() failed: %s\n",
541 curl_easy_strerror(conncode));
543 *ServerResult = FALSE;
544 ValidResponse = FALSE;
546 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
556 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
557 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
558 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
559 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
560 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
561 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
562 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
563 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
564 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
565 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
566 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
567 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
568 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
569 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
570 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
571 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
572 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
574 conncode = (curl_easy_perform(conn));
576 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
577 // then bring up the conflict resolution form.
579 if (EditMode == TRUE){
583 if (conncode == CURLE_OK){
585 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
587 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
589 fprintf(stderr, "curl_easy_perform() failed: %s\n",
590 curl_easy_strerror(conncode));
592 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
595 ValidResponse = FALSE;
601 fprintf(stderr, "curl_easy_perform() failed: %s\n",
602 curl_easy_strerror(conncode));
604 ValidResponse = FALSE;
612 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
614 for (nodeLevel1 = xmlCardDAVDoc->children;
616 nodeLevel1 = nodeLevel1->next)
619 for (nodeLevel2 = nodeLevel1->children;
621 nodeLevel2 = nodeLevel2->next)
625 for (nodeLevel3 = nodeLevel2->children;
627 nodeLevel3 = nodeLevel3->next)
630 for (nodeLevel4 = nodeLevel3->children;
632 nodeLevel4 = nodeLevel4->next)
635 for (nodeLevel5 = nodeLevel4->children;
637 nodeLevel5 = nodeLevel5->next)
640 for (nodeLevel6 = nodeLevel5->children;
642 nodeLevel6 = nodeLevel6->next)
645 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
646 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
647 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
650 // Found the <href> part so extract the principal URL address.
652 for (nodeLevel7 = nodeLevel6->children;
654 nodeLevel7 = nodeLevel7->next)
657 FinalPrefix = wxString::FromUTF8((const char*)nodeLevel7->content);
675 xmlFreeDoc(xmlCardDAVDoc);