PEPIdentity.cs
author Thomas
Tue, 13 Feb 2018 15:18:56 +0100
branchOUT-401
changeset 2003 c0dfc0f23c17
parent 1969 3f9e1c446eaa
child 2004 fef678d01f1f
permissions -rw-r--r--
Use PEPSettings to check for own identities and always use a userId
- if in cache, use the one from cache
- if not in cache and contact item available, use contact item id
- if not in cache and no contact, use address entry id
     1 ´╗┐using pEpCOMServerAdapterLib;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.ComponentModel;
     5 using System.Linq;
     6 using Outlook = Microsoft.Office.Interop.Outlook;
     7 
     8 namespace pEp
     9 {
    10     /// <summary>
    11     /// Class to wrap the pEp engine identity and expose additional functionality.
    12     /// </summary>
    13     internal class PEPIdentity : INotifyPropertyChanged,
    14                                  IEquatable<PEPIdentity>,
    15                                  Interfaces.ICopy<PEPIdentity>
    16     {
    17         /// <summary>
    18         /// Event raised when a property is changed on a component.
    19         /// </summary>
    20         public event PropertyChangedEventHandler PropertyChanged;
    21 
    22         private bool?             _IsForceUnencrypted;
    23         private bool              _IsSmartAddressEnabled;
    24         private List<PEPIdentity> _Members;
    25         private pEpRating         _Rating;
    26 
    27         private pEpIdentity internalIdentity;
    28 
    29         /**************************************************************
    30          * 
    31          * Constructors
    32          * 
    33          *************************************************************/
    34 
    35         /// <summary>
    36         /// Default constructor.
    37         /// </summary>
    38         public PEPIdentity()
    39         {
    40             this.internalIdentity = new pEpIdentity();
    41             this._IsForceUnencrypted = null;
    42             this._IsSmartAddressEnabled = true;
    43             this._Members = new List<PEPIdentity>();
    44             this._Rating = pEpRating.pEpRatingUndefined;
    45         }
    46 
    47         /// <summary>
    48         /// Constructor to build with the given address.
    49         /// </summary>
    50         /// <param name="address">The address of the identity to build with.</param>
    51         public PEPIdentity(string address)
    52         {
    53             this.internalIdentity = new pEpIdentity();
    54             this.internalIdentity.Address = address;
    55             this._IsForceUnencrypted = null;
    56             this._IsSmartAddressEnabled = true;
    57             this._Members = new List<PEPIdentity>();
    58             this._Rating = pEpRating.pEpRatingUndefined;
    59         }
    60 
    61         /// <summary>
    62         /// Constructor to build from a pEp engine identity.
    63         /// </summary>
    64         /// <param name="ident">The identity to build from.</param>
    65         public PEPIdentity(pEpIdentity ident)
    66         {
    67             this.internalIdentity = ident;
    68             this._IsForceUnencrypted = null;
    69             this._IsSmartAddressEnabled = true;
    70             this._Members = new List<PEPIdentity>();
    71             this._Rating = pEpRating.pEpRatingUndefined;
    72         }
    73 
    74         /**************************************************************
    75          * 
    76          * Property Accessors
    77          * 
    78          *************************************************************/
    79 
    80         /// <summary>
    81         /// Gets or sets the email address of the identity.
    82         /// Note: For get, the address used is the smart address.
    83         /// If smart address is enabled, and the set address is empty/null, the user name is 
    84         /// considered the address if it's formatted as an address itself.
    85         /// </summary>
    86         public string Address
    87         {
    88             get
    89             {
    90                 // Make sure to use the smart address
    91                 return (this.GetSmartAddress());
    92             }
    93             set
    94             {
    95                 this.internalIdentity.Address = value;
    96                 this.RaisePropertyChangedEvent(nameof(this.Address));
    97                 this.RaisePropertyChangedEvent(nameof(this.DisplayString));
    98                 this.RaisePropertyChangedEvent(nameof(this.IsAddressValid));
    99             }
   100         }
   101 
   102         /// <summary>
   103         /// Gets or sets the communication type with the identity.
   104         /// </summary>
   105         public pEpComType CommunicationType
   106         {
   107             get { return (this.internalIdentity.CommType); }
   108             set
   109             {
   110                 this.internalIdentity.CommType = value;
   111                 this.RaisePropertyChangedEvent(nameof(this.CommunicationType));
   112             }
   113         }
   114 
   115         /// <summary>
   116         /// Gets or sets the pEp engine fingerprint of the identity.
   117         /// </summary>
   118         public string Fingerprint
   119         {
   120             get { return (this.internalIdentity.fpr); }
   121             set
   122             {
   123                 this.internalIdentity.fpr = value;
   124                 this.RaisePropertyChangedEvent(nameof(this.Fingerprint));
   125             }
   126         }
   127 
   128         /// <summary>
   129         /// Gets or sets whether the identity is a member of an existing device group.
   130         /// </summary>
   131         public bool IsInDeviceGroup
   132         {
   133             get { return (this.internalIdentity.GetIsInDeviceGroup()); }
   134             set
   135             {
   136                 if (value)
   137                 {
   138                     this.internalIdentity.Flags |= pEpIdentityFlags.pEpIdfDevicegroup;
   139                 }
   140                 else
   141                 {
   142                     this.internalIdentity.Flags &= ~pEpIdentityFlags.pEpIdfDevicegroup;
   143                 }
   144 
   145                 this.RaisePropertyChangedEvent(nameof(this.IsInDeviceGroup));
   146             }
   147         }
   148 
   149         /// <summary>
   150         /// Gets or sets whether the identity is a list.
   151         /// Email lists should not be used for syncronization.
   152         /// </summary>
   153         public bool IsList
   154         {
   155             get { return (this.internalIdentity.GetIsList()); }
   156             set
   157             {
   158                 if (value)
   159                 {
   160                     this.internalIdentity.Flags |= pEpIdentityFlags.pEpIdfList;
   161                 }
   162                 else
   163                 {
   164                     this.internalIdentity.Flags &= ~pEpIdentityFlags.pEpIdfList;
   165                 }
   166 
   167                 this.RaisePropertyChangedEvent(nameof(this.IsList));
   168             }
   169         }
   170 
   171         /// <summary>
   172         /// Gets or sets whether the identity is to be included in device synchronization.
   173         /// </summary>
   174         public bool IsSyncEnabled
   175         {
   176             get { return (this.internalIdentity.GetIsSyncEnabled()); }
   177             set
   178             {
   179                 if (value)
   180                 {
   181                     // Note: Invert the logic
   182                     this.internalIdentity.Flags &= ~pEpIdentityFlags.pEpIdfNotForSync;
   183                 }
   184                 else
   185                 {
   186                     this.internalIdentity.Flags |= pEpIdentityFlags.pEpIdfNotForSync;
   187                 }
   188 
   189                 this.RaisePropertyChangedEvent(nameof(this.IsSyncEnabled));
   190             }
   191         }
   192 
   193         /// <summary>
   194         /// Gets or sets the communication language with the identity.
   195         /// </summary>
   196         public string Language
   197         {
   198             get { return (this.internalIdentity.lang); }
   199             set
   200             {
   201                 this.internalIdentity.lang = value;
   202                 this.RaisePropertyChangedEvent(nameof(this.Language));
   203             }
   204         }
   205 
   206         /// <summary>
   207         /// Gets or sets a unique user ID for the identity.
   208         /// </summary>
   209         public string UserId
   210         {
   211             get { return (this.internalIdentity.UserId); }
   212             set
   213             {
   214                 this.internalIdentity.UserId = value;
   215                 this.RaisePropertyChangedEvent(nameof(this.UserId));
   216             }
   217         }
   218 
   219         /// <summary>
   220         /// Gets or sets the user name of the identity.
   221         /// This is what the identity should be displayed as.
   222         /// </summary>
   223         public string UserName
   224         {
   225             get { return (this.internalIdentity.UserName); }
   226             set
   227             {
   228                 this.internalIdentity.UserName = value;
   229                 this.RaisePropertyChangedEvent(nameof(this.UserName));
   230                 this.RaisePropertyChangedEvent(nameof(this.DisplayString));
   231                 this.RaisePropertyChangedEvent(nameof(this.IsAddressValid));
   232             }
   233         }
   234 
   235         /// <summary>
   236         /// Gets or sets the force unencrypted property of the contact associated 
   237         /// with this identity. The value can be null if there is no contact property.
   238         /// </summary>
   239         public bool? IsForceUnencrypted
   240         {
   241             get { return (this._IsForceUnencrypted); }
   242             set
   243             {
   244                 this._IsForceUnencrypted = value;
   245                 this.RaisePropertyChangedEvent(nameof(this.IsForceUnencrypted));
   246                 this.RaisePropertyChangedEvent(nameof(this.IsForceUnencryptedBool));
   247             }
   248         }
   249 
   250         /// <summary>
   251         /// Gets or sets whether smart address processing is enabled.
   252         /// If smart address is enabled, and the set address is empty/null, the user name is 
   253         /// considered the address if it's formatted as an address itself.
   254         /// </summary>
   255         public bool IsSmartAddressEnabled
   256         {
   257             get { return (this._IsSmartAddressEnabled); }
   258             set
   259             {
   260                 this._IsSmartAddressEnabled = value;
   261                 this.RaisePropertyChangedEvent(nameof(this.IsSmartAddressEnabled));
   262                 this.RaisePropertyChangedEvent(nameof(this.DisplayString));
   263                 this.RaisePropertyChangedEvent(nameof(this.IsAddressValid));
   264             }
   265         }
   266 
   267         /// <summary>
   268         /// Gets the list of members in this identity if it is a group.
   269         /// Note: This is not supported by the pEp engine or the COM type.
   270         /// </summary>
   271         public List<PEPIdentity> Members
   272         {
   273             get { return (this._Members); }
   274         }
   275 
   276         /// <summary>
   277         /// Gets or sets the associated rating of the identity.
   278         /// This will not be automatically calculated and must be managed externally.
   279         /// </summary>
   280         public pEpRating Rating
   281         {
   282             get { return (this._Rating); }
   283             set
   284             {
   285                 this._Rating = value;
   286                 this.RaisePropertyChangedEvent(nameof(this.Rating));
   287             }
   288         }
   289 
   290         ///////////////////////////////////////////////////////////
   291         // Calculated
   292         ///////////////////////////////////////////////////////////
   293 
   294         /// <summary>
   295         /// Gets the string to display for this identity (based on UserName and Address).
   296         /// This internally will call .ToString().
   297         /// </summary>
   298         public string DisplayString
   299         {
   300             get { return (this.ToString()); }
   301         }
   302 
   303         /// <summary>
   304         /// Gets the color corresponding with the rating.
   305         /// </summary>
   306         public pEpColor Color
   307         {
   308             get { return (this._Rating.ToColor()); }
   309         }
   310 
   311         /// <summary>
   312         /// Gets whether the identity address is valid for the pEp engine and further use.
   313         /// This can only validate the basic format of the address (xxxx@xxxx.xxx).
   314         /// Note: The address used is the smart address.
   315         /// If smart address is enabled, and the set address is empty/null, the user name is 
   316         /// considered the address if it's formatted as an address itself.
   317         /// </summary>
   318         public bool IsAddressValid
   319         {
   320             get
   321             {
   322                 // Make sure to use the smart address
   323                 return (PEPIdentity.GetIsAddressValid(this.GetSmartAddress()));
   324             }
   325         }
   326 
   327         /// <summary>
   328         /// Gets whether this identity is empty and contains no data.
   329         /// Note: CommunicationType, Flags, Language and IsSmartAddressEnabled are ignored.
   330         /// </summary>
   331         public bool IsEmpty
   332         {
   333             get
   334             {
   335                 bool isEmpty = true;
   336 
   337                 // Check Address
   338                 if (isEmpty)
   339                 {
   340                     // Do not use the smart address
   341                     if (string.IsNullOrEmpty(this.internalIdentity.Address) == false)
   342                     {
   343                         isEmpty = false;
   344                     }
   345                 }
   346 
   347                 // Skip CommunicationType
   348 
   349                 // Check Fingerprint
   350                 if (isEmpty)
   351                 {
   352                     if (string.IsNullOrEmpty(this.internalIdentity.fpr) == false)
   353                     {
   354                         isEmpty = false;
   355                     }
   356                 }
   357 
   358                 // Skip Flags
   359                 // Skip Language
   360 
   361                 // Check UserId
   362                 if (isEmpty)
   363                 {
   364                     if (string.IsNullOrEmpty(this.internalIdentity.UserId) == false)
   365                     {
   366                         isEmpty = false;
   367                     }
   368                 }
   369 
   370                 // Check UserName
   371                 if (isEmpty)
   372                 {
   373                     if (string.IsNullOrEmpty(this.internalIdentity.UserName) == false)
   374                     {
   375                         isEmpty = false;
   376                     }
   377                 }
   378 
   379                 // Check IsForceUnencrypted
   380                 if (isEmpty)
   381                 {
   382                     if (this._IsForceUnencrypted != null)
   383                     {
   384                         isEmpty = false;
   385                     }
   386                 }
   387 
   388                 // Skip IsSmartAddressEnabled
   389 
   390                 // Check Members
   391                 if (isEmpty)
   392                 {
   393                     for (int i = 0; i < this._Members.Count; i++)
   394                     {
   395                         if (this._Members[i].IsEmpty == false)
   396                         {
   397                             isEmpty = false;
   398                             break;
   399                         }
   400                     }
   401                 }
   402 
   403                 return (isEmpty);
   404             }
   405         }
   406 
   407         /// <summary>
   408         /// Gets the true/false boolean value of the IsForceUnencrypted property 
   409         /// (null is considered false).
   410         /// </summary>
   411         public bool IsForceUnencryptedBool
   412         {
   413             get
   414             {
   415                 if ((this._IsForceUnencrypted == null) ||
   416                     ((bool)this._IsForceUnencrypted == false))
   417                 {
   418                     return (false);
   419                 }
   420                 else
   421                 {
   422                     return (true);
   423                 }
   424             }
   425         }
   426 
   427         /// <summary>
   428         /// Gets whether this identity is a group with individual members.
   429         /// </summary>
   430         public bool IsGroup
   431         {
   432             get { return (this._Members.Count > 0); }
   433         }
   434 
   435         /**************************************************************
   436          * 
   437          * Methods
   438          * 
   439          *************************************************************/
   440 
   441         /// <summary>
   442         /// Raises the property changed event, if possible, with the given arguments.
   443         /// </summary>
   444         /// <param name="propertyName">The name of the property that changed.</param>
   445         private void RaisePropertyChangedEvent(string propertyName)
   446         {
   447             this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   448             return;
   449         }
   450 
   451         /// <summary>
   452         /// Returns this pEp identity as a pEp engine identity.
   453         /// Copied data is used in the COM type.
   454         /// Note: This will not include any members of a group, 
   455         /// also the address used is the smart address.
   456         /// </summary>
   457         /// <returns>A pEp engine identity.</returns>
   458         public pEpIdentity ToCOMType()
   459         {
   460             pEpIdentity newIdent = new pEpIdentity();
   461 
   462             newIdent.Address = this.GetSmartAddress();
   463             newIdent.CommType = this.internalIdentity.CommType;
   464             newIdent.fpr = this.internalIdentity.fpr;
   465             newIdent.Flags = this.internalIdentity.Flags;
   466             newIdent.lang = this.internalIdentity.lang;
   467             newIdent.UserId = this.internalIdentity.UserId;
   468             newIdent.UserName = this.internalIdentity.UserName;
   469 
   470             return (newIdent);
   471         }
   472 
   473         /// <summary>
   474         /// Serves as a hash function for a particular type.
   475         /// </summary>
   476         /// <returns>A hash code for the current object.</returns>
   477         public override int GetHashCode()
   478         {
   479             return base.GetHashCode();
   480         }
   481 
   482         /// <summary>
   483         /// Indicates whether the current object is equal to another object of the same type.
   484         /// </summary>
   485         /// <param name="obj">The object to check equality with.</param>
   486         /// <returns>True if both objects are considered equal, otherwise false.</returns>
   487         public override bool Equals(object obj)
   488         {
   489             if ((obj == null) ||
   490                 !(obj is PEPIdentity))
   491             {
   492                 return (false);
   493             }
   494 
   495             return (this.Equals((PEPIdentity)obj));
   496         }
   497 
   498         /// <summary>
   499         /// Indicates whether the current object is equal to another object of the same type.
   500         /// </summary>
   501         /// <param name="obj">The object to check equality with.</param>
   502         /// <returns>True if both objects are considered equal, otherwise false.</returns>
   503         public bool Equals(PEPIdentity obj)
   504         {
   505             if (obj == null)
   506             {
   507                 return (false);
   508             }
   509 
   510             if (Comparisons.Equals(this.Address, obj.Address) &&
   511                 Comparisons.Equals(this.CommunicationType, obj.CommunicationType) &&
   512                 Comparisons.Equals(this.Fingerprint, obj.Fingerprint) &&
   513                 Comparisons.Equals(this.Language, obj.Language) &&
   514                 Comparisons.Equals(this.UserId, obj.UserId) &&
   515                 Comparisons.Equals(this.UserName, obj.UserName) &&
   516                 Comparisons.Equals(this.IsForceUnencrypted, obj.IsForceUnencrypted) &&
   517                 Comparisons.Equals(this.IsSmartAddressEnabled, obj.IsSmartAddressEnabled) &&
   518                 Comparisons.Equals(this.Members, obj.Members) &&
   519                 Comparisons.Equals(this.Rating, obj.Rating))
   520             {
   521                 return (true);
   522             }
   523 
   524             return (false);
   525         }
   526 
   527         /// <summary>
   528         /// Gets a deep copy of the object and all its data.
   529         /// </summary>
   530         /// <returns>The deep copy of the object.</returns>
   531         public PEPIdentity Copy()
   532         {
   533             PEPIdentity copy = new PEPIdentity();
   534 
   535             copy.Address = (this.internalIdentity.Address == null ? null : string.Copy(this.internalIdentity.Address));
   536             copy.CommunicationType = this.internalIdentity.CommType;
   537             copy.Fingerprint = (this.internalIdentity.fpr == null ? null : string.Copy(this.internalIdentity.fpr));
   538             copy.IsInDeviceGroup = this.internalIdentity.GetIsInDeviceGroup();
   539             copy.IsList = this.internalIdentity.GetIsList();
   540             copy.IsSyncEnabled = this.internalIdentity.GetIsSyncEnabled();
   541             copy.Language = (this.internalIdentity.lang == null ? null : string.Copy(this.internalIdentity.lang));
   542             copy.UserId = (this.internalIdentity.UserId == null ? null : string.Copy(this.internalIdentity.UserId));
   543             copy.UserName = (this.internalIdentity.UserName == null ? null : string.Copy(this.internalIdentity.UserName));
   544 
   545             // IsForceUnencrypted
   546             if (this._IsForceUnencrypted != null)
   547             {
   548                 copy.IsForceUnencrypted = (bool)this.IsForceUnencrypted;
   549             }
   550             else
   551             {
   552                 copy.IsForceUnencrypted = null;
   553             }
   554 
   555             copy.IsSmartAddressEnabled = this.IsSmartAddressEnabled;
   556 
   557             // Members
   558             copy.Members.Clear();
   559             for (int i = 0; i < this._Members.Count; i++)
   560             {
   561                 copy.Members.Add(this._Members[i].Copy());
   562             }
   563 
   564             copy.Rating = this._Rating;
   565 
   566             return (copy);
   567         }
   568 
   569         /// <summary>
   570         /// Indicates whether the current PEPIdentity is equal to another PEPIdentity by address only (case insensitive).
   571         /// Note: The address used is the smart address.
   572         /// Note: If either (or both) address is null or empty, they are considered NOT equal.
   573         /// </summary>
   574         /// <param name="obj">The PEPIdentity to compare against.</param>
   575         /// <returns>True if the given address and this object's address are neither null nor empty and considered equal, otherwise false.</returns>
   576         public bool EqualsByAddress(PEPIdentity obj)
   577         {
   578             bool areEqual = false;
   579             string addr = this.GetSmartAddress();
   580 
   581             if ((obj != null) &&
   582                 (string.IsNullOrEmpty(obj.Address) == false) &&
   583                 (string.IsNullOrEmpty(addr) == false))
   584             {
   585                 areEqual = string.Equals(addr.Trim(), obj.Address.Trim(), StringComparison.OrdinalIgnoreCase);
   586             }
   587 
   588             return (areEqual);
   589         }
   590 
   591         /// <summary>
   592         /// Indicates whether the current PEPIdentity is equal to an address only (case insensitive).
   593         /// Note: The address used is the smart address.
   594         /// Note: If either (or both) address is null or empty, they are considered NOT equal.
   595         /// </summary>
   596         /// <param name="address">The address to compare against.</param>
   597         /// <returns>True if the given address and this object's address are neither null nor empty and considered equal, otherwise false.</returns>
   598         public bool EqualsByAddress(string address)
   599         {
   600             bool areEqual = false;
   601             string addr = this.GetSmartAddress();
   602 
   603             if ((string.IsNullOrEmpty(address) == false) &&
   604                 (string.IsNullOrEmpty(addr) == false))
   605             {
   606                 areEqual = string.Equals(addr.Trim(), address.Trim(), StringComparison.OrdinalIgnoreCase);
   607             }
   608 
   609             return (areEqual);
   610         }
   611 
   612         /// <summary>
   613         /// Recursivley converts the given identity into a 'flat' list of any members.
   614         /// This will remove groups (hierarchy) and convert a group into its members.
   615         /// </summary>
   616         /// <param name="identity">The identity to get the flat list for.</param>
   617         /// <returns>The flat list of pEp identities in the identity.</returns>
   618         public List<PEPIdentity> ToFlatList()
   619         {
   620             List<PEPIdentity> result = new List<PEPIdentity>();
   621             this.RecToFlatList(this, result);
   622             return (result);
   623         }
   624 
   625         /// <summary>
   626         /// Recursively adds members of any group in the given identity to the list.
   627         /// </summary>
   628         /// <param name="identity">The identity to process.</param>
   629         /// <param name="list">The list to add identities to.</param>
   630         private void RecToFlatList(PEPIdentity identity,
   631                                    List<PEPIdentity> list)
   632         {
   633             List<PEPIdentity> memberList;
   634 
   635             if (identity.IsGroup)
   636             {
   637                 for (int i = 0; i < identity.Members.Count; i++)
   638                 {
   639                     memberList = new List<PEPIdentity>();
   640                     this.RecToFlatList(identity.Members[i], memberList);
   641 
   642                     for (int j = 0; j < memberList.Count; j++)
   643                     {
   644                         list.Add(memberList[j]);
   645                     }
   646                 }
   647             }
   648             else
   649             {
   650                 list.Add(identity.Copy());
   651             }
   652 
   653             return;
   654         }
   655 
   656         /// <summary>
   657         /// Gets the calculated smart address for this identity.
   658         /// If smart address is enabled, and the set address is empty/null, the user name may be returned as the address
   659         /// if it's formatted as an address.
   660         /// </summary>
   661         /// <returns>The calculated 'smart' adress for this identity.</returns>
   662         private string GetSmartAddress()
   663         {
   664             string addr = this.internalIdentity.Address;
   665 
   666             // If smart address processing is enabled, and the internal address is null/empty,
   667             // check if the user name is valid and return it as the address.
   668             if ((this._IsSmartAddressEnabled) &&
   669                 (string.IsNullOrWhiteSpace(this.internalIdentity.Address)))
   670             {
   671                 if (PEPIdentity.GetIsAddressValid(this.internalIdentity.UserName))
   672                 {
   673                     addr = this.internalIdentity.UserName;
   674                 }
   675             }
   676 
   677             return (addr);
   678         }
   679 
   680         /// <summary>
   681         /// Returns a string that represents this identity.
   682         /// Note: The address used is the smart address.
   683         /// </summary>
   684         /// <returns>The string representing the identity which can be empty.</returns>
   685         public override string ToString()
   686         {
   687             string result = "";
   688             string usrname = this.internalIdentity.UserName;
   689             string addr = this.GetSmartAddress();
   690 
   691             if ((string.IsNullOrWhiteSpace(usrname) == true) &&
   692                 (string.IsNullOrWhiteSpace(addr) == true))
   693             {
   694                 // Do nothing, return empty
   695             }
   696             else if ((string.IsNullOrWhiteSpace(usrname) == true) &&
   697                      (string.IsNullOrWhiteSpace(addr) == false))
   698             {
   699                 result = addr.Trim();
   700             }
   701             else if ((string.IsNullOrWhiteSpace(usrname) == false) &&
   702                      (string.IsNullOrWhiteSpace(addr) == true))
   703             {
   704                 result = usrname.Trim();
   705             }
   706             else if ((string.IsNullOrWhiteSpace(usrname) == false) &&
   707                      (string.IsNullOrWhiteSpace(addr) == false))
   708             {
   709                 if (usrname.Trim() == addr.Trim())
   710                 {
   711                     result = usrname.Trim();
   712                 }
   713                 else
   714                 {
   715                     result = usrname.Trim() + " <" + addr.Trim() + ">";
   716                 }
   717             }
   718 
   719             return (result);
   720         }
   721 
   722         /// <summary>
   723         /// Returns a string that represents this identity specifically for display.
   724         /// This will use the user name if it exists, otherwise address.
   725         /// </summary>
   726         /// <returns>The display string representing the identity which can be empty.</returns>
   727         public string ToDisplayString()
   728         {
   729             string result = "";
   730 
   731             if (string.IsNullOrWhiteSpace(this.internalIdentity.UserName) == false)
   732             {
   733                 result = this.internalIdentity.UserName.Trim();
   734             }
   735             // Do not use the smart address (no need since user name is tried first)
   736             else if (string.IsNullOrWhiteSpace(this.internalIdentity.Address) == false)
   737             {
   738                 result = this.internalIdentity.Address.Trim();
   739             }
   740 
   741             return (result);
   742         }
   743 
   744         /**************************************************************
   745          * 
   746          * Static Methods
   747          * 
   748          *************************************************************/
   749 
   750         /// <summary>
   751         /// Parses the given string as a new PEPIdentity. This will never return null.
   752         /// The string should be of the format 'user name &lt;email@address.com&gt;'
   753         /// If the string does not have both &lt; and &gt; it will be read as an address if it is a valid address, 
   754         /// otherwise a user name.
   755         /// </summary>
   756         /// <param name="str">The string to parse as a PEPIdentity.</param>
   757         /// <returns>The string parsed as a PEPIdentity, otherwise null if failure.</returns>
   758         public static PEPIdentity Parse(string str)
   759         {
   760             int addressStartSymbol;
   761             int addressEndSymbol;
   762             string workingStr;
   763             string userName;
   764             string address;
   765             string[] temp;
   766             PEPIdentity result = new PEPIdentity();
   767 
   768             if (string.IsNullOrWhiteSpace(str) == false)
   769             {
   770                 workingStr = str.Trim();
   771                 addressStartSymbol = workingStr.IndexOf("<");
   772                 addressEndSymbol = workingStr.IndexOf(">");
   773 
   774                 if ((addressStartSymbol < 0) &&
   775                     (addressEndSymbol < 0))
   776                 {
   777                     if (PEPIdentity.GetIsAddressValid(workingStr))
   778                     {
   779                         // Only address
   780                         result = new PEPIdentity();
   781                         result.Address = workingStr;
   782                     }
   783                     else
   784                     {
   785                         // Only user name
   786                         result = new PEPIdentity();
   787                         result.UserName = workingStr;
   788                     }
   789                 }
   790                 else if ((addressStartSymbol >= 0) &&
   791                          (addressEndSymbol >= 0) &&
   792                          (addressEndSymbol > addressStartSymbol) &&
   793                          (workingStr.LastIndexOf("<") == addressStartSymbol) &&
   794                          (workingStr.LastIndexOf(">") == addressEndSymbol))
   795                 {
   796                     temp = workingStr.Split('<');
   797 
   798                     if (temp.Length == 2)
   799                     {
   800                         userName = temp[0].Trim();
   801                         address = temp[1].Replace(">", "").Trim();
   802 
   803                         // Require both the user name and address to exist
   804                         if ((string.IsNullOrWhiteSpace(userName) == false) &&
   805                             (PEPIdentity.GetIsAddressValid(address)))
   806                         {
   807                             result = new PEPIdentity();
   808                             result.UserName = userName;
   809                             result.Address = address;
   810                         }
   811                     }
   812                 }
   813             }
   814 
   815             return (result);
   816         }
   817 
   818         /// <summary>
   819         /// Gets whether the given address string is considered valid based on its format.
   820         /// This can only validate the basic format of the address (xxxx@xxxx.xxx).
   821         /// </summary>
   822         /// <param name="address">The address string to determine if it's valid.</param>
   823         /// <returns>True if the address string's format is valid, otherwise false.</returns>
   824         public static bool GetIsAddressValid(string address)
   825         {
   826             if ((string.IsNullOrWhiteSpace(address) == false) &&
   827                 (address.Contains("@")))
   828             {
   829                 return (true);
   830             }
   831 
   832             return (false);
   833         }
   834 
   835         /// <summary>
   836         /// Gets whether the given address represents an own personal identity (myself).
   837         /// This is done by comparing against the current session's account list.
   838         /// If a registered account's SMTP address, or current user address, matches the given address (case insensitive), it is a match.
   839         /// This means it should work for both Exchange formatted and normal address strings.
   840         /// </summary>
   841         /// <param name="address">The email address to check if it is an own identity.</param>
   842         /// <returns>True if the given address represents an own identity, otherwise false.</returns>
   843         public static bool GetIsOwnIdentity(string address)
   844         {
   845             bool isMyself = false;
   846 
   847             try
   848             {
   849                 isMyself = Globals.ThisAddIn.Settings.AccountSettingsList.Any(a => (a.SmtpAddress?.Equals(address) == true));
   850             }
   851             catch (Exception ex)
   852             {
   853                 isMyself = false;
   854                 Log.Error("GetIsOwnIdentity: Error determining if own identity. " + ex.ToString());
   855             }
   856 
   857             return isMyself;
   858         }
   859 
   860         /// <summary>
   861         /// Gets an own identity using the given email address (can handle exchange encoded addresses).
   862         /// The account for the email address must already exist in Outlook.
   863         /// Warning: The result can be null.
   864         /// </summary>
   865         /// <param name="address">The email address to get the own identity from.</param>
   866         /// <param name="ownIdentity">The output own identity (may be null).</param>
   867         /// <returns>The own identity or null.</returns>
   868         public static Globals.ReturnStatus GetOwnIdentity(string address,
   869                                                           out PEPIdentity ownIdentity)
   870         {
   871             pEpIdentity ident;
   872             PEPIdentity ownBaseIdentity = null;
   873             PEPIdentity own = null;
   874             Outlook.Account account = null;
   875             Globals.ReturnStatus sts;
   876             Globals.ReturnStatus status = Globals.ReturnStatus.Failure;
   877 
   878             try
   879             {
   880                 account = PEPIdentity.GetOwnAccount(address);
   881 
   882                 // Get the identity of the account, ignore any errors
   883                 sts = PEPIdentity.GetOwnIdentity(account, out ownBaseIdentity);
   884 
   885                 // Update the identity in the pEp engine (and get fingerprint)
   886                 if (ownBaseIdentity != null)
   887                 {
   888                     try
   889                     {
   890                         ident = ThisAddIn.PEPEngine.Myself(ownBaseIdentity.ToCOMType());
   891                         own = new PEPIdentity(ident);
   892                         status = Globals.ReturnStatus.Success;
   893                     }
   894                     catch
   895                     {
   896                         own = null;
   897                         status = Globals.ReturnStatus.Failure;
   898                     }
   899                 }
   900             }
   901             catch (Exception ex)
   902             {
   903                 status = Globals.ReturnStatus.Failure;
   904                 Log.Error("GetOwnIdentity: Failure occured, " + ex.ToString());
   905             }
   906             finally
   907             {
   908                 if (account != null)
   909                 {
   910                     // Marshal.ReleaseComObject(account);
   911                     account = null;
   912                 }
   913             }
   914 
   915             ownIdentity = own;
   916             return (status);
   917         }
   918 
   919         /// <summary>
   920         /// Gets the Outlook account in this session that corresponds with the given address.
   921         /// If a registered account's SMTP address, or current user address, matches the given address (case insensitive), it is a match.
   922         /// This means it should work for both Exchange formatted and normal address strings.
   923         /// Warning: If multiple accounts have the same address this will only return the first one.
   924         /// </summary>
   925         /// <param name="address">The email address to find the account for.</param>
   926         /// <returns>The own Outlook Acccount associated with given address, otherwise null.
   927         /// The caller has ownership of the returned account.</returns>
   928         public static Outlook.Account GetOwnAccount(string address)
   929         {
   930             bool accountFound = false;
   931             string workingAddress;
   932             string accountAddress;
   933             string currUserAddress;
   934             Outlook.Account account = null;
   935             Outlook.Accounts accounts = null;
   936             Outlook.NameSpace ns = null;
   937             Outlook.Recipient currUser = null;
   938 
   939             /* Note: For exchange accounts the given address may not be an actual address unless the caller correctly 
   940              * gets the exchange user then the exchange user's address.
   941              * However, this function can handle both situations by simply checking against both the account smtp address
   942              * and the account's current user address.
   943              * If an exchange encoded address is given, it should match against the current user which also will have an
   944              * exchange encoded address. Otherwise, it should match against the account smpt address.
   945              */
   946 
   947             try
   948             {
   949                 if (string.IsNullOrWhiteSpace(address) == false)
   950                 {
   951                     ns = Globals.ThisAddIn.Application.Session;
   952                     accounts = ns.Accounts;
   953                     workingAddress = address.Trim();
   954 
   955                     // Look in existing accounts
   956                     for (int i = 1; i <= accounts.Count; i++)
   957                     {
   958                         account = accounts[i];
   959                         currUser = account.CurrentUser;
   960                         accountAddress = (account.SmtpAddress != null ? account.SmtpAddress.Trim() : "");
   961                         currUserAddress = (currUser.Address != null ? currUser.Address.Trim() : "");
   962 
   963                         // Match by current user or account account
   964                         if ((string.IsNullOrEmpty(accountAddress) == false) &&
   965                             (string.IsNullOrEmpty(currUserAddress) == false) &&
   966                             ((string.Equals(workingAddress, currUserAddress, StringComparison.OrdinalIgnoreCase)) ||
   967                              (string.Equals(workingAddress, accountAddress, StringComparison.OrdinalIgnoreCase))))
   968                         {
   969                             accountFound = true;
   970                         }
   971 
   972                         if (currUser != null)
   973                         {
   974                             // Marshal.ReleaseComObject(currUser);
   975                             currUser = null;
   976                         }
   977 
   978                         // Do not release the account if it was a match
   979                         if ((account != null) &&
   980                             (accountFound == false))
   981                         {
   982                             // Marshal.ReleaseComObject(account);
   983                             account = null;
   984                         }
   985 
   986                         if (accountFound)
   987                         {
   988                             break;
   989                         }
   990                     }
   991                 }
   992             }
   993             catch (Exception ex)
   994             {
   995                 Log.Error("GetOwnAccount: Failure occured, " + ex.ToString());
   996             }
   997             finally
   998             {
   999                 // Do not release the account if it was a match
  1000                 if ((account != null) &&
  1001                     (accountFound == false))
  1002                 {
  1003                     // Marshal.ReleaseComObject(account);
  1004                     account = null;
  1005                 }
  1006 
  1007                 if (accounts != null)
  1008                 {
  1009                     // Marshal.ReleaseComObject(accounts);
  1010                     accounts = null;
  1011                 }
  1012 
  1013                 if (ns != null)
  1014                 {
  1015                     // Marshal.ReleaseComObject(ns);
  1016                     ns = null;
  1017                 }
  1018 
  1019                 if (currUser != null)
  1020                 {
  1021                     // Marshal.ReleaseComObject(currUser);
  1022                     currUser = null;
  1023                 }
  1024             }
  1025 
  1026             return (account);
  1027         }
  1028 
  1029         /// <summary>
  1030         /// Gets the personal identity using the given cryptable mail item.
  1031         /// An account for the personal identity does not need to exist in Outlook.
  1032         /// Warning: The result can be null.
  1033         /// </summary>
  1034         /// <param name="omi">The mail item to get the personal identity from.</param>
  1035         /// <param name="ownIdentity">The output own identity (may be null).</param>
  1036         /// <returns>The status of the method.</returns>
  1037         public static Globals.ReturnStatus GetOwnIdentity(Outlook.MailItem omi,
  1038                                                           out PEPIdentity ownIdentity)
  1039         {
  1040             string entryID;
  1041             string address = null;
  1042             string userName = null;
  1043             pEpIdentity ident;
  1044             PEPIdentity own = null;
  1045             PEPIdentity from = null;
  1046             Outlook.Account account = null;
  1047             Outlook.Accounts accounts = null;
  1048             Outlook.NameSpace ns = null;
  1049             Outlook.Recipient currUser = null;
  1050             Globals.ReturnStatus status = Globals.ReturnStatus.Failure;
  1051             Globals.ReturnStatus sts;
  1052 
  1053             try
  1054             {
  1055                 if (omi.GetIsIncoming())
  1056                 {
  1057                     entryID = omi.ReceivedByEntryID;
  1058                     ns = Globals.ThisAddIn.Application.Session;
  1059                     accounts = ns.Accounts;
  1060 
  1061                     // Attempt to get address by entry ID
  1062                     for (int i = 1; i <= accounts.Count; i++)
  1063                     {
  1064                         account = accounts[i];
  1065                         currUser = account.CurrentUser;
  1066 
  1067                         if (currUser != null)
  1068                         {
  1069                             if (currUser.EntryID == entryID)
  1070                             {
  1071                                 address = account.SmtpAddress;
  1072                                 break;
  1073                             }
  1074 
  1075                             // Marshal.ReleaseComObject(currUser);
  1076                             currUser = null;
  1077                         }
  1078 
  1079                         // Marshal.ReleaseComObject(account);
  1080                         account = null;
  1081                     }
  1082 
  1083                     // Attempt to get the address from the MAPI property
  1084                     if (string.IsNullOrWhiteSpace(address))
  1085                     {
  1086                         address = (string)MapiHelper.GetProperty(omi, MapiProperty.PidTagReceivedByEmailAddress);
  1087                         if (string.IsNullOrEmpty(address) ||
  1088                             (address[0] == '/'))
  1089                         {
  1090                             address = null;
  1091                         }
  1092                     }
  1093 
  1094                     // Attempt to find the account, test by containing folder
  1095                     if (string.IsNullOrWhiteSpace(address))
  1096                     {
  1097                         Outlook.Folder parent = omi.Parent;
  1098                         Outlook.Store store = parent.Store;
  1099                         foreach (Outlook.Account a in accounts)
  1100                         {
  1101                             if (store.StoreID == a.DeliveryStore.StoreID)
  1102                             {
  1103                                 address = a.SmtpAddress;
  1104                                 break;
  1105                             }
  1106                         }
  1107                         // Marshal.ReleaseComObject(parent);
  1108                     }
  1109 
  1110                     // Attempt to get the email address from the user property. Do not use it if it is an internal
  1111                     // Exchange X500 address ("/O=DOMAIN/OU=EXCHANGE ADMINISTRATIVE GROUP...").
  1112                     if (string.IsNullOrWhiteSpace(address))
  1113                     {
  1114                         string addr = omi.GetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName) as string;
  1115                         if ((string.IsNullOrWhiteSpace(addr) == false) &&
  1116                             (addr.StartsWith("/O") == false))
  1117                         {
  1118                             address = addr;
  1119                         }
  1120                     }
  1121 
  1122                     // nothing worked, use default
  1123                     if (string.IsNullOrWhiteSpace(address))
  1124                     {
  1125                         address = accounts[1].SmtpAddress;
  1126                     }
  1127 
  1128                     sts = PEPIdentity.GetOwnIdentity(address, out own);
  1129                     status = sts; // Can return the just calculated value, therefore set status
  1130 
  1131                     /* Try using the mail item received-by user property information directly.
  1132                      * Only incoming messages can do this, outgoing 'myself' identites must exist after GetMyIdentity(address).
  1133                      */
  1134                     if ((own == null) ||
  1135                         (sts != Globals.ReturnStatus.Success))
  1136                     {
  1137                         address = (string)omi.GetUserProperty(MapiProperty.PidTagReceivedByEmailAddress.DaslName);
  1138                         userName = (string)omi.GetUserProperty(MapiProperty.PidTagReceivedByName.DaslName);
  1139 
  1140                         if (string.IsNullOrEmpty(address) == false)
  1141                         {
  1142                             own = new PEPIdentity();
  1143                             own.Address = address;
  1144                             own.UserName = userName;
  1145                             own.UserId = Globals.ThisAddIn.GetUserId(own.Address); // Not using 'myself ID'
  1146 
  1147                             // Update the identity in the pEp engine (and get fingerprint)
  1148                             try
  1149                             {
  1150                                 ident = ThisAddIn.PEPEngine.Myself(own.ToCOMType());
  1151                                 own = new PEPIdentity(ident);
  1152                                 status = Globals.ReturnStatus.Success;
  1153                             }
  1154                             catch
  1155                             {
  1156                                 own = null;
  1157                                 status = Globals.ReturnStatus.Failure;
  1158                             }
  1159                         }
  1160                     }
  1161                 }
  1162                 else // Outgoing
  1163                 {
  1164                     sts = PEPIdentity.GetFromIdentity(omi, out from);
  1165 
  1166                     // Default
  1167                     if ((from == null) ||
  1168                         (from.IsEmpty) ||
  1169                         (sts != Globals.ReturnStatus.Success))
  1170                     {
  1171                         sts = PEPIdentity.GetDefaultFromIdentity(out from);
  1172                     }
  1173 
  1174                     if ((from != null) &&
  1175                         (sts == Globals.ReturnStatus.Success))
  1176                     {
  1177                         /* Note: while From could be used directly, call GetMyIdentity on the address to ensure
  1178                          * it is an active account in Outlook or registered if new.
  1179                          */
  1180                         status = PEPIdentity.GetOwnIdentity(from.Address, out own);
  1181                     }
  1182                     else
  1183                     {
  1184                         own = null;
  1185                         status = Globals.ReturnStatus.Failure;
  1186                     }
  1187                 }
  1188             }
  1189             catch (Exception ex)
  1190             {
  1191                 status = Globals.ReturnStatus.Failure;
  1192                 Log.Error("GetOwnIdentity: Failure occured, " + ex.ToString());
  1193             }
  1194             finally
  1195             {
  1196                 // Release objects
  1197                 if (account != null)
  1198                 {
  1199                     // Marshal.ReleaseComObject(account);
  1200                     account = null;
  1201                 }
  1202 
  1203                 if (accounts != null)
  1204                 {
  1205                     // Marshal.ReleaseComObject(accounts);
  1206                     accounts = null;
  1207                 }
  1208 
  1209                 if (ns != null)
  1210                 {
  1211                     // Marshal.ReleaseComObject(ns);
  1212                     ns = null;
  1213                 }
  1214 
  1215                 if (currUser != null)
  1216                 {
  1217                     // Marshal.ReleaseComObject(currUser);
  1218                     currUser = null;
  1219                 }
  1220             }
  1221 
  1222             ownIdentity = own;
  1223             return (status);
  1224         }
  1225 
  1226         /// <summary>
  1227         /// Gets an own identity using the given Outlook account.
  1228         /// Warning: The result can be null, fingerprint is not returned.
  1229         /// </summary>
  1230         /// <param name="account">The account to get the own identity from.</param>
  1231         /// <param name="ownIdentity">The output own identity (may be null).</param>
  1232         /// <returns>The own identity or null.</returns>
  1233         public static Globals.ReturnStatus GetOwnIdentity(Outlook.Account account,
  1234                                                           out PEPIdentity ownIdentity)
  1235         {
  1236             PEPIdentity newIdentity = null;
  1237             Outlook.Recipient currUser = null;
  1238             Outlook.ExchangeUser currExchUser = null;
  1239             Outlook.AddressEntry currAddrEntry = null;
  1240             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  1241 
  1242             try
  1243             {
  1244                 if (account != null)
  1245                 {
  1246                     currUser = account.CurrentUser;
  1247 
  1248                     // Attempt to get the address entry
  1249                     try
  1250                     {
  1251                         currAddrEntry = currUser.AddressEntry;
  1252                     }
  1253                     catch
  1254                     {
  1255                         currAddrEntry = null;
  1256                     }
  1257 
  1258                     // Try to get an exchange user
  1259                     if ((currAddrEntry != null) &&
  1260                         (string.Equals(currAddrEntry.Type, "EX", StringComparison.OrdinalIgnoreCase)))
  1261                     {
  1262                         try
  1263                         {
  1264                             currExchUser = currAddrEntry.GetExchangeUser();
  1265                         }
  1266                         catch
  1267                         {
  1268                             // Assume the failure was because there is no connection to exchange
  1269                             status = Globals.ReturnStatus.FailureNoConnection;
  1270                         }
  1271                     }
  1272 
  1273                     // Create identity
  1274                     newIdentity = new PEPIdentity();
  1275                     newIdentity.Address = account.SmtpAddress?.Trim();
  1276 
  1277                     // Use Exchange information
  1278                     if (currExchUser != null)
  1279                     {
  1280                         newIdentity.UserName = currExchUser.Name?.Trim();
  1281                     }
  1282                     // Use current user recipient information
  1283                     else if (currUser != null)
  1284                     {
  1285                         newIdentity.UserName = currUser.Name?.Trim();
  1286                     }
  1287 
  1288                     // Add user ID
  1289                     newIdentity.UserId = Globals.ThisAddIn.GetUserId(newIdentity.Address);
  1290                 }
  1291             }
  1292             catch (Exception ex)
  1293             {
  1294                 status = Globals.ReturnStatus.Failure;
  1295                 Log.Error("GetOwnIdentity: Failure occured, " + ex.ToString());
  1296             }
  1297             finally
  1298             {
  1299                 if (currUser != null)
  1300                 {
  1301                     // Marshal.ReleaseComObject(currUser);
  1302                     currUser = null;
  1303                 }
  1304 
  1305                 if (currExchUser != null)
  1306                 {
  1307                     // Marshal.ReleaseComObject(currExchUser);
  1308                     currExchUser = null;
  1309                 }
  1310 
  1311                 if (currAddrEntry != null)
  1312                 {
  1313                     // Marshal.ReleaseComObject(currAddrEntry);
  1314                     currAddrEntry = null;
  1315                 }
  1316             }
  1317 
  1318             ownIdentity = newIdentity;
  1319             return (status);
  1320         }
  1321 
  1322         /// <summary>
  1323         /// Gets the from/sender user name from the given outlook mail item.
  1324         /// Only the sender fields of the outlook mail item will be used (SendUsingAccount is ignored).
  1325         /// This should only be used for messages that have already been sent/received (not draft).
  1326         /// Warning: The result can be null.
  1327         /// </summary>
  1328         /// <param name="omi">The outlook mail item to get the from/sender name from.</param>
  1329         /// <param name="fromUserName">The output from/sender user name (may be null).</param>
  1330         /// <returns>The status of the method.</returns>
  1331         public static Globals.ReturnStatus GetFromUserName(Outlook.MailItem omi,
  1332                                                            out string fromUserName)
  1333         {
  1334             string userName = null;
  1335             Outlook.AddressEntry sender = null;
  1336             Outlook.ExchangeUser exchSender = null;
  1337             Globals.ReturnStatus status = Globals.ReturnStatus.Failure;
  1338 
  1339             // Note: see https://msdn.microsoft.com/en-us/library/office/ff184624.aspx
  1340             try
  1341             {
  1342                 try
  1343                 {
  1344                     sender = omi.Sender;
  1345                 }
  1346                 catch { }
  1347 
  1348                 // Try to handle exchange using an exchange user
  1349                 if ((status != Globals.ReturnStatus.Success) &&
  1350                     (omi.SenderEmailType == "EX") &&
  1351                     (sender != null))
  1352                 {
  1353                     // Common failures occur here when an Exchange server is unavailable but required.
  1354                     // ExchangeConnectionMode could be checked but a global try/catch is sufficient.
  1355                     try
  1356                     {
  1357                         if (sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry ||
  1358                             sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeRemoteUserAddressEntry)
  1359                         {
  1360                             exchSender = sender.GetExchangeUser();
  1361                             if (exchSender != null)
  1362                             {
  1363                                 userName = exchSender.Name;
  1364                                 status = Globals.ReturnStatus.Success;
  1365                             }
  1366                             else
  1367                             {
  1368                                 // Failed to access Exchange sender information
  1369                                 // Other methods will still be tried, if they fail too null will be returned
  1370                             }
  1371                         }
  1372                         else
  1373                         {
  1374                             userName = sender.Name;
  1375                             status = Globals.ReturnStatus.Success;
  1376                         }
  1377                     }
  1378                     catch
  1379                     {
  1380                         // Assume the failure was because there is no connection to exchange
  1381                         // Other methods will still be tried, if they fail too null will be returned
  1382                         status = Globals.ReturnStatus.FailureNoConnection;
  1383                     }
  1384                 }
  1385 
  1386                 // Try using the mail item properties (non-exchange)
  1387                 if ((status != Globals.ReturnStatus.Success) &&
  1388                     (omi.SenderEmailType != "EX") &&
  1389                     (omi.SenderEmailAddress != null))
  1390                 {
  1391                     userName = omi.SenderName;
  1392                     status = Globals.ReturnStatus.Success;
  1393                 }
  1394             }
  1395             catch (Exception ex)
  1396             {
  1397                 status = Globals.ReturnStatus.Failure;
  1398                 Log.Error("GetFromUsername: Failure occured, " + ex.ToString());
  1399             }
  1400             finally
  1401             {
  1402                 // Release objects
  1403                 if (sender != null)
  1404                 {
  1405                     // Marshal.ReleaseComObject(sender);
  1406                     sender = null;
  1407                 }
  1408 
  1409                 if (exchSender != null)
  1410                 {
  1411                     // Marshal.ReleaseComObject(exchSender);
  1412                     exchSender = null;
  1413                 }
  1414             }
  1415 
  1416             fromUserName = userName;
  1417             return (status);
  1418         }
  1419 
  1420         /// <summary>
  1421         /// Gets the from/sender identity from the given outlook mail item.
  1422         /// Warning: The result can be null.
  1423         /// </summary>
  1424         /// <param name="omi">The outlook mail item to get the from/sender identity from.</param>
  1425         /// <param name="fromIdentity">The output from/sender identity (may be null).</param>
  1426         /// <returns>The status of the method.</returns>
  1427         public static Globals.ReturnStatus GetFromIdentity(Outlook.MailItem omi,
  1428                                                            out PEPIdentity fromIdentity)
  1429         {
  1430             bool exchangeUser = false;
  1431             PEPIdentity from = null;
  1432             Outlook.ContactItem contact = null;
  1433             Outlook.AddressEntry sender = null;
  1434             Outlook.ExchangeUser exchSender = null;
  1435             Outlook.Account sendingAccount = null;
  1436             Globals.ReturnStatus status = Globals.ReturnStatus.Failure;
  1437 
  1438             // Note: see https://msdn.microsoft.com/en-us/library/office/ff184624.aspx
  1439             try
  1440             {
  1441                 // Probably incoming  if sender not null
  1442                 if (omi.Sender != null)
  1443                 {
  1444                     // Get sender
  1445                     try
  1446                     {
  1447                         sender = omi.Sender;
  1448                     }
  1449                     catch { }
  1450 
  1451                     if (omi.SenderEmailType == "EX")
  1452                     {
  1453                         exchangeUser = true;
  1454                     }
  1455                     /* SenderEmailType showed to be null in some occasions. Therefore, we need to 
  1456                      * doublecheck if this is an Exchange user.
  1457                      */
  1458                     else if (omi.SenderEmailType == null)
  1459                     {
  1460                         try
  1461                         {
  1462                             // Exchange address types, see: https://msdn.microsoft.com/en-us/library/office/ff868214.aspx
  1463                             if ((sender != null) &&
  1464                                 ((int)sender.AddressEntryUserType <= 5))
  1465                             {
  1466                                 exchangeUser = true;
  1467                             }
  1468                         }
  1469                         catch (Exception ex)
  1470                         {
  1471                             exchangeUser = false;
  1472                             Log.Error("GetFromIdentity: Error getting email type. " + ex.ToString());
  1473                         }
  1474                     }
  1475 
  1476                     // Try to handle exchange using an exchange user
  1477                     if ((status != Globals.ReturnStatus.Success) &&
  1478                          exchangeUser &&
  1479                         (sender != null))
  1480                     {
  1481                         // Common failures occur here when an Exchange server is unavailable but required.
  1482                         // ExchangeConnectionMode could be checked but a global try/catch is sufficient.
  1483                         try
  1484                         {
  1485                             if (sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeUserAddressEntry ||
  1486                                 sender.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeRemoteUserAddressEntry)
  1487                             {
  1488                                 // Use the ExchangeUser object PrimarySmtpAddress
  1489                                 exchSender = sender.GetExchangeUser();
  1490                                 if (exchSender != null)
  1491                                 {
  1492                                     from = new PEPIdentity();
  1493                                     from.Address = exchSender.PrimarySmtpAddress;
  1494                                     from.UserName = exchSender.Name;
  1495 
  1496                                     // Add the contact force unencrypted property
  1497                                     try
  1498                                     {
  1499                                         contact = exchSender.GetContact();
  1500 
  1501                                         if (contact != null)
  1502                                         {
  1503                                             from.IsForceUnencrypted = contact.GetForceUnencrypted();
  1504                                             // Marshal.ReleaseComObject(contact);
  1505                                             contact = null;
  1506                                         }
  1507                                     }
  1508                                     catch { }
  1509 
  1510                                     // Add the user ID
  1511                                     from.UserId = Globals.ThisAddIn.GetUserId(from.Address, contact, sender);
  1512 
  1513                                     status = Globals.ReturnStatus.Success;
  1514                                 }
  1515                                 else
  1516                                 {
  1517                                     // Failed to access Exchange sender information
  1518                                     // Other methods will still be tried, if they fail too null will be returned
  1519                                 }
  1520                             }
  1521                             else
  1522                             {
  1523                                 from = new PEPIdentity();
  1524                                 from.Address = (string)MapiHelper.GetProperty(sender, MapiProperty.PidTagSmtpAddress);
  1525                                 from.UserName = sender.Name;
  1526 
  1527                                 // Add the contact force unencrypted property
  1528                                 try
  1529                                 {
  1530                                     contact = sender.GetContact();
  1531 
  1532                                     if (contact != null)
  1533                                     {
  1534                                         from.IsForceUnencrypted = contact.GetForceUnencrypted();
  1535                                         // Marshal.ReleaseComObject(contact);
  1536                                         contact = null;
  1537                                     }
  1538                                 }
  1539                                 catch { }
  1540 
  1541                                 // Add the user ID
  1542                                 from.UserId = Globals.ThisAddIn.GetUserId(from.Address, contact, sender);
  1543 
  1544                                 status = Globals.ReturnStatus.Success;
  1545                             }
  1546                         }
  1547                         catch
  1548                         {
  1549                             // Assume the failure was because there is no connection to exchange
  1550                             // Other methods will still be tried, if they fail too null will be returned
  1551                             status = Globals.ReturnStatus.FailureNoConnection;
  1552                         }
  1553                     }
  1554 
  1555                     // Try using the mail item properties (non-exchange)
  1556                     if ((status != Globals.ReturnStatus.Success) &&
  1557                         (exchangeUser == false) &&
  1558                         (omi.SenderEmailAddress != null))
  1559                     {
  1560                         from = new PEPIdentity();
  1561                         from.Address = omi.SenderEmailAddress;
  1562                         from.UserName = omi.SenderName;
  1563 
  1564                         // Add the contact force unencrypted property
  1565                         try
  1566                         {
  1567                             contact = sender.GetContact();
  1568 
  1569                             if (contact != null)
  1570                             {
  1571                                 from.IsForceUnencrypted = contact.GetForceUnencrypted();
  1572                                 // Marshal.ReleaseComObject(contact);
  1573                                 contact = null;
  1574                             }
  1575                         }
  1576                         catch { }
  1577 
  1578                         // Add the user ID
  1579                         from.UserId = Globals.ThisAddIn.GetUserId(from.Address, contact, sender);
  1580 
  1581                         status = Globals.ReturnStatus.Success;
  1582                     }
  1583                 }
  1584                 else if (omi.GetIsIncoming() == false) // Probably outgoing if sender is null
  1585                 {
  1586                     sendingAccount = omi.SendUsingAccount;
  1587                     if (sendingAccount != null)
  1588                     {
  1589                         status = PEPIdentity.GetOwnIdentity(sendingAccount.SmtpAddress, out from);
  1590                     }
  1591                 }
  1592                 else
  1593                 {
  1594                     Log.Error("GetFromIdentity: Sender is null and mail is not outgoing. Can not determine From identity.");
  1595                 }
  1596             }
  1597             catch (Exception ex)
  1598             {
  1599                 status = Globals.ReturnStatus.Failure;
  1600                 Log.Error("GetFromIdentity: Failure occured, " + ex.ToString());
  1601             }
  1602             finally
  1603             {
  1604                 // Release objects
  1605                 if (contact != null)
  1606                 {
  1607                     // Marshal.ReleaseComObject(contact);
  1608                     contact = null;
  1609                 }
  1610 
  1611                 if (sender != null)
  1612                 {
  1613                     // Marshal.ReleaseComObject(sender);
  1614                     sender = null;
  1615                 }
  1616 
  1617                 if (exchSender != null)
  1618                 {
  1619                     // Marshal.ReleaseComObject(exchSender);
  1620                     exchSender = null;
  1621                 }
  1622 
  1623                 if (sendingAccount != null)
  1624                 {
  1625                     // Marshal.ReleaseComObject(sendingAccount);
  1626                     sendingAccount = null;
  1627                 }
  1628             }
  1629 
  1630             fromIdentity = from;
  1631             return (status);
  1632         }
  1633 
  1634         /// <summary>
  1635         /// Gets the default from/sender identity using the default Outlook user.
  1636         /// Warning: The result can be null.
  1637         /// </summary>
  1638         /// <param name="fromIdentity">The output default from/sender identity (may be null).</param>
  1639         /// <returns>The status of the method.</returns>
  1640         public static Globals.ReturnStatus GetDefaultFromIdentity(out PEPIdentity fromIdentity)
  1641         {
  1642             PEPIdentity from = null;
  1643             Outlook.NameSpace ns = null;
  1644             Outlook.Recipient currUser = null;
  1645             Globals.ReturnStatus status = Globals.ReturnStatus.Failure;
  1646 
  1647             try
  1648             {
  1649                 ns = Globals.ThisAddIn.Application.Session;
  1650                 currUser = ns.CurrentUser;
  1651 
  1652                 if (currUser != null)
  1653                 {
  1654                     status = PEPIdentity.GetOwnIdentity(currUser.Address, out from);
  1655                 }
  1656             }
  1657             catch (Exception ex)
  1658             {
  1659                 status = Globals.ReturnStatus.Failure;
  1660                 Log.Error("GetDefaultFromIdentity: Failure occured, " + ex.ToString());
  1661             }
  1662             finally
  1663             {
  1664                 // Release objects
  1665                 if (ns != null)
  1666                 {
  1667                     // Marshal.ReleaseComObject(ns);
  1668                     ns = null;
  1669                 }
  1670 
  1671                 if (currUser != null)
  1672                 {
  1673                     // Marshal.ReleaseComObject(currUser);
  1674                     currUser = null;
  1675                 }
  1676             }
  1677 
  1678             fromIdentity = from;
  1679             return (status);
  1680         }
  1681 
  1682         /// <summary>
  1683         /// Creates a new pEp identity from the given outlook recipient.
  1684         /// The output will never be null.
  1685         /// Warning: The result can contain identity groups.
  1686         /// </summary>
  1687         /// <param name="recipient">The recipient to determine pEp identity for.</param>
  1688         /// <param name="identity">The output pEp identity of the recipient (will never be null).</param>
  1689         /// <returns>The status of the method.</returns>
  1690         public static Globals.ReturnStatus Create(Outlook.Recipient recipient,
  1691                                                   out PEPIdentity identity)
  1692         {
  1693             string smtpAddress= null;
  1694             Outlook.AddressEntry addressEntry = null;
  1695             PEPIdentity newIdent = null;
  1696             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  1697 
  1698             try
  1699             {
  1700                 // Get the address entry of the recipient
  1701                 addressEntry = recipient.AddressEntry;
  1702                 if (addressEntry == null)
  1703                 {
  1704                     // Attempt to resolve if necessary
  1705                     recipient.Resolve();
  1706                     addressEntry = recipient.AddressEntry;
  1707                 }
  1708             }
  1709             catch { }
  1710 
  1711             // A resolved recipient has an address entry. This should be the common case. If an address entry can be retrieved,
  1712             // run through the full Create() method. If no address entry is available, create a new pEp identity from the recipient.
  1713             if (addressEntry != null)
  1714             {
  1715                 status = Create(addressEntry, out newIdent);
  1716             }
  1717             else
  1718             {
  1719                 Log.Info("PEPIdentity.Create: Unable to get addressEntry from recipient. Trying to create identity directly.");
  1720 
  1721                 try
  1722                 {
  1723                     smtpAddress = (string)MapiHelper.GetProperty(recipient, MapiProperty.PidTagSmtpAddress);
  1724                 }
  1725                 catch { }
  1726 
  1727                 newIdent = new PEPIdentity
  1728                 {
  1729                     Address = smtpAddress?.Trim(),
  1730                     UserName = recipient?.Name?.Trim()
  1731                 };
  1732 
  1733                 // Get the unique user ID if possible (Assume no contact is available)
  1734                 if (string.IsNullOrEmpty(newIdent.Address) == false)
  1735                 {
  1736                     newIdent.UserId = Globals.ThisAddIn.GetUserId(newIdent.Address);
  1737                 }
  1738             }
  1739 
  1740             // Free resources
  1741             if (addressEntry != null)
  1742             {
  1743                 // Marshal.ReleaseComObject(addressEntry);
  1744                 addressEntry = null;
  1745             }
  1746 
  1747             identity = newIdent;
  1748             return (status);
  1749         }
  1750 
  1751         /// <summary>
  1752         /// Creates a new pEp identity from the given outlook address entry.
  1753         /// The output will never be null.
  1754         /// Warning: The result can contain identity groups.
  1755         /// </summary>
  1756         /// <param name="addressEntry">The address entry to determine pEp identity for. This should never be null.</param>
  1757         /// <param name="identity">The output pEp identity of the address entry (will never be null).</param>
  1758         /// <returns>The status of the method.</returns>
  1759         public static Globals.ReturnStatus Create(Outlook.AddressEntry addressEntry,
  1760                                                   out PEPIdentity identity)
  1761         {
  1762             bool identityCreated = false;
  1763             bool? forceUnencryptedProperty = null;
  1764             string tempAddress;
  1765             string tempUserName;
  1766             PEPIdentity newIdent = new PEPIdentity();
  1767             PEPIdentity newIdent2;
  1768             PEPIdentity member;
  1769             Outlook.OlAddressEntryUserType? dlType = null;
  1770             Outlook.AddressEntry currAddressEntry = null;
  1771             Outlook.AddressEntry currentMember = null;
  1772             Outlook.ContactItem contact = null;
  1773             Outlook.ContactItem currContact = null;
  1774             Outlook.AddressEntries members = null;
  1775             Outlook.ExchangeDistributionList exchDL = null;
  1776             Outlook.AddressEntries exchDLMembers = null;
  1777             Outlook.ExchangeUser exchUser = null;
  1778             Globals.ReturnStatus sts;
  1779             Globals.ReturnStatus status = Globals.ReturnStatus.Success;
  1780 
  1781             ///////////////////////////////////////////////////////////
  1782             // Pre-process
  1783             ///////////////////////////////////////////////////////////
  1784 
  1785             // If address entry is null, no identity can be created and the process can be aborted.
  1786             if (addressEntry == null)
  1787             {
  1788                 Log.Error("PEPIdentity.Create: addressEntry is null.");
  1789                 identity = new PEPIdentity();
  1790                 return Globals.ReturnStatus.Failure;
  1791             }
  1792 
  1793             try
  1794             {
  1795                 if ((addressEntry.AddressEntryUserType == Outlook.OlAddressEntryUserType.olOutlookDistributionListAddressEntry) ||
  1796                      (addressEntry.AddressEntryUserType == Outlook.OlAddressEntryUserType.olExchangeDistributionListAddressEntry))
  1797                 {
  1798                     dlType = addressEntry.AddressEntryUserType;
  1799                 }
  1800             }
  1801             catch
  1802             {
  1803                 dlType = null;
  1804             }
  1805 
  1806             // Add the contact force unencrypted property (and get contact)
  1807             try
  1808             {
  1809                 // Save contact for later user ID calculation
  1810                 contact = addressEntry.GetContact();
  1811                 if (contact != null)
  1812                 {
  1813                     forceUnencryptedProperty = contact.GetForceUnencrypted();
  1814                 }
  1815             }
  1816             catch
  1817             {
  1818                 forceUnencryptedProperty = null;
  1819             }
  1820 
  1821             ///////////////////////////////////////////////////////////
  1822             // Handle Outlook distribution lists
  1823             ///////////////////////////////////////////////////////////
  1824 
  1825             // Handle when the recipient is a local Outlook distribution list/contact group
  1826             if ((identityCreated == false) &&
  1827                 (dlType != null) &&
  1828                 (((Outlook.OlAddressEntryUserType)dlType) == Outlook.OlAddressEntryUserType.olOutlookDistributionListAddressEntry))
  1829             {
  1830                 try
  1831                 {
  1832                     members = addressEntry.Members;
  1833 
  1834                     try
  1835                     {
  1836                         exchUser = addressEntry.GetExchangeUser();
  1837                     }
  1838                     catch
  1839                     {
  1840                         exchUser = null;
  1841                     }
  1842 
  1843                     if ((exchUser != null) &&
  1844                         (exchUser.Members != null))
  1845                     {
  1846                         members = exchUser.Members;
  1847                         // Marshal.ReleaseComObject(exchUser);
  1848                         exchUser = null;
  1849                     }
  1850 
  1851                     if (members != null)
  1852                     {
  1853                         for (int i = 1; i <= members.Count; i++)
  1854                         {
  1855                             currentMember = members[i];
  1856 
  1857                             if (currentMember != null)
  1858                             {
  1859                                 sts = PEPIdentity.Create(currentMember, out member);
  1860                                 newIdent.Members.Add(member);
  1861 
  1862                                 // Marshal.ReleaseComObject(currentMember);
  1863                                 currentMember = null;
  1864                             }
  1865                             else
  1866                             {
  1867                                 Log.Error("PEPIdentity.Create: Error getting a local distribution list member.");
  1868                             }
  1869                         }
  1870 
  1871                         // Marshal.ReleaseComObject(members);
  1872                         members = null;
  1873 
  1874                         identityCreated = true;
  1875                     }
  1876                     else
  1877                     {
  1878                         Log.Error("PEPIdentity.Create: Error getting local distribution list members.");
  1879                     }
  1880                 }
  1881                 catch
  1882                 {
  1883                     identityCreated = false;
  1884                 }
  1885                 finally
  1886                 {
  1887                     // Free resources
  1888                     if (currentMember != null)
  1889                     {
  1890                         // Marshal.ReleaseComObject(currentMember);
  1891                         currentMember = null;
  1892                     }
  1893 
  1894                     if (members != null)
  1895                     {
  1896                         // Marshal.ReleaseComObject(members);
  1897                         members = null;
  1898                     }
  1899 
  1900                     if (exchUser != null)
  1901                     {
  1902                         // Marshal.ReleaseComObject(exchUser);
  1903                         exchUser = null;
  1904                     }
  1905                 }
  1906             }
  1907 
  1908             ///////////////////////////////////////////////////////////
  1909             // Handle exchange distribution lists
  1910             ///////////////////////////////////////////////////////////
  1911 
  1912             // Handle when the recipient is an exchange distribution list
  1913             // See: https://msdn.microsoft.com/en-us/library/office/bb645998.aspx
  1914             if ((identityCreated == false) &&
  1915                 (dlType != null) &&
  1916                 (((Outlook.OlAddressEntryUserType)dlType) == Outlook.OlAddressEntryUserType.olExchangeDistributionListAddressEntry))
  1917             {
  1918                 try
  1919                 {
  1920                     exchDL = addressEntry.GetExchangeDistributionList();
  1921                     exchDLMembers = exchDL.GetExchangeDistributionListMembers();
  1922 
  1923                     // Add group
  1924                     newIdent = new PEPIdentity();
  1925                     newIdent.Address = null;
  1926                     newIdent.UserName = exchDL.Name;
  1927                     newIdent.UserId = null;
  1928 
  1929                     // Add members
  1930                     if (exchDLMembers != null)
  1931                     {
  1932                         for (int i = 1; i <= exchDLMembers.Count; i++)
  1933                         {
  1934                             try
  1935                             {
  1936                                 currAddressEntry = exchDLMembers[i];
  1937                                 exchUser = currAddressEntry.GetExchangeUser();
  1938 
  1939                                 if (exchUser != null)
  1940                                 {
  1941                                     newIdent2 = new PEPIdentity();
  1942                                     newIdent2.Address = exchUser.PrimarySmtpAddress;
  1943                                     newIdent2.UserName = exchUser.Name;
  1944 
  1945                                     // Add the contact force unencrypted property
  1946                                     currContact = null;
  1947                                     try
  1948                                     {
  1949                                         currContact = exchUser.GetContact();
  1950 
  1951                                         if (currContact != null)
  1952                                         {
  1953                                             newIdent2.IsForceUnencrypted = currContact.GetForceUnencrypted();
  1954                                         }
  1955                                     }
  1956                                     catch
  1957                                     {
  1958                                         currContact = null;
  1959                                     }
  1960 
  1961                                     newIdent2.UserId = Globals.ThisAddIn.GetUserId(newIdent2.Address, currContact, currAddressEntry);
  1962                                     newIdent.Members.Add(newIdent2);
  1963                                 }
  1964                             }
  1965                             catch { }
  1966                             finally
  1967                             {
  1968                                 if (currAddressEntry != null)
  1969                                 {
  1970                                     // Marshal.ReleaseComObject(currAddressEntry);
  1971                                     currAddressEntry = null;
  1972                                 }
  1973 
  1974                                 if (exchUser != null)
  1975                                 {
  1976                                     // Marshal.ReleaseComObject(exchUser);
  1977                                     exchUser = null;
  1978                                 }
  1979 
  1980                                 if (currContact != null)
  1981                                 {
  1982                                     // Marshal.ReleaseComObject(currContact);
  1983                                     currContact = null;
  1984                                 }
  1985                             }
  1986                         }
  1987                     }
  1988 
  1989                     identityCreated = true;
  1990                 }
  1991                 catch { }
  1992                 finally
  1993                 {
  1994                     if (exchDL != null)
  1995                     {
  1996                         // Marshal.ReleaseComObject(exchDL);
  1997                         exchDL = null;
  1998                     }
  1999 
  2000                     if (exchDLMembers != null)
  2001                     {
  2002                         // Marshal.ReleaseComObject(exchDLMembers);
  2003                         exchDLMembers = null;
  2004                     }
  2005 
  2006                     if (currAddressEntry != null)
  2007                     {
  2008                         // Marshal.ReleaseComObject(currAddressEntry);
  2009                         currAddressEntry = null;
  2010                     }
  2011 
  2012                     if (exchUser != null)
  2013                     {
  2014                         // Marshal.ReleaseComObject(exchUser);
  2015                         exchUser = null;
  2016                     }
  2017 
  2018                     if (currContact != null)
  2019                     {
  2020                         // Marshal.ReleaseComObject(currContact);
  2021                         currContact = null;
  2022                     }
  2023                 }
  2024             }
  2025 
  2026             ///////////////////////////////////////////////////////////
  2027             // Handle standard recipients (not lists)
  2028             ///////////////////////////////////////////////////////////
  2029 
  2030             if (identityCreated == false)
  2031             {
  2032                 exchUser = null;
  2033                 tempAddress = null;
  2034                 tempUserName = null;
  2035                 newIdent = new PEPIdentity();
  2036                 newIdent.IsForceUnencrypted = forceUnencryptedProperty;
  2037 
  2038                 // Handle any exchange users first
  2039                 try
  2040                 {
  2041                     // Note: Getting the address entry type can throw an exception if the 
  2042                     // address entry is invalid (example invalid formatted address in draft email)
  2043                     if (addressEntry.Type == "EX")
  2044                     {
  2045                         exchUser = addressEntry.GetExchangeUser();
  2046                         tempAddress = exchUser.PrimarySmtpAddress;
  2047                         tempUserName = exchUser.Name;
  2048                     }
  2049                 }
  2050                 catch { }
  2051                 finally
  2052                 {
  2053                     if (exchUser != null)
  2054                     {
  2055                         // Marshal.ReleaseComObject(exchUser);
  2056                         exchUser = null;
  2057                     }
  2058                 }
  2059 
  2060                 // Get mail address from address entry
  2061                 if (string.IsNullOrWhiteSpace(tempAddress))
  2062                 {
  2063                     try
  2064                     {
  2065                         tempAddress = addressEntry.Address;
  2066                     }
  2067                     catch (Exception ex)
  2068                     {
  2069                         Log.Warning("PEPIdentity.Create: Could not get address from addressEntry. " + ex.ToString());
  2070                     }
  2071                 }
  2072 
  2073                 // Get user name from the address entry
  2074                 if (string.IsNullOrWhiteSpace(tempUserName))
  2075                 {
  2076                     tempUserName = addressEntry.Name;
  2077                 }
  2078 
  2079                 // As a fallback, get information from the associated contact
  2080                 if (string.IsNullOrWhiteSpace(tempAddress))
  2081                 {
  2082                     try
  2083                     {
  2084                         tempAddress = contact.Email1Address;
  2085                         tempUserName = contact.Email1DisplayName;
  2086                     }
  2087                     catch { }
  2088                 }
  2089 
  2090                 // Add address if possible
  2091                 if (string.IsNullOrWhiteSpace(tempAddress) == false)
  2092                 {
  2093                     newIdent.Address = tempAddress.Trim();
  2094                 }
  2095 
  2096                 // Add user name if possible
  2097                 if (string.IsNullOrWhiteSpace(tempUserName) == false)
  2098                 {
  2099                     newIdent.UserName = tempUserName.Trim();
  2100                 }
  2101 
  2102                 // Get the unique user ID if possible
  2103                 if (string.IsNullOrEmpty(newIdent.Address) == false)
  2104                 {
  2105                     newIdent.UserId = Globals.ThisAddIn.GetUserId(newIdent.Address, contact, addressEntry);
  2106                 }
  2107 
  2108                 identityCreated = true;
  2109             }
  2110 
  2111             // Free resources
  2112             if (addressEntry != null)
  2113             {
  2114                 // Marshal.ReleaseComObject(addressEntry);
  2115                 addressEntry = null;
  2116             }
  2117 
  2118             if (contact != null)
  2119             {
  2120                 // Marshal.ReleaseComObject(contact);
  2121                 contact = null;
  2122             }
  2123 
  2124             identity = newIdent;
  2125             return (status);
  2126         }
  2127 
  2128         /// <summary>
  2129         /// Recursivley converts the given identities into a 'flat' list of any members.
  2130         /// This will remove groups (hierarchy) and convert a group into its members.
  2131         /// The list will be build by calling each identity's .ToFlatList method.
  2132         /// </summary>
  2133         /// <param name="identities">The identities to get the flat list for.</param>
  2134         /// <returns>The flat list of pEp identities.</returns>
  2135         public static List<PEPIdentity> ToFlatList(List<PEPIdentity> identities)
  2136         {
  2137             List<PEPIdentity> result = new List<PEPIdentity>();
  2138             List<PEPIdentity> singleList;
  2139 
  2140             for (int i = 0; i < identities.Count; i++)
  2141             {
  2142                 singleList = identities[i].ToFlatList();
  2143 
  2144                 for (int j = 0; j < singleList.Count; j++)
  2145                 {
  2146                     result.Add(singleList[j]);
  2147                 }
  2148             }
  2149 
  2150             return (result);
  2151         }
  2152     }
  2153 }