public override void PopulateEntry(PwEntry pwEntry, PwDatabase pwDatabase, UserPrefs userPrefs, RecordType recordType) { base.PopulateEntry(pwEntry, pwDatabase, userPrefs, recordType); List <SecureContentsSection> sections; if (this.sections != null) { sections = this.sections; } else { sections = this.createSectionsFromPlainFields(recordType); } if (sections != null) { int unnamedSectionNumber = 1; SectionFieldLocator usernameFieldLocator = this.GetUsernameFieldLocator(); SectionFieldLocator passwordFieldLocator = this.GetPasswordFieldLocator(); SectionFieldLocator urlFieldLocator = this.GetURLFieldLocator(); foreach (SecureContentsSection section in sections) { // Linked items are not supported if (section.name.Equals(LINKED_ITEMS_SECTION_NAME)) { continue; } // Section without fields, nothing to import if (section.fields == null) { continue; } string sectionTitle = section.title; // If it's an unnamed user-defined section, set a generic unique name (prevents field name collisions) if (string.IsNullOrEmpty(section.title) && this.IsUserSection(section)) { sectionTitle = string.Format("{0} {1}", Properties.Strings.Section_Title, unnamedSectionNumber++); } foreach (SectionField field in section.fields) { // Special treatment fields if (field.k == SectionFieldType.concealed && OTP_FIELD_NAME.IsMatch(field.n ?? string.Empty)) { // OTP fields must be formatted to comply with one of the OTP plugins this.setOTPField(pwEntry, pwDatabase.MemoryProtection.ProtectPassword, field as GeneralSectionField, userPrefs.OTPFormat); continue; } else if (field.k == SectionFieldType.address && (field as AddressSectionField).v != null) { // Addresses can be imported as a single composite field or splitting each component in a separate field if (userPrefs.AddressFormat == AddressFormat.Compact) { this.setCompactAddressField(pwEntry, sectionTitle, field as AddressSectionField); } else if (userPrefs.AddressFormat == AddressFormat.Multiline) { this.setMultilineAddressField(pwEntry, sectionTitle, field as AddressSectionField); } else { this.setExpandedAddressField(pwEntry, sectionTitle, field as AddressSectionField); } continue; } string fieldLabel = field.t; string fieldValue = null; // If the field is in a named section, prefix its name to avoid collisions if (!string.IsNullOrEmpty(sectionTitle)) { fieldLabel = string.Concat(sectionTitle, " - ", field.t); } // Format the field value according to its type if (field.k == SectionFieldType.date) { DateSectionField dateSectionField = field as DateSectionField; if (!DateTime.MinValue.Equals(dateSectionField.v)) { fieldValue = DateTimeFormatter.FormatDate(dateSectionField.v, userPrefs.DateFormat); } } else if (field.k == SectionFieldType.monthYear) { MonthYearSectionField monthYearSectionField = field as MonthYearSectionField; if (!DateTime.MinValue.Equals(monthYearSectionField.v)) { fieldValue = DateTimeFormatter.FormatMonthYear(monthYearSectionField.v, userPrefs.DateFormat); } } else if (field.k == SectionFieldType.menu || field.k == SectionFieldType.cctype || field.k == SectionFieldType.gender) { GeneralSectionField generalSectionField = field as GeneralSectionField; // Combo-box values can't be empty if (!string.IsNullOrEmpty(generalSectionField.v)) { fieldValue = Properties.Strings.ResourceManager.GetString(string.Join("_", new string[] { "Menu", generalSectionField.n, generalSectionField.v })); } } else { fieldValue = (field as GeneralSectionField).v; } // Use the proper line terminator in multiline values if (field.a != null && field.a.multiline) { fieldValue = StringExt.FixNewLines(fieldValue); } // No point in importing an empty template field. If it's user-defined it might be there for a reason. if (string.IsNullOrEmpty(fieldValue) && !this.IsUserSection(section)) { continue; } bool protect; if (usernameFieldLocator != null && usernameFieldLocator.Equals(section.name, field.n)) { protect = pwDatabase.MemoryProtection.ProtectUserName; pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(protect, fieldValue ?? string.Empty)); fieldValue = "{USERNAME}"; } else if (passwordFieldLocator != null && passwordFieldLocator.Equals(section.name, field.n)) { protect = pwDatabase.MemoryProtection.ProtectPassword; pwEntry.Strings.Set(PwDefs.PasswordField, new ProtectedString(protect, fieldValue ?? string.Empty)); fieldValue = "{PASSWORD}"; } else if (urlFieldLocator != null && urlFieldLocator.Equals(section.name, field.n)) { protect = pwDatabase.MemoryProtection.ProtectUrl; pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(protect, fieldValue ?? string.Empty)); fieldValue = "{URL}"; } else { protect = (field.k == SectionFieldType.concealed && pwDatabase.MemoryProtection.ProtectPassword) || (field.k == SectionFieldType.URL && pwDatabase.MemoryProtection.ProtectUrl); } // If it's one of the three special fields (username, password, url) and the 1Password field // has the same name as the KeePass entry field, don't overwrite its value with the placeholder. if (!pwEntry.Strings.Exists(fieldLabel)) { pwEntry.Strings.Set(fieldLabel, new ProtectedString(protect, fieldValue ?? string.Empty)); } } } } if (!string.IsNullOrEmpty(this.notesPlain)) { pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(pwDatabase.MemoryProtection.ProtectNotes, StringExt.FixNewLines(this.notesPlain))); } if (this.customIcon != null) { byte[] customIconData = null; try { using (MemoryStream originalIconStream = new MemoryStream(this.customIcon)) using (Bitmap originalIcon = new Bitmap(originalIconStream)) using (MemoryStream convertedIconStream = new MemoryStream()) { originalIcon.Save(convertedIconStream, ImageFormat.Png); customIconData = convertedIconStream.ToArray(); using (MD5 md5 = MD5.Create()) { PwUuid customIconUuid = new PwUuid(md5.ComputeHash(customIconData)); this.PwCustomIcon = new PwCustomIcon(customIconUuid, customIconData); pwEntry.CustomIconUuid = customIconUuid; } } } catch (ArgumentException) { // Image format is not supported or one of its dimensions is bigger than 65,535 } } }
private List <SecureContentsSection> createSectionsFromPlainFields(RecordType recordType) { Dictionary <string, SecureContentsSection> sectionsByName = new Dictionary <string, SecureContentsSection>(); foreach (PropertyInfo propertyInfo in this.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)) { string fieldValue = Convert.ToString(propertyInfo.GetValue(this, null)); if (!Attribute.IsDefined(propertyInfo, typeof(ItemFieldAttribute)) || string.IsNullOrEmpty(fieldValue)) { continue; } ItemFieldAttribute itemFieldAttribute = Attribute.GetCustomAttribute(propertyInfo, typeof(ItemFieldAttribute)) as ItemFieldAttribute; string sectionName = itemFieldAttribute.sectionName ?? string.Empty; SecureContentsSection section; if (!sectionsByName.TryGetValue(sectionName, out section)) { section = new SecureContentsSection() { name = sectionName, title = Properties.Strings.ResourceManager.GetString(string.Join("_", new string[] { "TemplateSection", Enum.GetName(typeof(RecordType), recordType), sectionName.Replace(" ", "_") })), fields = new List <SectionField>() }; sectionsByName.Add(sectionName, section); } string fieldName = itemFieldAttribute.fieldName ?? propertyInfo.Name; if (itemFieldAttribute.type == SectionFieldType.address && Attribute.IsDefined(propertyInfo, typeof(AddressComponentAttribute))) { AddressSectionField addressSectionField = section.fields.Find(field => { return(fieldName.Equals(field.n)); }) as AddressSectionField; if (addressSectionField == null) { addressSectionField = new AddressSectionField() { n = fieldName, t = Properties.Strings.ResourceManager.GetString(string.Join("_", new string[] { "TemplateField", Enum.GetName(typeof(RecordType), recordType), fieldName.Replace(" ", "_") })), v = new AddressValue(), k = itemFieldAttribute.type }; section.fields.Add(addressSectionField); } AddressComponentAttribute addressAttribute = Attribute.GetCustomAttribute(propertyInfo, typeof(AddressComponentAttribute)) as AddressComponentAttribute; switch (addressAttribute.addressPart) { case AddressComponentAttribute.AddressPart.Address1: addressSectionField.v.street = string.IsNullOrEmpty(addressSectionField.v.street) ? fieldValue : string.Join(Environment.NewLine, new string[] { fieldValue, addressSectionField.v.street }); break; case AddressComponentAttribute.AddressPart.Address2: addressSectionField.v.street = string.IsNullOrEmpty(addressSectionField.v.street) ? fieldValue : string.Join(Environment.NewLine, new string[] { addressSectionField.v.street, fieldValue }); break; case AddressComponentAttribute.AddressPart.ZIP: addressSectionField.v.zip = fieldValue; break; case AddressComponentAttribute.AddressPart.City: addressSectionField.v.city = fieldValue; break; case AddressComponentAttribute.AddressPart.State: addressSectionField.v.state = fieldValue; break; case AddressComponentAttribute.AddressPart.Region: addressSectionField.v.region = fieldValue; break; case AddressComponentAttribute.AddressPart.Country: addressSectionField.v.country = fieldValue; break; default: break; } } else if (itemFieldAttribute.type == SectionFieldType.date && Attribute.IsDefined(propertyInfo, typeof(DateComponentAttribute))) { DateSectionField dateSectionField = section.fields.Find(field => { return(fieldName.Equals(field.n)); }) as DateSectionField; if (dateSectionField == null) { dateSectionField = new DateSectionField() { n = fieldName, t = Properties.Strings.ResourceManager.GetString(string.Join("_", new string[] { "TemplateField", Enum.GetName(typeof(RecordType), recordType), fieldName.Replace(" ", "_") })), // 2000 is a leap year and January has 31 days. This guarantees no matter the order in which we add days, // months or years to this base date, we won't get an invalid intermediate date (assuming a valid target date). v = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), k = itemFieldAttribute.type }; section.fields.Add(dateSectionField); } DateComponentAttribute dateAttribute = Attribute.GetCustomAttribute(propertyInfo, typeof(DateComponentAttribute)) as DateComponentAttribute; int intFieldValue; if (int.TryParse(fieldValue, out intFieldValue)) { switch (dateAttribute.datePart) { case DateComponentAttribute.DatePart.Day: dateSectionField.v = dateSectionField.v.AddDays(intFieldValue - dateSectionField.v.Day); break; case DateComponentAttribute.DatePart.Month: dateSectionField.v = dateSectionField.v.AddMonths(intFieldValue - dateSectionField.v.Month); break; case DateComponentAttribute.DatePart.Year: dateSectionField.v = dateSectionField.v.AddYears(intFieldValue - dateSectionField.v.Year); break; default: break; } } } else if (itemFieldAttribute.type == SectionFieldType.monthYear && Attribute.IsDefined(propertyInfo, typeof(MonthYearComponentAttribute))) { MonthYearSectionField monthYearSectionField = section.fields.Find(field => { return(fieldName.Equals(field.n)); }) as MonthYearSectionField; if (monthYearSectionField == null) { monthYearSectionField = new MonthYearSectionField() { n = fieldName, t = Properties.Strings.ResourceManager.GetString(string.Join("_", new string[] { "TemplateField", Enum.GetName(typeof(RecordType), recordType), fieldName.Replace(" ", "_") })), // 2000 is a leap year and January has 31 days. This guarantees no matter the order in which we add days, // months or years to this base date, we won't get an invalid intermediate date (assuming a valid target date). v = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc), k = itemFieldAttribute.type }; section.fields.Add(monthYearSectionField); } MonthYearComponentAttribute monthYearAttribute = Attribute.GetCustomAttribute(propertyInfo, typeof(MonthYearComponentAttribute)) as MonthYearComponentAttribute; int intFieldValue; if (int.TryParse(fieldValue, out intFieldValue)) { switch (monthYearAttribute.monthYearPart) { case MonthYearComponentAttribute.MonthYearPart.Month: monthYearSectionField.v = monthYearSectionField.v.AddMonths(intFieldValue - monthYearSectionField.v.Month); break; case MonthYearComponentAttribute.MonthYearPart.Year: monthYearSectionField.v = monthYearSectionField.v.AddYears(intFieldValue - monthYearSectionField.v.Year); break; default: break; } } } else // the remaining field types are all GeneralSectionFields { section.fields.Add(new GeneralSectionField() { n = fieldName, t = Properties.Strings.ResourceManager.GetString(string.Join("_", new string[] { "TemplateField", Enum.GetName(typeof(RecordType), recordType), fieldName.Replace(" ", "_") })), v = fieldValue, k = itemFieldAttribute.type, a = new SectionFieldAttributes() { multiline = itemFieldAttribute.multiline } }); } } return(new List <SecureContentsSection>(sectionsByName.Values)); }