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 struct curl_slist *connhd = NULL;
54 struct curl_slist *connhd2 = NULL;
55 struct curl_slist *connhd3 = NULL;
57 connhd = curl_slist_append(connhd, "Depth: 0");
58 connhd = curl_slist_append(connhd, "Prefer: return-minimal");
59 connhd = curl_slist_append(connhd, "Content-Type: application/xml; charset=utf-8");
61 connhd2 = curl_slist_append(connhd2, "Depth: 0");
62 connhd2 = curl_slist_append(connhd2, "Prefer: return-minimal");
63 connhd2 = curl_slist_append(connhd2, "Content-Type: application/xml; charset=utf-8");
65 connhd3 = curl_slist_append(connhd3, "Depth: 1");
66 connhd3 = curl_slist_append(connhd3, "Prefer: return-minimal");
67 connhd3 = curl_slist_append(connhd3, "Content-Type: application/xml; charset=utf-8");
69 struct CardDAVCURLPasser {
72 bool HeaderMode = TRUE;
74 } CardDAVHeader, CardDAVFooter;
76 CardDAVHeader.Data = this;
77 CardDAVHeader.HeaderMode = TRUE;
79 CardDAVFooter.Data = this;
80 CardDAVFooter.HeaderMode = FALSE;
86 wxString ETagOriginal;
89 ServerAddressURL = ServerAddress + wxT(":") + wxString::Format(wxT("%i"), ServerPort) + wxT("/");
90 ServerAddressSSL = wxT("https://") + ServerAddressURL;
91 ServerAddressNormal = wxT("http://") + ServerAddressURL;
93 ServerAuth = ServerUser + wxT(":") + ServerPass;
95 wxString SAURLPrincipals;
96 wxString SAURLPrincipalURL;
97 wxString SAURLAddressURL;
101 SAURLPrincipals = ServerAddressSSL + wxT("principals/");
102 SAURLPrincipalURL = ServerAddressSSL;
103 SAURLAddressURL = ServerAddressSSL;
107 SAURLPrincipals = ServerAddressNormal + wxT("principals/");
108 SAURLPrincipalURL = ServerAddressNormal;
109 SAURLAddressURL = ServerAddressNormal;
113 wxString FinalPrefix;
115 struct UploadDataStruc UploadData;
117 // Setup the first query finding out where the principal URL is.
119 const char* query = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
120 "<D:propfind xmlns:D=\"DAV:\">\n"
122 " <D:current-user-principal/>\n"
126 // Setup the second query finding out where the address book home URL is.
128 const char* query2 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
129 "<D:propfind xmlns:D=\"DAV:\""
130 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
132 " <C:addressbook-home-set/>\n"
136 // Setup the third query finding out where the default address book URL is.
138 const char* query3 = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
139 "<D:propfind xmlns:D=\"DAV:\""
140 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\">\n"
142 " <C:default-addressbook-URL/>\n"
148 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
149 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
150 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
151 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
152 curl_easy_setopt(conn, CURLOPT_FAILONERROR, TRUE);
153 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
154 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
155 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
156 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
157 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
158 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
159 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
160 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
161 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
162 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
164 if (AllowSelfSign == TRUE){
165 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
166 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
169 conncode = (curl_easy_perform(conn));
171 if (conncode == CURLE_OK){
173 *ServerResult = TRUE;
175 ValidResponse = TRUE;
180 fprintf(stderr, "curl_easy_perform() failed: %s\n",
181 curl_easy_strerror(conncode));
183 *ServerResult = FALSE;
185 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
195 // Do an initial connection (incase of Digest authentication).
200 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipals.mb_str(wxConvUTF8));
201 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
202 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
203 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
204 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
205 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
206 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
207 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
208 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
209 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
210 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
211 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
212 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
213 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
214 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query);
215 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query));
216 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd);
218 conncode = (curl_easy_perform(conn));
220 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
221 // then bring up the conflict resolution form.
223 if (EditMode == TRUE){
227 if (conncode == CURLE_OK){
229 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
231 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
233 fprintf(stderr, "curl_easy_perform() failed: %s\n",
234 curl_easy_strerror(conncode));
236 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
243 fprintf(stderr, "curl_easy_perform() failed: %s\n",
244 curl_easy_strerror(conncode));
251 // Process the XML data from the application.
253 xmlDocPtr xmlCardDAVDoc;
254 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
256 xmlNodePtr nodeLevel1;
257 xmlNodePtr nodeLevel2;
258 xmlNodePtr nodeLevel3;
259 xmlNodePtr nodeLevel4;
260 xmlNodePtr nodeLevel5;
261 xmlNodePtr nodeLevel6;
262 xmlNodePtr nodeLevel7;
264 for (nodeLevel1 = xmlCardDAVDoc->children;
266 nodeLevel1 = nodeLevel1->next)
269 for (nodeLevel2 = nodeLevel1->children;
271 nodeLevel2 = nodeLevel2->next)
275 for (nodeLevel3 = nodeLevel2->children;
277 nodeLevel3 = nodeLevel3->next)
280 for (nodeLevel4 = nodeLevel3->children;
282 nodeLevel4 = nodeLevel4->next)
285 for (nodeLevel5 = nodeLevel4->children;
287 nodeLevel5 = nodeLevel5->next)
290 for (nodeLevel6 = nodeLevel5->children;
292 nodeLevel6 = nodeLevel6->next)
295 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
296 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
297 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
300 // Found the <href> part so extract the principal URL address.
302 for (nodeLevel7 = nodeLevel6->children;
304 nodeLevel7 = nodeLevel7->next)
307 SAURLPrincipalURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
325 xmlFreeDoc(xmlCardDAVDoc);
329 // Second: Get the addressbook-home-set
331 curl_easy_reset(conn);
335 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
336 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
337 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
338 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
339 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
340 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
341 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
342 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
343 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
344 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
345 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
346 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
347 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
348 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
349 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
351 if (AllowSelfSign == TRUE){
352 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
353 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
356 conncode = (curl_easy_perform(conn));
358 if (conncode == CURLE_OK){
360 *ServerResult = TRUE;
366 fprintf(stderr, "curl_easy_perform() failed: %s\n",
367 curl_easy_strerror(conncode));
369 *ServerResult = FALSE;
370 ValidResponse = FALSE;
372 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
382 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLPrincipalURL.mb_str(wxConvUTF8));
383 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
384 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
385 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
386 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
387 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
388 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
389 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
390 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
391 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
392 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
393 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
394 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
395 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
396 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query2);
397 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query2));
398 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd2);
400 conncode = (curl_easy_perform(conn));
402 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
403 // then bring up the conflict resolution form.
405 if (EditMode == TRUE){
409 if (conncode == CURLE_OK){
411 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
413 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
415 fprintf(stderr, "curl_easy_perform() failed: %s\n",
416 curl_easy_strerror(conncode));
418 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
421 ValidResponse = FALSE;
427 fprintf(stderr, "curl_easy_perform() failed: %s\n",
428 curl_easy_strerror(conncode));
430 ValidResponse = FALSE;
438 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
440 for (nodeLevel1 = xmlCardDAVDoc->children;
442 nodeLevel1 = nodeLevel1->next)
445 for (nodeLevel2 = nodeLevel1->children;
447 nodeLevel2 = nodeLevel2->next)
451 for (nodeLevel3 = nodeLevel2->children;
453 nodeLevel3 = nodeLevel3->next)
456 for (nodeLevel4 = nodeLevel3->children;
458 nodeLevel4 = nodeLevel4->next)
461 for (nodeLevel5 = nodeLevel4->children;
463 nodeLevel5 = nodeLevel5->next)
466 for (nodeLevel6 = nodeLevel5->children;
468 nodeLevel6 = nodeLevel6->next)
471 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
472 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
473 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
476 // Found the <href> part so extract the principal URL address.
478 for (nodeLevel7 = nodeLevel6->children;
480 nodeLevel7 = nodeLevel7->next)
483 SAURLAddressURL.Append(wxString::FromUTF8((const char*)nodeLevel7->content));
501 xmlFreeDoc(xmlCardDAVDoc);
505 // Finally: Get the default-addressbook-URL from the addressbook-home-set address.
507 curl_easy_reset(conn);
511 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
512 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 1L);
513 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
514 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
515 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
516 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
517 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
518 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
519 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
520 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
521 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
522 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
523 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
524 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
525 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
527 if (AllowSelfSign == TRUE){
528 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYPEER, 0L);
529 curl_easy_setopt(conn, CURLOPT_SSL_VERIFYHOST, 0L);
532 conncode = (curl_easy_perform(conn));
534 if (conncode == CURLE_OK){
536 *ServerResult = TRUE;
542 fprintf(stderr, "curl_easy_perform() failed: %s\n",
543 curl_easy_strerror(conncode));
545 *ServerResult = FALSE;
546 ValidResponse = FALSE;
548 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
558 curl_easy_setopt(conn, CURLOPT_URL, (const char*)SAURLAddressURL.mb_str(wxConvUTF8));
559 curl_easy_setopt(conn, CURLOPT_NOPROGRESS, 0);
560 curl_easy_setopt(conn, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
561 curl_easy_setopt(conn, CURLOPT_TIMEOUT, 60);
562 curl_easy_setopt(conn, CURLOPT_FAILONERROR, FALSE);
563 curl_easy_setopt(conn, CURLOPT_USERAGENT, XSDAB_USERAGENT);
564 curl_easy_setopt(conn, CURLOPT_USERPWD, (const char*)ServerAuth.mb_str(wxConvUTF8));
565 curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, WritebackFunc);
566 curl_easy_setopt(conn, CURLOPT_WRITEDATA, &PageData);
567 curl_easy_setopt(conn, CURLOPT_WRITEHEADER, &PageHeader);
568 curl_easy_setopt(conn, CURLOPT_PROGRESSDATA, this);
569 curl_easy_setopt(conn, CURLOPT_PROGRESSFUNCTION, ProgressFunc);
570 curl_easy_setopt(conn, CURLOPT_NOSIGNAL, 1);
571 curl_easy_setopt(conn, CURLOPT_CUSTOMREQUEST, "PROPFIND");
572 curl_easy_setopt(conn, CURLOPT_POSTFIELDS, query3);
573 curl_easy_setopt(conn, CURLOPT_POSTFIELDSIZE, strlen(query3));
574 curl_easy_setopt(conn, CURLOPT_HTTPHEADER, connhd3);
576 conncode = (curl_easy_perform(conn));
578 // If the ETag is different to the non-matching X-XAB-ETAG and X-XAB-ETAG-ORIG,
579 // then bring up the conflict resolution form.
581 if (EditMode == TRUE){
585 if (conncode == CURLE_OK){
587 } else if (conncode == CURLE_HTTP_RETURNED_ERROR){
589 curl_easy_getinfo(conn, CURLINFO_RESPONSE_CODE, &HTTPErrorCode);
591 fprintf(stderr, "curl_easy_perform() failed: %s\n",
592 curl_easy_strerror(conncode));
594 fprintf(stderr, "curl_easy_perform() HTTP code was: %i\n",
597 ValidResponse = FALSE;
603 fprintf(stderr, "curl_easy_perform() failed: %s\n",
604 curl_easy_strerror(conncode));
606 ValidResponse = FALSE;
614 xmlCardDAVDoc = xmlReadMemory(PageData.mb_str(wxConvUTF8), (int)PageData.Len(), "noname.xml", NULL, 0);
616 for (nodeLevel1 = xmlCardDAVDoc->children;
618 nodeLevel1 = nodeLevel1->next)
621 for (nodeLevel2 = nodeLevel1->children;
623 nodeLevel2 = nodeLevel2->next)
627 for (nodeLevel3 = nodeLevel2->children;
629 nodeLevel3 = nodeLevel3->next)
632 for (nodeLevel4 = nodeLevel3->children;
634 nodeLevel4 = nodeLevel4->next)
637 for (nodeLevel5 = nodeLevel4->children;
639 nodeLevel5 = nodeLevel5->next)
642 for (nodeLevel6 = nodeLevel5->children;
644 nodeLevel6 = nodeLevel6->next)
647 if (!xmlStrcmp(nodeLevel6->name, (const xmlChar *)"href") ||
648 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"d:href") ||
649 !xmlStrcmp(nodeLevel6->name, (const xmlChar *)"D:href")
652 // Found the <href> part so extract the principal URL address.
654 for (nodeLevel7 = nodeLevel6->children;
656 nodeLevel7 = nodeLevel7->next)
659 FinalPrefix = wxString::FromUTF8((const char*)nodeLevel7->content);
677 xmlFreeDoc(xmlCardDAVDoc);