private void buttonOK_Click(object sender, EventArgs e) { options.HIBPFileName = textBoxFileName.Text; options.ColumnName = textBoxColumnName.Text; options.SecureText = textBoxSecureText.Text; options.InsecureText = textBoxInsecureText.Text; options.BreachCountDetails = checkBoxBreachCountDetails.Checked; options.WarningDialog = checkBoxWarningDialog.Checked; options.WarningDialogText = textBoxWarningDialog.Text; var standardFields = PwDefs.GetStandardFields(); foreach (string key in standardFields) { if (key == options.ColumnName) { MessageBox.Show("Column name conflicts with KeePass columns", " Invalid column name", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } ext.SaveOptions(options); this.Close(); }
private bool CommitOptions() { options.CheckMode = radioButtonOffline.Checked ? Options.CheckModeType.Offline : Options.CheckModeType.Online; options.HIBPFileName = textBoxFileName.Text; options.ColumnName = textBoxColumnName.Text; options.SecureText = textBoxSecureText.Text; options.InsecureText = textBoxInsecureText.Text; options.BreachCountDetails = checkBoxBreachCountDetails.Checked; options.WarningDialog = checkBoxWarningDialog.Checked; options.WarningDialogText = textBoxWarningDialog.Text; var standardFields = PwDefs.GetStandardFields(); foreach (string key in standardFields) { if (key == options.ColumnName) { MessageBox.Show("Column name conflicts with KeePass columns", " Invalid column name", MessageBoxButtons.OK, MessageBoxIcon.Error); return(false); } } ext.SaveOptions(options); return(true); }
private void EnsureStandardStrings() { foreach (string strKey in PwDefs.GetStandardFields()) { if (!m_peM.Strings.Exists(strKey)) { m_peM.Strings.Set(strKey, (m_pd.MemoryProtection.GetProtection( strKey) ? ProtectedString.EmptyEx : ProtectedString.Empty)); } // Standard string protections are normalized while // loading/saving the database file this.MultiStringProt[strKey] = true; } }
private static string FillEntryStrings(string str, SprContext ctx, uint uRecursionLevel) { List <string> vKeys = ctx.Entry.Strings.GetKeys(); // Ensure that all standard field names are in the list // (this is required in order to replace the standard placeholders // even if the corresponding standard field isn't present in // the entry) List <string> vStdNames = PwDefs.GetStandardFields(); foreach (string strStdField in vStdNames) { if (!vKeys.Contains(strStdField)) { vKeys.Add(strStdField); } } // Do not directly enumerate the strings in ctx.Entry.Strings, // because strings might change during the Spr compilation foreach (string strField in vKeys) { string strKey = (PwDefs.IsStandardField(strField) ? (@"{" + strField + @"}") : (@"{" + PwDefs.AutoTypeStringPrefix + strField + @"}")); if (!ctx.ForcePlainTextPasswords && strKey.Equals(@"{" + PwDefs.PasswordField + @"}", StrUtil.CaseIgnoreCmp) && Program.Config.MainWindow.IsColumnHidden(AceColumnType.Password)) { str = SprEngine.FillIfExists(str, strKey, new ProtectedString( false, PwDefs.HiddenPassword), ctx, uRecursionLevel); continue; } // Use GetSafe because the field doesn't necessarily exist // (might be a standard field that has been added above) str = SprEngine.FillIfExists(str, strKey, ctx.Entry.Strings.GetSafe( strField), ctx, uRecursionLevel); } return(str); }
private bool CommitOptions() { options.CheckMode = radioButtonOffline.Checked ? Options.CheckModeType.Offline : radioButtonOnline.Checked ? Options.CheckModeType.Online : Options.CheckModeType.BloomFilter; options.HIBPFileName = textBoxFileName.Text; options.ColumnName = textBoxColumnName.Text; options.SecureText = textBoxSecureText.Text; options.InsecureText = textBoxInsecureText.Text; options.ExcludedText = textBoxExcludedText.Text; options.BreachCountDetails = checkBoxBreachCountDetails.Checked; options.ExcludeRecycleBin = checkBoxExcludeRecycleBin.Checked; options.ExcludeExpired = checkBoxExcludeExpired.Checked; options.WarningDialog = checkBoxWarningDialog.Checked; options.AutoCheck = checkBoxAutoCheck.Checked; options.WarningDialogText = textBoxWarningDialog.Text; bool bloomFilterChanged = (options.BloomFilter != textBoxBloomFilter.Text); options.BloomFilter = textBoxBloomFilter.Text; var standardFields = PwDefs.GetStandardFields(); foreach (string key in standardFields) { if (key == options.ColumnName) { MessageBox.Show("Column name conflicts with KeePass columns", " Invalid column name", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } if (bloomFilterChanged) { ext.Prov.BloomFilter = null; } ext.SaveOptions(options); return true; }
private static void EnsureStandardFieldsExist(PwDatabase pd) { List <string> l = PwDefs.GetStandardFields(); EntryHandler eh = delegate(PwEntry pe) { foreach (string strName in l) { ProtectedString ps = pe.Strings.Get(strName); if (ps == null) { pe.Strings.Set(strName, new ProtectedString( pd.MemoryProtection.GetProtection(strName), string.Empty)); } } return(true); }; pd.RootGroup.TraverseTree(TraversalMethod.PreOrder, null, eh); }
public void PrepareForm() { if (KeePassPasswordChangerExt.PrepareDatabase()) { DialogResult allEntriesResult = MessageBox.Show("Notice: Please read the about section of the plugin. This is an alpha release. Backup your Database before you use this plugin.\r\n\r\nDo you want to proceed?", "Notice", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (allEntriesResult == DialogResult.No) { KeePassPasswordChangerExt.ExtentEntryNote("User aborted..."); this.Close(); } else { foreach (var pwEntry in KeePassPasswordChangerExt._mHost.MainWindow.ActiveDatabase.RootGroup.GetEntries(true)) { try { if (KeePassPasswordChangerExt.GroupPlugin.FindEntry(pwEntry.Uuid, true) != null) { continue; } if (pwEntry.Strings.Get(PwDefs.UrlField) == null) { continue; } string uri = pwEntry.Strings.Get(PwDefs.UrlField).ReadString(); int count = 0; Template template = null; TemplateManagement.LoadTemplates(); foreach (var availableTemplate in TemplateManagement.AvailableTemplates) { if (availableTemplate.Value.BoundUrl.IsRegex.Value) { if (Regex.IsMatch(uri, availableTemplate.Value.BoundUrl.Value.Value)) { template = availableTemplate.Value; count++; } } else { if (uri.Contains(availableTemplate.Value.BoundUrl.Value.Value) || availableTemplate.Value.BoundUrl.Value.Value.Contains(uri)) { template = availableTemplate.Value; count++; } } } switch (count) { case 0: KeePassPasswordChangerExt.ExtentEntryNote("Ignoring entry " + pwEntry.Strings.Get(PwDefs.TitleField).ReadString() + "(UID: " + pwEntry.Uuid.ToString() + ") it has no template"); continue; case 1: { PwGroup dustbin = KeePassPasswordChangerExt._mHost.MainWindow.ActiveDatabase.RootGroup.FindGroup( KeePassPasswordChangerExt._mHost.MainWindow.ActiveDatabase.RecycleBinUuid, true); if (dustbin != null) { if (dustbin.FindEntry(pwEntry.Uuid, true) != null) { KeePassPasswordChangerExt.ExtentEntryNote("Ignoring entry " + pwEntry.Strings.Get( PwDefs.TitleField) .ReadString() + "(UID: " + pwEntry.Uuid.ToString() + ") because it is dustbinned"); continue; } if (DateTimeNotLaterThan != DateTime.MinValue && pwEntry.LastModificationTime >= DateTimeNotLaterThan) { KeePassPasswordChangerExt.ExtentEntryNote("Ignoring entry " + pwEntry.Strings.Get( PwDefs.TitleField) .ReadString() + "(UID: " + pwEntry.Uuid.ToString() + ") because was modified @ " + pwEntry .LastModificationTime .ToString() + "(UTC) and Limit is: " + DateTimeNotLaterThan .ToString() + "(UTC)"); continue; } } KeePassPasswordChangerExt.ExtentEntryNote("Entry " + pwEntry.Strings.Get(PwDefs.TitleField).ReadString() + "(UID: " + pwEntry.Uuid.ToString() + ") matched on the template " + template.Name); Template templateSibling = (Template)template.Clone(); List <KeyValuePairEx <string, ProtectedString> > parameters = new List <KeyValuePairEx <string, ProtectedString> >(); List <string> requiredParameters = template.GetRequiredKeepassVariables(); foreach (var requiredKeepassVariable in requiredParameters) { try { if ( BaseObject.ExtractSinglePlaceholderToString(requiredKeepassVariable) == "") { ProtectedString newPassword = KeePassPasswordChangerExt .GeneratePassword( template.PasswordCreationPolicy); parameters.Add( new KeyValuePairEx <string, ProtectedString>( BaseObject.ConvertStringToPlaceholderString(""), newPassword)); continue; } bool foundPwDef = false; foreach (var pwdef in PwDefs.GetStandardFields()) { if (pwdef == BaseObject.ExtractSinglePlaceholderToString( requiredKeepassVariable)) { parameters.Add( new KeyValuePairEx <string, ProtectedString>( requiredKeepassVariable, pwEntry.Strings.Get( BaseObject.ExtractSinglePlaceholderToString( requiredKeepassVariable)))); foundPwDef = true; break; } } if (!foundPwDef) { parameters.Add( new KeyValuePairEx <string, ProtectedString>( requiredKeepassVariable, new ProtectedString(true, requiredKeepassVariable))); } } catch (Exception ex) { ExceptionHandling.Handling.GetException("Unexpected", ex); } } if (requiredParameters.Count != parameters.Count) { KeePassPasswordChangerExt.ExtentEntryNote( "Sorry, i could not gather all required parameters from the password entry " + pwEntry.Strings.Get(PwDefs.TitleField).ReadString()); } else { while (true) { try { templateSibling.GenerateNewUtid(KeePassPasswordChangerExt.Counter++.ToString()); TemplateManagement.LockTemplates.AcquireWriterLock(Options.LockTimeOut); templateSibling.InitializeTemplate(parameters, KeePassPasswordChangerExt.CefControl, pwEntry.Strings.Get(PwDefs.TitleField).ReadString(), pwEntry.Uuid); TemplateManagement.TemplatesReady.Add(templateSibling.UTID, templateSibling); break; } catch (ApplicationException ex) { ExceptionHandling.Handling.GetException("ReaderWriterLock", ex); } finally { if (TemplateManagement.LockTemplates.IsWriterLockHeld) { TemplateManagement.LockTemplates.ReleaseWriterLock(); } } } } } break; default: //Write detailed list! KeePassPasswordChangerExt.ExtentEntryNote("Entry \"" + pwEntry.Strings.Get(PwDefs.TitleField).ReadString() + "\" matches on too many templates, ignoring this one, sorry"); continue; } } catch (Exception ex) { ExceptionHandling.Handling.GetException("Unexpected", ex); } } } } else { return; } KeePassPasswordChangerExt.UnlockDatabase(); CheckValues(); }
private static bool HighlightRegularPlh(SprPart pPart, List <SprStyle> lStyles, Stack <SprPart> sToDo, SprContext ctx) { string str = pPart.Text; int iStart = str.IndexOf('{'); if (iStart < 0) { return(false); } if ((iStart + 2) >= str.Length) { SetStyle(lStyles, pPart, iStart, str.Length - iStart, SprStyleError); } else { int iOpen = str.IndexOf('{', iStart + 2); int iClose = str.IndexOf('}', iStart + 2); bool bAT = ((ctx != null) && ctx.EncodeAsAutoTypeSequence); if (iClose < 0) { SetStyle(lStyles, pPart, iStart, str.Length - iStart, SprStyleError); } else if ((iOpen >= 0) && (iOpen < iClose)) { sToDo.Push(pPart.GetPart(iOpen)); SetStyle(lStyles, pPart, iStart, iOpen - iStart, SprStyleError); } else if (bAT && ((str[iStart + 1] == '{') || (str[iStart + 1] == '}')) && (iClose != (iStart + 2))) { sToDo.Push(pPart.GetPart(iClose + 1)); SetStyle(lStyles, pPart, iStart, iClose - iStart + 1, SprStyleError); } else { sToDo.Push(pPart.GetPart(iClose + 1)); int iErrLvl = 0; string strPlh = str.Substring(iStart + 1, iClose - iStart - 1); if (strPlh.Length == 0) { Debug.Assert(false); iErrLvl = 2; } else if (char.IsWhiteSpace(strPlh[0])) { iErrLvl = 2; } else if (strPlh.StartsWith("S:", StrUtil.CaseIgnoreCmp) && (ctx != null) && (ctx.Entry != null)) { string strField = strPlh.Substring(2); List <string> lFields = PwDefs.GetStandardFields(); lFields.AddRange(ctx.Entry.Strings.GetKeys()); bool bFound = false; foreach (string strAvail in lFields) { if (strField.Equals(strAvail, StrUtil.CaseIgnoreCmp)) { bFound = true; break; } } if (!bFound) { iErrLvl = 1; } } SprStyle s = SprStyleOK; if (iErrLvl == 1) { s = SprStyleWarning; } else if (iErrLvl > 1) { s = SprStyleError; } SetStyle(lStyles, pPart, iStart, iClose - iStart + 1, s); } } if (iStart > 0) { sToDo.Push(pPart.GetPart(0, iStart)); } return(true); }
private static string CompileInternal(string strText, PwEntry pwEntry, PwDatabase pwDatabase, SprContentFlags cf, uint uRecursionLevel, SprRefsCache vRefsCache) { if (strText == null) { Debug.Assert(false); return(string.Empty); } if (uRecursionLevel >= SprEngine.MaxRecursionDepth) { Debug.Assert(false); // Most likely a recursive reference return(string.Empty); // Do not return strText } string str = strText; str = AppLocator.FillPlaceholders(str, cf); str = EntryUtil.FillPlaceholders(str, pwEntry, pwDatabase, cf); if (pwEntry != null) { List <string> vKeys = pwEntry.Strings.GetKeys(); // Ensure that all standard field names are in the list // (this is required in order to replace the standard placeholders // even if the corresponding standard field isn't present in // the entry) List <string> vStdNames = PwDefs.GetStandardFields(); foreach (string strStdField in vStdNames) { if (!vKeys.Contains(strStdField)) { vKeys.Add(strStdField); } } // Do not directly enumerate the strings in pwEntry.Strings, // because strings might change during the Spr compilation foreach (string strField in vKeys) { string strKey = (PwDefs.IsStandardField(strField) ? (@"{" + strField + @"}") : (@"{" + PwDefs.AutoTypeStringPrefix + strField + @"}")); // Use GetSafe because the field doesn't necessarily exist // (might be a standard field that has been added above) str = SprEngine.FillIfExists(str, strKey, pwEntry.Strings.GetSafe( strField), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); } if (cf != null) { cf.UrlRemoveSchemeOnce = true; } str = SprEngine.FillIfExists(str, @"{URL:RMVSCM}", pwEntry.Strings.GetSafe(PwDefs.UrlField), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); if (cf != null) { Debug.Assert(!cf.UrlRemoveSchemeOnce); } if (str.IndexOf(@"{PASSWORD_ENC}", SprEngine.ScMethod) >= 0) { str = SprEngine.FillIfExists(str, @"{PASSWORD_ENC}", new ProtectedString(false, StrUtil.EncryptString(pwEntry.Strings.ReadSafe(PwDefs.PasswordField))), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); } if (pwEntry.ParentGroup != null) { str = SprEngine.FillIfExists(str, @"{GROUP}", new ProtectedString( false, pwEntry.ParentGroup.Name), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{GROUPPATH}", new ProtectedString( false, pwEntry.ParentGroup.GetFullPath()), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); } } if (m_strAppExePath != null) { str = SprEngine.FillIfExists(str, @"{APPDIR}", new ProtectedString( false, UrlUtil.GetFileDirectory(m_strAppExePath, false, false)), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); } if (pwDatabase != null) { // For backward compatibility only str = SprEngine.FillIfExists(str, @"{DOCDIR}", new ProtectedString( false, UrlUtil.GetFileDirectory(pwDatabase.IOConnectionInfo.Path, false, false)), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DB_PATH}", new ProtectedString( false, pwDatabase.IOConnectionInfo.Path), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DB_DIR}", new ProtectedString( false, UrlUtil.GetFileDirectory(pwDatabase.IOConnectionInfo.Path, false, false)), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DB_NAME}", new ProtectedString( false, UrlUtil.GetFileName(pwDatabase.IOConnectionInfo.Path)), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DB_BASENAME}", new ProtectedString( false, UrlUtil.StripExtension(UrlUtil.GetFileName( pwDatabase.IOConnectionInfo.Path))), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DB_EXT}", new ProtectedString( false, UrlUtil.GetExtension(pwDatabase.IOConnectionInfo.Path)), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); } str = SprEngine.FillIfExists(str, @"{ENV_DIRSEP}", new ProtectedString( false, Path.DirectorySeparatorChar.ToString()), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); DateTime dtNow = DateTime.Now; // Local time str = SprEngine.FillIfExists(str, @"{DT_YEAR}", new ProtectedString( false, dtNow.Year.ToString("D4")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_MONTH}", new ProtectedString( false, dtNow.Month.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_DAY}", new ProtectedString( false, dtNow.Day.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_HOUR}", new ProtectedString( false, dtNow.Hour.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_MINUTE}", new ProtectedString( false, dtNow.Minute.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_SECOND}", new ProtectedString( false, dtNow.Second.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_SIMPLE}", new ProtectedString( false, dtNow.ToString("yyyyMMddHHmmss")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); dtNow = dtNow.ToUniversalTime(); str = SprEngine.FillIfExists(str, @"{DT_UTC_YEAR}", new ProtectedString( false, dtNow.Year.ToString("D4")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_UTC_MONTH}", new ProtectedString( false, dtNow.Month.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_UTC_DAY}", new ProtectedString( false, dtNow.Day.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_UTC_HOUR}", new ProtectedString( false, dtNow.Hour.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_UTC_MINUTE}", new ProtectedString( false, dtNow.Minute.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_UTC_SECOND}", new ProtectedString( false, dtNow.Second.ToString("D2")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillIfExists(str, @"{DT_UTC_SIMPLE}", new ProtectedString( false, dtNow.ToString("yyyyMMddHHmmss")), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); str = SprEngine.FillRefPlaceholders(str, pwDatabase, cf, uRecursionLevel, vRefsCache); // Replace environment variables foreach (DictionaryEntry de in Environment.GetEnvironmentVariables()) { string strKey = (de.Key as string); string strValue = (de.Value as string); if ((strKey != null) && (strValue != null)) { str = SprEngine.FillIfExists(str, @"%" + strKey + @"%", new ProtectedString(false, strValue), pwEntry, pwDatabase, cf, uRecursionLevel, vRefsCache); } else { Debug.Assert(false); } } str = EntryUtil.FillPlaceholdersFinal(str, pwEntry, pwDatabase, cf); return(str); }
private static string ReplacePickField(string strText, SprContext ctx) { string str = strText; PwEntry pe = ((ctx != null) ? ctx.Entry : null); PwDatabase pd = ((ctx != null) ? ctx.Database : null); while (true) { const string strPlh = @"{PICKFIELD}"; int p = str.IndexOf(strPlh, StrUtil.CaseIgnoreCmp); if (p < 0) { break; } string strRep = string.Empty; List <FpField> l = new List <FpField>(); string strGroup; if (pe != null) { strGroup = KPRes.Entry + " - " + KPRes.StandardFields; Debug.Assert(PwDefs.GetStandardFields().Count == 5); l.Add(new FpField(KPRes.Title, pe.Strings.GetSafe(PwDefs.TitleField), strGroup)); l.Add(new FpField(KPRes.UserName, pe.Strings.GetSafe(PwDefs.UserNameField), strGroup)); l.Add(new FpField(KPRes.Password, pe.Strings.GetSafe(PwDefs.PasswordField), strGroup)); l.Add(new FpField(KPRes.Url, pe.Strings.GetSafe(PwDefs.UrlField), strGroup)); l.Add(new FpField(KPRes.Notes, pe.Strings.GetSafe(PwDefs.NotesField), strGroup)); strGroup = KPRes.Entry + " - " + KPRes.CustomFields; foreach (KeyValuePair <string, ProtectedString> kvp in pe.Strings) { if (PwDefs.IsStandardField(kvp.Key)) { continue; } l.Add(new FpField(kvp.Key, kvp.Value, strGroup)); } PwGroup pg = pe.ParentGroup; if (pg != null) { strGroup = KPRes.Group; l.Add(new FpField(KPRes.Name, new ProtectedString( false, pg.Name), strGroup)); if (pg.Notes.Length > 0) { l.Add(new FpField(KPRes.Notes, new ProtectedString( false, pg.Notes), strGroup)); } } } if (pd != null) { strGroup = KPRes.Database; if (pd.Name.Length > 0) { l.Add(new FpField(KPRes.Name, new ProtectedString( false, pd.Name), strGroup)); } l.Add(new FpField(KPRes.FileOrUrl, new ProtectedString( false, pd.IOConnectionInfo.Path), strGroup)); } FpField fpf = FieldPickerForm.ShowAndRestore(KPRes.PickField, KPRes.PickFieldDesc, l); if (fpf != null) { strRep = fpf.Value.ReadString(); } strRep = SprEngine.Compile(strRep, ctx.WithoutContentTransformations()); strRep = SprEngine.TransformContent(strRep, ctx); str = str.Remove(p, strPlh.Length); str = str.Insert(p, strRep); } return(str); }