1 // vcard.cpp - vCard Object
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/>
22 #include <wx/tokenzr.h>
25 // vcard.cpp - Deals with vCard 4.0 formatted files meeting the
26 // RFC 6350 specification.
30 // Setup the vCard object.
40 void vCard::Add(wxString SettingName, wxString SettingValue, bool ReplaceMode){
42 // Add data to vCard object.
44 // Check for backslashes used for commas, newlines and
45 // backslashes used for values.
47 if (ReplaceMode == TRUE){
49 SettingValue.Replace(wxT("\\n"), wxT("\n"));
50 SettingValue.Replace(wxT("\\,"), wxT(","));
51 SettingValue.Replace(wxT("\\:"), wxT(":"));
52 SettingValue.Replace(wxT("\\\\"), wxT("\\"));
56 SettingValue.Replace(wxT("\\"), wxT("\\\\"));
57 SettingValue.Replace(wxT("\n"), wxT("\\n"));
58 SettingValue.Replace(wxT(","), wxT("\\,"));
59 SettingValue.Replace(wxT(":"), wxT("\\:"));
60 SettingValue = SettingValue + wxT("\n");
64 // Check data to make sure that it meets the required
65 // vCard 4.0 specifications.
67 if (SettingName == wxT("BEGIN") && SettingValue == wxT("VCARD")){
71 if (SettingName == wxT("END") && SettingValue == wxT("VCARD")){
75 if (SettingName.Mid(0,2) == wxT("FN")){
79 if (SettingName == wxT("VERSION") && SettingValue == wxT("4.0")){
83 if (SettingName == wxT("VERSION") && SettingValue == wxT("3.0")){
87 if (SettingName == wxT("VERSION") && SettingValue == wxT("3.0")){
93 if (SettingValue.Right(2) != wxT("\r\n")){
95 SettingValue.Append(wxT("\r\n"));
99 SettingNames.Add(SettingName, 1);
100 SettingValues.Add(SettingValue, 1);
106 void vCard::AddRaw(wxString SettingName, wxString SettingValue){
108 // Add data to the vCard in raw mode.
110 // Check data to make sure that it meets the required
111 // vCard 4.0 specifications.
113 if (SettingName == wxT("BEGIN") && SettingValue == wxT("VCARD")){
117 if (SettingName == wxT("END") && SettingValue == wxT("VCARD")){
121 if (SettingName.Mid(0,2) == wxT("FN")){
125 if (SettingName == wxT("VERSION") && SettingValue == wxT("4.0")){
129 if (SettingName == wxT("VERSION") && SettingValue == wxT("3.0")){
133 if (SettingName == wxT("VERSION") && SettingValue == wxT("3.0")){
139 if (SettingValue.Right(2) != wxT("\r\n")){
141 SettingValue.Append(wxT("\r\n"));
145 SettingNames.Add(SettingName, 1);
146 SettingValues.Add(SettingValue, 1);
152 wxString vCard::Get(wxString SettingName){
154 // Get values from the vCard object.
156 wxString SettingValue;
158 // Look for the setting name.
160 for (int i = 0; i < SettingCount; i++){
162 if (SettingNames[i] == SettingName){
164 SettingValue = SettingValues[i];
165 SettingValue.Trim(TRUE);
167 while (SettingValues[(i + 1)].Mid(0, 1) == wxT(" ") || SettingValues[(i + 1)].Mid(0, 1) == wxT("\t")){
170 SettingValue.Append(SettingValues[(i + 1)]);
182 return wxEmptyString;
186 vCardName vCard::GetName(){
188 // Get the name from the vCard object.
191 ArrayvCardOutData NameArray = this->GetByPartial(wxT("N"));
192 //wxString NameDataGet = NameArray.PropValues[0];
193 wxString NameDataGet = NameArray.PropValues[0];
194 std::map<int, int> SplitPoints;
195 std::map<int, int> SplitLength;
196 std::map<int, int>::iterator SLiter;
198 // Process the name data to get the required information.
200 int intPropertyLen = NameDataGet.Len();
201 int intSplitSeek = 0;
202 int intSplitsFound = 0;
203 int intSplitSize = 0;
204 int intPrevValue = 0;
206 for (int i = 0; i <= intPropertyLen; i++){
210 if (NameDataGet.Mid(i, 1) == wxT(";") && NameDataGet.Mid((i - 1), 1) != wxT("\\")){
213 SplitPoints.insert(std::make_pair(intSplitsFound, (i + 1)));
215 if (intSplitsFound == 4){
217 SplitLength.insert(std::make_pair(intSplitsFound, (intSplitSize - 1)));
222 SplitLength.insert(std::make_pair(intSplitsFound, (intSplitSize - 1)));
232 for (std::map<int, int>::iterator intiter = SplitPoints.begin();
233 intiter != SplitPoints.end(); ++intiter){
235 if (intiter->first == 1){
237 // Deal with family name.
239 SLiter = SplitLength.find(1);
241 NameData.Surname = NameDataGet.Mid(0, SLiter->second);
242 intPrevValue = intiter->second;
244 } else if (intiter->first == 2){
246 // Deal with given names.
248 SLiter = SplitLength.find(2);
250 NameData.Forename = NameDataGet.Mid(intPrevValue, SLiter->second);
251 intPrevValue = intiter->second;
254 } else if (intiter->first == 3){
256 // Deal with additional names.
258 SLiter = SplitLength.find(3);
260 NameData.OtherNames = NameDataGet.Mid(intPrevValue, SLiter->second);
261 intPrevValue = intiter->second;
263 } else if (intiter->first == 4){
265 // Deal with honorifix prefixes and suffixes.
266 SLiter = SplitLength.find(4);
268 NameData.Title = NameDataGet.Mid(intPrevValue, SLiter->second);
269 intPrevValue = intiter->second;
270 NameData.Suffix = NameDataGet.Mid(intPrevValue);
281 ArrayvCardOutData vCard::GetByPartial(wxString SettingName){
283 // Get data from the vCard object based on a partial match.
285 ArrayvCardOutData vCardOutData;
286 wxArrayString SettingList;
287 wxString SettingValueCurrent;
288 wxString SettingValue;
291 bool FirstToken = TRUE;
293 SettingNameLen = SettingName.Len();
295 for (int i = 0; i < SettingCount; i++){
297 if (SettingNames[i].Mid(0, SettingNameLen) == SettingName){
299 SettingValue = SettingValues[i];
302 while (SettingValues[(i + 1)].Mid(0, 1) == wxT(" ") || SettingValues[(i + 1)].Mid(0, 1) == wxT("\t")){
304 if (FirstToken == TRUE){
306 SettingValue.Trim(FALSE);
307 SettingValue.Trim(TRUE);
312 SettingValueCurrent = SettingValues[(i + 1)];
313 SettingValueCurrent.Trim(FALSE);
314 SettingValueCurrent.Trim(TRUE);
316 SettingValue.Append(SettingValueCurrent);
322 //SettingList.Add(SettingNames[SettingNameSeek] + wxT(":") + SettingValue);
323 vCardOutData.PropData.Add(SettingNames[SettingNameSeek]);
324 vCardOutData.PropValues.Add(SettingValue);
325 vCardOutData.PropCount++;
330 /*for (int i = 0; i < SettingCount; i++){
331 if (SettingNames[i].Mid(0, SettingNameLen) == SettingName){
333 SettingValue = SettingValues[i];
336 while (SettingValues[(i + 1)].Mid(0, 1) == wxT(" ") || SettingValues[(i + 1)].Mid(0, 1) == wxT("\t")){
338 SettingValueCurrent = SettingValues[(i + 1)];
339 SettingValueCurrent.Trim(FALSE);
340 SettingValueCurrent.Trim(TRUE);
342 SettingValue.Append(SettingValueCurrent);
348 //SettingList.Add(SettingNames[SettingNameSeek] + wxT(":") + SettingValue);
349 vCardOutData.PropData.Add(SettingName);
350 vCardOutData.PropData.Add(SettingValue);
359 wxString vCard::GetById(int id){
361 // Get data from the vCard object based on ID.
368 int vCard::WriteFile(wxString WriteFilename){
370 // Write the vCard to a file using the WriteFilename given.
372 // Open the file and begin writing data into the file.
374 wxString SettingName;
375 wxString SettingValue;
376 wxString SettingLine;
378 SettingCount = SettingNames.GetCount();
381 if (ContactFile.Create(WriteFilename, TRUE, wxS_DEFAULT) == FALSE){
385 for (int i = 0; i < SettingCount; i++){
387 SettingLine = SettingNames[i] + wxT(":") + SettingValues[i];
389 int SettingLineLen = SettingLine.Len();
391 int intTimes = floor((SettingLine.Len() / intDivider));
395 bool FirstLine = TRUE;
397 // Remember to round down the calculation.
399 while (intSeek < SettingLineLen){
401 if ((intLineSeek == intDivider && FirstLine == TRUE) ||
402 (intLineSeek == (intDivider - 1) && FirstLine == FALSE)){
404 SettingLine.insert(intSeek, wxT("\r\n "));
405 intSeek = intSeek + 3;
406 SettingLineLen = SettingLineLen + 3;
408 intPrevLine = intSeek;
420 for (int x = 0; x < intTimes; x++){
423 SettingLine.insert((intDivider - 1), wxT("\r\n "));
424 } else if (x == intTimes){
428 SettingLine.insert((intDivider * (x+1)) + (x * 3), wxT("\r\n "));
432 intTimes = floor(SettingLine.Len() / intDivider);
438 ContactFile.Write(SettingLine);
448 int vCard::LoadFile(wxString LoadFilename){
450 // Load data from a file using the LoadFilename given.
454 wxString wxSContactString;
456 vCardFilename = LoadFilename;
458 // Check if we are using wxWidgets version 2.8 or less and
459 // execute the required command accordingly.
461 #if wxABI_VERSION < 20900
462 ContactFile.Open(LoadFilename.c_str(), wxT("r"));
464 ContactFile.Open(LoadFilename, wxT("r"));
467 if (ContactFile.IsOpened() == FALSE){
473 ContactFile.ReadAll(&wxSContactString, wxConvAuto());
477 ProcessString(&wxSContactString);
483 int vCard::LoadString(wxString ContactData){
485 // Load data from a wxString.
487 ProcessString(&ContactData);
493 void vCard::ProcessString(wxString *ContactDataInc){
495 // Process data from a wxString pointer.
497 // Split the vCards (if there are more than one vCard in the file).
499 wxString ContactLine;
502 bool ExtraLineSeek = FALSE;
503 int QuoteBreakPoint = 0;
505 bool PropertyFind = FALSE;
506 bool QuoteMode = FALSE;
508 wxString wxSPropertyNextLine;
509 wxString wxSProperty;
510 wxString wxSPropertySeg1;
511 wxString wxSPropertySeg2;
513 bool FoundBegin = FALSE;
514 bool FoundEnd = FALSE;
515 bool FirstContact = TRUE;
516 wxString FirstContactData;
517 wxString ContactData;
518 int ContactCount = 0;
520 wxStringTokenizer wSTContactFileLines(*ContactDataInc, wxT("\r\n"));
522 while(wSTContactFileLines.HasMoreTokens() == TRUE){
524 ContactLine = wSTContactFileLines.GetNextToken();
526 if (ContactLine == wxT("BEGIN:VCARD")){
528 if (FoundBegin == TRUE){
530 // No END:VCARD was found so discard current data.
534 if (FirstContact == TRUE){
536 FirstContactData.Clear();
544 FirstContactData.Append(ContactLine + wxT("\r\n"));
545 ContactData.Append(ContactLine + wxT("\r\n"));
547 } else if (ContactLine == wxT("END:VCARD") && FoundBegin == TRUE){
549 if (FirstContact == TRUE){
551 FirstContact = FALSE;
552 FirstContactData.Append(ContactLine + wxT("\r\n"));
556 ContactData.Append(ContactLine + wxT("\r\n"));
558 Cards.insert(std::make_pair(ContactCount, ContactData));
562 } else if (FoundBegin == TRUE){
564 if (FirstContact == TRUE){
566 FirstContactData.Append(ContactLine + wxT("\r\n"));
570 ContactData.Append(ContactLine + wxT("\r\n"));
580 std::map<int, wxString> ContactFileLines;
581 std::map<int, wxString>::iterator striter;
583 wxStringTokenizer wSTFirstContactLines(FirstContactData, wxT("\r\n"));
585 int ContactLineSeek = 0;
587 while (wSTFirstContactLines.HasMoreTokens() == TRUE){
589 ContactLine = wSTFirstContactLines.GetNextToken();
590 ContactFileLines.insert(std::make_pair(ContactLineSeek, ContactLine));
595 for (std::map<int,wxString>::iterator iter = ContactFileLines.begin();
596 iter != ContactFileLines.end(); ++iter){
598 // Find the colon which splits the start bit from the data part.
600 ContactLine = iter->second;
602 while (ExtraLineSeek == TRUE){
604 // Check if there is extra data on the next line
605 // (indicated by space or tab at the start) and add data.
609 if (iter == ContactFileLines.end()){
616 wxSPropertyNextLine = iter->second;
618 if (wxSPropertyNextLine.Mid(0, 1) == wxT(" ") || wxSPropertyNextLine.Mid(0, 1) == wxT("\t")){
620 wxSPropertyNextLine.Remove(0, 1);
621 //wxSPropertyNextLine.Trim(FALSE);
622 //ContactLine.Trim();
623 ContactLine.Append(wxSPropertyNextLine);
628 ExtraLineSeek = FALSE;
634 ContactLineLen = ContactLine.Len();
636 // Make sure we are not in quotation mode.
637 // Make sure colon does not have \ or \\ before it.
639 for (int i = 0; i <= ContactLineLen; i++){
641 if ((ContactLine.Mid(i, 1) == wxT(";") || ContactLine.Mid(i, 1) == wxT(":")) && PropertyFind == TRUE){
643 PropertyFind = FALSE;
645 } else if (PropertyFind == TRUE){
647 wxSProperty.Append(ContactLine.Mid(i, 1));
651 if (ContactLine.Mid(i, 1) == wxT("\"")){
653 if (QuoteMode == TRUE){
665 if (ContactLine.Mid(i, 1) == wxT(":") && ContactLine.Mid((i - 1), 1) != wxT("\\") && QuoteMode == FALSE){
674 // Split that line at the point into two variables (ignore the colon).
676 wxSPropertySeg1 = ContactLine.Mid(0, QuoteBreakPoint);
677 wxSPropertySeg2 = ContactLine.Mid((QuoteBreakPoint + 1));
679 // Insert both into the vCard data file.
681 AddRaw(wxSPropertySeg1, wxSPropertySeg2);
685 ExtraLineSeek = TRUE;
696 wxString vCard::WriteString(){
698 // Write the vCard file into a wxString.
700 // Open the file and begin writing data into the file.
702 wxString SettingName;
703 wxString SettingValue;
704 wxString SettingLine;
705 wxString SettingFinal;
707 SettingCount = SettingNames.GetCount();
709 for (int i = 0; i < SettingCount; i++){
711 SettingLine = SettingNames[i] + wxT(":") + SettingValues[i];
713 int SettingLineLen = SettingLine.Len();
715 int intTimes = floor((SettingLine.Len() / intDivider));
719 bool FirstLine = TRUE;
721 // Remember to round down the calculation.
723 while (intSeek < SettingLineLen){
725 if ((intLineSeek == intDivider && FirstLine == TRUE) ||
726 (intLineSeek == (intDivider - 1) && FirstLine == FALSE)){
728 SettingLine.insert(intSeek, wxT("\r\n "));
729 intSeek = intSeek + 3;
730 SettingLineLen = SettingLineLen + 3;
732 intPrevLine = intSeek;
742 SettingFinal.Append(SettingLine);
750 bool vCard::MeetBaseSpecification(){
752 // Check and see if the vCard object meets the base specification
755 if (vCardBegin == TRUE && vCardEnd == TRUE && vCardFN == TRUE &&
756 vCardVersion == 4.0){
768 wxString vCard::Convert(wxString SettingValue, bool ReplaceMode){
770 // Check for backslashes used for commas, newlines and
771 // backslashes used for values.
773 if (ReplaceMode == TRUE){
775 SettingValue.Replace(wxT("\\n"), wxT("\n"));
776 SettingValue.Replace(wxT("\\,"), wxT(","));
777 SettingValue.Replace(wxT("\\;"), wxT(";"));
778 SettingValue.Replace(wxT("\\\\"), wxT("\\"));
782 SettingValue.Replace(wxT("\\"), wxT("\\\\"));
783 SettingValue.Replace(wxT("\n"), wxT("\\n"));
784 SettingValue.Replace(wxT(","), wxT("\\,"));
785 SettingValue.Replace(wxT(";"), wxT("\\;"));
786 SettingValue = SettingValue + wxT("\n");
794 wxString vCard::GetFilename(){
796 // Get the filename associated with the vCard object.
798 return vCardFilename;
802 std::map<int,wxString>* vCard::GetAllCards(){
804 // Get all of vCards within the vCard object.