Overview

Namespaces

  • MatthiasMullie
    • Minify
      • Exceptions
    • PathConverter
  • None
  • ReCaptcha
    • RequestMethod
  • TOTP

Classes

  • apc_cache
  • apcu_cache
  • ApprovePost_Notify_Background
  • ApproveReply_Notify_Background
  • Attachments
  • Birthday_Notify_Background
  • browser_detector
  • Buddy_Notify_Background
  • cache_api
  • CreateAttachment_Notify_Background
  • CreatePost_Notify_Background
  • curl_fetch_web_data
  • custom_search
  • EventNew_Notify_Background
  • ftp_connection
  • fulltext_search
  • gif_color_table
  • gif_file
  • gif_file_header
  • gif_image
  • gif_image_header
  • gif_lzw_compression
  • GroupAct_Notify_Background
  • GroupReq_Notify_Background
  • Likes
  • Likes_Notify_Background
  • MatthiasMullie\Minify\CSS
  • MatthiasMullie\Minify\JS
  • MatthiasMullie\Minify\Minify
  • MatthiasMullie\PathConverter\Converter
  • MemberReport_Notify_Background
  • MemberReportReply_Notify_Background
  • memcache_cache
  • memcached_cache
  • Mentions
  • MsgReport_Notify_Background
  • MsgReportReply_Notify_Background
  • paypal_display
  • paypal_payment
  • postgres_cache
  • ProxyServer
  • Punycode
  • ReCaptcha\ReCaptcha
  • ReCaptcha\RequestMethod\Curl
  • ReCaptcha\RequestMethod\CurlPost
  • ReCaptcha\RequestMethod\Post
  • ReCaptcha\RequestMethod\Socket
  • ReCaptcha\RequestMethod\SocketPost
  • ReCaptcha\RequestParameters
  • ReCaptcha\Response
  • Register_Notify_Background
  • search_api
  • SMF_BackgroundTask
  • smf_cache
  • sqlite_cache
  • standard_search
  • TOTP\Auth
  • TypeError
  • Update_TLD_Regex
  • xcache_cache
  • xmlArray
  • zend_cache

Interfaces

  • cache_api_interface
  • ReCaptcha\RequestMethod
  • search_api_interface

Exceptions

  • Error
  • MatthiasMullie\Minify\Exception
  • MatthiasMullie\Minify\Exceptions\BasicException
  • MatthiasMullie\Minify\Exceptions\FileImportException
  • MatthiasMullie\Minify\Exceptions\IOException

Functions

  • _safe_serialize
  • _safe_unserialize
  • account
  • Activate
  • activateAccount
  • add_integration_function
  • addData
  • addInlineCss
  • addInlineJavaScript
  • addJavaScriptVar
  • AddLanguage
  • AddMailQueue
  • AddMembergroup
  • addMembersToGroup
  • addSeparator
  • AddSmiley
  • addSubscription
  • addTriggers
  • AdminAccount
  • AdminApprove
  • AdminBoardRecount
  • AdminEndSession
  • AdminHome
  • adminLogin
  • adminLogin_outputPostVars
  • AdminLogs
  • AdminMain
  • adminNotify
  • AdminRegister
  • AdminSearch
  • AdminSearchInternal
  • AdminSearchMember
  • AdminSearchOM
  • alert_configuration
  • alert_count
  • alert_delete
  • alert_mark
  • alert_markread
  • alert_notifications_boards
  • alert_notifications_topics
  • alert_purge
  • alerts_popup
  • AlignURLsWithSSLSetting
  • allowedTo
  • AnnouncementSelectMembergroup
  • AnnouncementSend
  • AnnounceTopic
  • ApplyRules
  • approveAllData
  • ApproveAttach
  • ApproveAttachments
  • ApproveMessage
  • approveMessages
  • approvePosts
  • approveTopics
  • array_column
  • array_length
  • assignAttachments
  • attachDirStatus
  • attachmentChecks
  • attachments_init_dir
  • automanage_attachments_by_space
  • automanage_attachments_check_directory
  • automanage_attachments_create_directory
  • AutoSuggest_Search_Member
  • AutoSuggest_Search_MemberGroups
  • AutoSuggest_Search_SMFVersions
  • AutoSuggestHandler
  • AutoTask
  • BackupDatabase
  • backupTable
  • Ban
  • BanBrowseTriggers
  • BanEdit
  • banEdit2
  • BanEditTrigger
  • BanList
  • banLoadAdditionalIPs
  • banLoadAdditionalIPsError
  • banLoadAdditionalIPsMember
  • BanLog
  • banPermissions
  • bbc_to_html
  • BoardIndex
  • BoardNotify
  • BoardPermissionsReport
  • BoardReport
  • boardsAllowedTo
  • BoardurlMatch
  • BookOfUnknown
  • BrowseFiles
  • BrowseMailQueue
  • BuddyListToggle
  • build_query_board
  • build_regex
  • buildEventDatetimes
  • cache_get_data
  • cache_getLastPosts
  • cache_getMembergroupList
  • cache_getOffsetIndependentEvents
  • cache_getRecentEvents
  • cache_put_data
  • cache_quick_get
  • CalculateNextTrigger
  • CalendarMain
  • CalendarPost
  • call_helper
  • call_integration_hook
  • canLinkEvent
  • canPickTheme
  • cdata_parse
  • censorText
  • check_cron
  • checkActivation
  • checkChange
  • checkConfirm
  • checkExistingTriggerIP
  • CheckFilesWritable
  • checkGD
  • checkImageContents
  • checkImagick
  • checkLogin
  • checkMagickWand
  • checkSession
  • checkSubmitOnce
  • clean_cache
  • cleanLangString
  • cleanRequest
  • cleanRequest_cron
  • cleanTokens
  • cleanXml
  • ClearMailQueue
  • cli_scheduled_fetchSMfiles
  • clock
  • cmdStep0
  • comma_format
  • compareVersions
  • ComposeMailing
  • consolidateSpiderStats
  • construct_query_string
  • constructPageIndex
  • ConvertEntities
  • ConvertMsgBody
  • convertSettingstoOptions
  • convertSettingsToTheme
  • ConvertUtf8
  • CoppaForm
  • CopyTemplate
  • copytree
  • countReports
  • create_button
  • create_chmod_control
  • create_control_richedit
  • create_control_verification
  • createAttachment
  • createBoard
  • createCategory
  • createList
  • createMenu
  • CreateMessageIndex
  • createPost
  • createSalvageArea
  • createThumbnail
  • createToken
  • createWaveFile
  • Credits
  • custFieldsMaxOrder
  • custMinify
  • DatabaseChanges
  • DatabasePopulation
  • DatabaseSettings
  • db_extend
  • db_extra_init
  • db_fix_prefix
  • db_packages_init
  • db_search_init
  • db_version_check
  • debugPrint
  • deleteAccount
  • deleteAccount2
  • deleteAllMinified
  • deleteBoards
  • deleteCategories
  • DeleteDraft
  • deleteErrors
  • DeleteInstall
  • DeleteMembergroup
  • deleteMembergroups
  • deleteMembers
  • DeleteMessage
  • deleteMessages
  • deleteModComment
  • deleteNotifyPrefs
  • DeleteUpgrade
  • deltree
  • Destroy
  • destroyMenu
  • detectBrowser
  • detectFulltextIndex
  • determineActions
  • Display
  • display_db_error
  • display_loadavg_error
  • display_maintenance_message
  • DisplayAdminFile
  • displayDebug
  • DisplayStats
  • DoLogin
  • Download
  • downloadAvatar
  • DownloadLanguage
  • dumpTags
  • EditAgreement
  • EditBoard
  • EditBoard2
  • EditBoardSettings
  • editBuddies
  • editBuddyIgnoreLists
  • EditCategory
  • EditCategory2
  • EditComment
  • EditCustomProfiles
  • EditHoliday
  • editIgnoreList
  • EditMembergroup
  • EditMessageIcons
  • editModComment
  • EditNews
  • EditPermissionProfiles
  • EditPoll
  • EditPoll2
  • EditSearchMethod
  • EditSearchSettings
  • EditSmileyOrder
  • EditSmileys
  • EditSmileySets
  • EditSmileySettings
  • EditSpider
  • EditTask
  • EditTheme
  • EditWeights
  • emailAdmins
  • EnableTheme
  • entity_fix__callback
  • escapestring__recursive
  • ExamineFile
  • expandIPv6
  • fatal_error
  • fatal_lang_error
  • fetch_alerts
  • fetch_task
  • fetch_web_data
  • fetchPerms__recursive
  • fetchTagAttributes
  • find_gpg
  • find_signed_off
  • find_signed_off_parents
  • findForumErrors
  • findMembers
  • findSearchAPI
  • finishTables
  • fix_possible_url
  • fix_serialized_data
  • fixchar__callback
  • fixchardb__callback
  • fixChildren
  • fixModSecurity
  • fixRelativePath
  • fixTag
  • fixTags
  • forum_time
  • forumProfile
  • ForumSettings
  • frameOptionsHeader
  • GeneralPermissionSettings
  • generateSubscriptionError
  • generateValidationCode
  • get_all_themes
  • get_current_settings
  • get_date_or_time_format
  • get_directory_tree_elements
  • get_file_listing
  • get_files_recursive
  • get_gravatar_url
  • get_hook_info_from_raw
  • get_installed_themes
  • get_integration_hooks
  • get_integration_hooks_count
  • get_integration_hooks_data
  • get_proxied_url
  • get_single_theme
  • get_theme_info
  • getAttachmentFilename
  • getAttachMsgInfo
  • getAvatars
  • getBirthdayRange
  • getBoardIndex
  • getBoardList
  • getBoardModeratorGroups
  • getBoardModerators
  • getBoardParents
  • getBoardTree
  • getCalendarGrid
  • getCalendarList
  • getCalendarWeek
  • getCommentModDetails
  • getCustFieldsMList
  • getDailyStats
  • getEventPoster
  • getEventProperties
  • getEventRange
  • getFileVersions
  • getHolidayRange
  • GetJumpTo
  • getLanguages
  • getLastPost
  • getLastPosts
  • GetMemberActivationCounts
  • getMemberData
  • getMemberGroups
  • getMembersOnlineStats
  • getMessageIcons
  • getMsgMemberID
  • getNewEventDatetimes
  • getNotifyPrefs
  • getPackageInfo
  • getRawAttachInfo
  • getReportComments
  • getReportDetails
  • getReports
  • getServerVersions
  • getTodayInfo
  • getTopic
  • getTreeOrder
  • getUserTimezone
  • getXmlMembers
  • getXmlNews
  • getXmlProfile
  • getXmlRecent
  • gif_outputAsPng
  • GroupList
  • groupMembership
  • groupMembership2
  • GroupPermissionsReport
  • GroupRequests
  • Groups
  • groupsAllowedTo
  • HandleComment
  • HandleReport
  • hash_benchmark
  • hash_length
  • hash_password
  • hash_salt
  • hash_verify_password
  • HelpIndex
  • HelpRules
  • highlight
  • highlight_php_code
  • host_from_ip
  • html_to_bbc
  • htmlspecialchars__recursive
  • htmltrim__recursive
  • https_redirect_active
  • httpsOn
  • iCalDownload
  • ignoreboards
  • imagecopyresamplebicubic
  • imagecreatefrombmp
  • imageMemoryCheck
  • ImportSmileys
  • inet_dtop
  • inet_ptod
  • initialize_inputs
  • InMaintenance
  • insertBanGroup
  • insertEvent
  • InstallCopy
  • InstallDir
  • installer_updateSettingsFile
  • installExit
  • InstallFile
  • InstallSmileySet
  • ip2range
  • iri_to_url
  • is_not_banned
  • is_not_guest
  • isAccessiblePM
  • isAllowedTo
  • isBannedEmail
  • isBrowser
  • isChildOf
  • isReservedName
  • issueWarning
  • isValidIP
  • isValidIPv6
  • JavaScriptEscape
  • JavaScriptModify
  • jeffsdatediff
  • JSMembers
  • KickGuest
  • legalise_bbc
  • list_getAttachDirs
  • list_getAttachments
  • list_getBanItems
  • list_getBanLogEntries
  • list_getBans
  • list_getBanTriggers
  • list_getBaseDirs
  • list_getBoardNotifications
  • list_getFiles
  • list_getGroupRequestCount
  • list_getGroupRequests
  • list_getGroupRequestsCount
  • list_getHolidays
  • list_getIPMessageCount
  • list_getIPMessages
  • list_getLanguages
  • list_getLanguagesList
  • list_getLoginCount
  • list_getLogins
  • list_getMailQueue
  • list_getMailQueueSize
  • list_getMembergroups
  • list_getMembers
  • list_getMessageIcons
  • list_getModLogEntries
  • list_getModLogEntryCount
  • list_getNews
  • list_getNumAttachments
  • list_getNumBanItems
  • list_getNumBanLogEntries
  • list_getNumBans
  • list_getNumBanTriggers
  • list_getNumFiles
  • list_getNumHolidays
  • list_getNumLanguages
  • list_getNumMembers
  • list_getNumSmileys
  • list_getNumSmileySets
  • list_getNumSpiderLogs
  • list_getNumSpiders
  • list_getNumSpiderStats
  • list_getNumTaskLogEntries
  • list_getNumUnapprovedAttachments
  • list_getNumUnwatched
  • list_getPackages
  • list_getProfileEditCount
  • list_getProfileEdits
  • list_getProfileFields
  • list_getProfileFieldSize
  • list_getScheduledTasks
  • list_getSmileys
  • list_getSmileySets
  • list_getSpiderLogs
  • list_getSpiders
  • list_getSpiderStats
  • list_getSubscribedUserCount
  • list_getSubscribedUsers
  • list_getTaskLogEntries
  • list_getTopicNotificationCount
  • list_getTopicNotifications
  • list_getUnapprovedAttachments
  • list_getUnwatched
  • list_getUserErrorCount
  • list_getUserErrors
  • list_getUserWarningCount
  • list_getUserWarnings
  • list_getWarningCount
  • list_getWarnings
  • list_getWarningTemplateCount
  • list_getWarningTemplates
  • list_getWatchedUserCount
  • list_getWatchedUserPosts
  • list_getWatchedUserPostsCount
  • list_getWatchedUsers
  • list_integration_hooks
  • listMembergroupMembers_Href
  • ListMessageIcons
  • listtree
  • load_database
  • load_file
  • load_lang_file
  • loadAttachmentContext
  • loadBoard
  • loadCacheAccelerator
  • loadCacheAPIs
  • loadCSSFile
  • loadCustomFields
  • loadDatabase
  • loadDatePair
  • loadDatePicker
  • loadEmailTemplate
  • loadEssentialData
  • loadEssentialThemeData
  • loadForumTests
  • loadGeneralSettingParameters
  • loadIllegalBBCHtmlGroups
  • loadIllegalGuestPermissions
  • loadIllegalPermissions
  • loadInstalledPackages
  • loadJavaScriptFile
  • loadLanguage
  • loadLocale
  • loadMemberContext
  • loadMemberCustomFields
  • loadMemberData
  • loadPaymentGateways
  • loadPermissionProfiles
  • loadPermissions
  • loadProfileFields
  • LoadRules
  • loadSearchAPIs
  • loadSession
  • loadSubscriptions
  • loadSubTemplate
  • loadTemplate
  • loadTheme
  • loadThemeOptions
  • loadTimePicker
  • loadUserSettings
  • LockTopic
  • LockVoting
  • log_ban
  • log_error
  • log_error_online
  • logAction
  • logActions
  • Login
  • Login2
  • LoginTFA
  • logLastDatabaseError
  • Logout
  • logSpider
  • logTriggersUpdates
  • MaintainCleanCache
  • MaintainDatabase
  • MaintainEmptyUnimportantLogs
  • MaintainFiles
  • MaintainFindFixErrors
  • MaintainMassMoveTopics
  • MaintainMembers
  • MaintainPurgeInactiveMembers
  • MaintainReattributePosts
  • MaintainRecountPosts
  • MaintainRemoveOldDrafts
  • MaintainRemoveOldPosts
  • MaintainRoutine
  • MaintainTopics
  • makeCustomFieldChanges
  • makeFilesWritable
  • makeNotificationChanges
  • makeThemeChanges
  • ManageAttachmentPaths
  • ManageAttachments
  • ManageAttachmentSettings
  • ManageAvatarSettings
  • ManageBoards
  • ManageBoardsMain
  • ManageCalendar
  • ManageLabels
  • ManageLanguages
  • ManageMail
  • ManageMaintenance
  • ManageNews
  • ManagePaidSubscriptions
  • ManagePostSettings
  • ManageRules
  • ManageScheduledTasks
  • ManageSearch
  • ManageSearchEngineSettings
  • ManageSmileys
  • markBoardsRead
  • markMessages
  • MarkRead
  • matchHighestPackageVersion
  • matchIPtoCIDR
  • matchPackageVersion
  • md5_hmac
  • MembergroupIndex
  • MembergroupMembers
  • MemberGroupsReport
  • Memberlist
  • membersAllowedTo
  • MembersAwaitingActivation
  • memoryReturnBytes
  • MergeDone
  • MergeExecute
  • MergeIndex
  • mergePosts
  • MergeTopics
  • MessageActionsApply
  • MessageDrafts
  • MessageFolder
  • MessageIndex
  • messageIndexBar
  • MessageKillAll
  • MessageMain
  • MessagePopup
  • MessagePost
  • MessagePost2
  • messagePostError
  • MessagePrune
  • MessageSearch
  • MessageSearch2
  • MessageSettings
  • mimespecialchars
  • mktree
  • MLAll
  • MLSearch
  • ModBlockGroupRequests
  • ModBlockNotes
  • ModBlockReportedMembers
  • ModBlockReportedPosts
  • ModBlockWatchedUsers
  • ModEndSession
  • ModerateGroups
  • ModerationHome
  • ModerationMain
  • ModerationSettings
  • ModifyAlertsSettings
  • ModifyAntispamSettings
  • ModifyBasicSettings
  • ModifyBBCSettings
  • modifyBoard
  • ModifyCacheSettings
  • ModifyCalendarSettings
  • ModifyCat
  • modifyCategory
  • ModifyCookieSettings
  • ModifyDatabaseSettings
  • ModifyDraftSettings
  • modifyEvent
  • ModifyFeatureSettings
  • ModifyGeneralModSettings
  • ModifyGeneralSecuritySettings
  • ModifyGeneralSettings
  • ModifyHolidays
  • ModifyLanguage
  • ModifyLanguages
  • ModifyLanguageSettings
  • ModifyLayoutSettings
  • ModifyLikesSettings
  • ModifyLoadBalancingSettings
  • ModifyLogSettings
  • ModifyMailSettings
  • ModifyMembergroup
  • ModifyMembergroup2
  • ModifyMembergroups
  • ModifyMembergroupsettings
  • ModifyMentionsSettings
  • ModifyModSettings
  • ModifyNewsSettings
  • ModifyPermissions
  • modifyPost
  • ModifyPostModeration
  • ModifyPostSettings
  • ModifyProfile
  • ModifyRegistrationSettings
  • ModifySettings
  • ModifySignatureSettings
  • ModifySubscription
  • ModifySubscriptionSettings
  • ModifyTopicSettings
  • ModifyUserSubscription
  • ModifyWarningSettings
  • ModifyWarningTemplate
  • MoveTopic
  • MoveTopic2
  • moveTopicConcurrence
  • moveTopics
  • MySQLConvertOldIp
  • newsletterpreview
  • newspreview
  • newTable
  • next_time
  • nextSubstep
  • notification
  • ob_sessrewrite
  • obExit
  • obExit_cron
  • OptimizeTables
  • package_chmod
  • package_create_backup
  • package_crypt
  • package_flush_cache
  • package_get_contents
  • package_put_contents
  • PackageBrowse
  • PackageDownload
  • PackageFTPTest
  • PackageGBrowse
  • PackageGet
  • PackageInstall
  • PackageInstallTest
  • PackageList
  • PackageOptions
  • PackagePermissions
  • PackagePermissionsAction
  • PackageRemove
  • packageRequireFTP
  • Packages
  • PackageServerAdd
  • PackageServerRemove
  • PackageServers
  • PackageUpload
  • parse_bbc
  • parse_path
  • parse_sql
  • parseAttachBBC
  • parseBoardMod
  • parseModification
  • parsePackageInfo
  • parsesmileys
  • password_get_info
  • password_hash
  • password_needs_rehash
  • password_verify
  • pauseAttachmentMaintenance
  • pauseMailQueueClear
  • pauseRepairProcess
  • pauseSignatureApplySettings
  • perform_task
  • PermissionByBoard
  • PermissionIndex
  • permute
  • php_version_check
  • phpBB3_password_check
  • PickTheme
  • PlushSearch1
  • PlushSearch2
  • populateDuplicateMembers
  • Post
  • Post2
  • PostModerationMain
  • prepareAttachsByMsg
  • prepareCLIhandler
  • prepareDBSettingContext
  • prepareDisplayContext
  • prepareLikesContext
  • prepareMailingForPreview
  • prepareMessageContext
  • prepareSearchContext
  • prepareServerSettingsContext
  • preparsecode
  • print_error
  • printMemberListRows
  • PrintTopic
  • processAttachments
  • profile_popup
  • profileLoadAvatarData
  • profileLoadGroups
  • profileLoadLanguages
  • profileLoadSignatureData
  • profileReloadUser
  • profileSaveAvatarData
  • profileSaveGroups
  • profileSendActivation
  • profileValidateEmail
  • profileValidateSignature
  • protected_alter
  • quickFileWritable
  • QuickInTopicModeration
  • QuickModeration
  • QuoteFast
  • random_bytes
  • random_int
  • RandomCompat_intval
  • RandomCompat_strlen
  • RandomCompat_substr
  • range2ip
  • read_tgz_data
  • read_tgz_file
  • read_zip_data
  • read_zip_file
  • ReadDraft
  • reapplySubscriptions
  • reattributePosts
  • rebuildModCache
  • recacheSpiderNames
  • RecentPosts
  • recountOpenReports
  • recursiveBoards
  • redirectexit
  • redirectLocation
  • ReduceMailQueue
  • reencodeImage
  • RegCenter
  • Register
  • Register2
  • RegisterCheckUsername
  • registerMember
  • registerSMStats
  • reloadSettings
  • RemindMe
  • RemindPick
  • remove_dir
  • remove_integration_function
  • remove_theme
  • RemoveAllAttachments
  • RemoveAttachment
  • RemoveAttachmentByAge
  • RemoveAttachmentBySize
  • removeBanGroups
  • removeBanLogs
  • removeBanTriggers
  • removeDeleteConcurrence
  • removeEvent
  • removeHolidays
  • removeIllegalBBCHtmlPermission
  • removeMembersFromGroups
  • removeMessage
  • removeMessages
  • RemoveOldTopics2
  • RemovePoll
  • removeSubscription
  • RemoveTheme
  • RemoveTopic2
  • removeTopics
  • reorderBoards
  • RepairAttachments
  • RepairBoards
  • replaceEntities__callback
  • ReportDetails
  • ReportedContent
  • ReportedMembers
  • ReportMessage
  • reportPost
  • ReportsMain
  • ReportToModerator
  • ReportToModerator2
  • reportUser
  • RequestMembers
  • resetPassword
  • resizeImage
  • resizeImageFile
  • RestoreTopic
  • RetrievePreview
  • safe_file_write
  • safe_serialize
  • safe_unserialize
  • sanitize_iri
  • sanitizeMSCutPaste
  • saveDBSettings
  • SaveDraft
  • saveModComment
  • SavePMDraft
  • saveProfileChanges
  • saveProfileFields
  • saveSettings
  • saveTriggers
  • scheduled_birthdayemails
  • scheduled_daily_digest
  • scheduled_daily_maintenance
  • scheduled_fetchSMfiles
  • scheduled_paid_subscriptions
  • scheduled_remove_old_drafts
  • scheduled_remove_temp_attachments
  • scheduled_remove_topic_redirect
  • scheduled_weekly_digest
  • scheduled_weekly_maintenance
  • ScheduledTasks
  • SearchEngines
  • SearchMembers
  • searchSort
  • SecretAnswer2
  • SecretAnswerInput
  • secureDirectory
  • SelectMailingMembers
  • send_http_status
  • SendActivation
  • sendmail
  • SendMailing
  • sendNotifications
  • sendpm
  • sentence_list
  • serialize_to_json
  • sessionClose
  • sessionDestroy
  • sessionGC
  • sessionOpen
  • sessionRead
  • sessionWrite
  • set_alert_icon
  • set_avatar_data
  • set_fatal_error_headers
  • set_tld_regex
  • SetCensor
  • setEventStartEnd
  • SetJavaScript
  • setKeys
  • setLoginCookie
  • setMemoryLimit
  • setNotifyPrefs
  • setPassword
  • setPassword2
  • SetQuickGroups
  • SetReserved
  • setSqlMode
  • setTFACookie
  • SetThemeOptions
  • SetThemeSettings
  • setup_fatal_error_context
  • setupMenuContext
  • setupProfileContext
  • setupThemeContext
  • sha1_core
  • sha1_ft
  • sha1_kt
  • sha1_raw
  • sha1_rol
  • sha1_smf
  • shorten_subject
  • ShowAdminHelp
  • showAlerts
  • showAttachment
  • showAttachments
  • ShowClosedReports
  • showCodeImage
  • ShowCustomProfiles
  • ShowDrafts
  • ShowHelp
  • showLetterImage
  • ShowNotice
  • showPermissions
  • ShowPHPinfoSettings
  • showPMDrafts
  • showPosts
  • showProfileDrafts
  • ShowReports
  • showUnwatched
  • ShowXmlFeed
  • sig_preview
  • smf_chmod
  • smf_crc32
  • smf_db_add_column
  • smf_db_add_index
  • smf_db_affected_rows
  • smf_db_allow_persistent
  • smf_db_backup_table
  • smf_db_calculate_type
  • smf_db_change_column
  • smf_db_create_query_column
  • smf_db_create_table
  • smf_db_create_word_search
  • smf_db_cte_support
  • smf_db_custom_order
  • smf_db_drop_table
  • smf_db_error
  • smf_db_error_backtrace
  • smf_db_error_insert
  • smf_db_escape_string
  • smf_db_escape_wildcard_string
  • smf_db_fetch_all
  • smf_db_get_server_info
  • smf_db_get_vendor
  • smf_db_get_version
  • smf_db_initiate
  • smf_db_insert
  • smf_db_insert_id
  • smf_db_list_columns
  • smf_db_list_indexes
  • smf_db_list_tables
  • smf_db_native_replace
  • smf_db_optimize_table
  • smf_db_query
  • smf_db_quote
  • smf_db_remove_column
  • smf_db_remove_index
  • smf_db_replacement__callback
  • smf_db_search_language
  • smf_db_search_query
  • smf_db_search_support
  • smf_db_select
  • smf_db_select_db
  • smf_db_table_sql
  • smf_db_table_structure
  • smf_db_transaction
  • smf_db_version
  • smf_error_handler
  • smf_error_handler_cron
  • smf_is_resource
  • smf_json_decode
  • smf_list_timezones
  • smf_main
  • smf_mysql_fetch_assoc
  • smf_mysql_fetch_row
  • smf_mysql_free_result
  • smf_mysql_insert_id
  • smf_mysql_num_rows
  • smf_mysql_real_escape_string
  • smf_seed_generator
  • smf_serverResponse
  • smf_setcookie
  • smf_strtolower
  • smf_var_export
  • SMStats
  • sortBoards
  • sortCategories
  • spamProtection
  • spell_check
  • spell_init
  • spell_suggest
  • SpellCheck
  • SpiderCheck
  • SpiderLogs
  • SpiderStats
  • SplitExecute
  • SplitIndex
  • SplitSelectionExecute
  • SplitSelectTopics
  • splitTopic
  • SplitTopics
  • ssi_boardNews
  • ssi_boardStats
  • ssi_checkPassword
  • ssi_copyright
  • ssi_fetchGroupMembers
  • ssi_fetchMember
  • ssi_fetchPosts
  • ssi_full_version
  • ssi_latestMember
  • ssi_login
  • ssi_logOnline
  • ssi_logout
  • ssi_menubar
  • ssi_news
  • ssi_pollVote
  • ssi_queryMembers
  • ssi_queryPosts
  • ssi_quickSearch
  • ssi_randomMember
  • ssi_recentAttachments
  • ssi_recentEvents
  • ssi_recentPoll
  • ssi_recentPosts
  • ssi_recentTopics
  • ssi_showPoll
  • ssi_shutdown
  • ssi_software_year
  • ssi_todaysBirthdays
  • ssi_todaysCalendar
  • ssi_todaysEvents
  • ssi_todaysHolidays
  • ssi_topBoards
  • ssi_topPoll
  • ssi_topPoster
  • ssi_topTopics
  • ssi_topTopicsReplies
  • ssi_topTopicsViews
  • ssi_version
  • ssi_welcome
  • ssi_whosOnline
  • ssl_cert_found
  • StaffReport
  • statPanel
  • Sticky
  • strip_php_comments
  • stripslashes__recursive
  • subscriptions
  • summary
  • TaskLog
  • TaskSettings
  • template_action_permissions
  • template_add_edit_group_boards_list
  • template_add_language
  • template_add_rule
  • template_additional_rows
  • template_addsmiley
  • template_admin
  • template_admin_account
  • template_admin_browse
  • template_admin_login
  • template_admin_quick_search
  • template_admin_register
  • template_admin_search_results
  • template_after
  • template_alert_configuration
  • template_alert_notifications_boards
  • template_alert_notifications_topics
  • template_alerts_all_read
  • template_alerts_popup
  • template_announce
  • template_announcement_send
  • template_ask
  • template_ask_delete
  • template_attachment_errors
  • template_attachment_paths
  • template_attachment_repair
  • template_avatar_settings_above
  • template_avatar_settings_below
  • template_backup_database
  • template_backup_xml
  • template_ban_edit
  • template_ban_edit_trigger
  • template_bcd
  • template_bi_board_children
  • template_bi_board_icon
  • template_bi_board_info
  • template_bi_board_lastpost
  • template_bi_board_stats
  • template_bi_redirect_icon
  • template_bi_redirect_stats
  • template_boardindex_outer_above
  • template_boardindex_outer_below
  • template_body_above
  • template_body_below
  • template_browse
  • template_button_strip
  • template_by_board
  • template_calendar_top
  • template_callback_question_answer_list
  • template_check_username
  • template_chmod
  • template_chmod_files
  • template_choose_payment
  • template_clean_cache_button_above
  • template_clean_cache_button_below
  • template_confirm_board_delete
  • template_confirm_category_delete
  • template_control_chmod
  • template_control_richedit
  • template_control_richedit_buttons
  • template_control_verification
  • template_convert_entities
  • template_convert_msgbody
  • template_convert_utf8
  • template_convert_xml
  • template_coppa
  • template_coppa_form
  • template_copy_template
  • template_create_index
  • template_create_index_done
  • template_create_index_progress
  • template_create_list_menu
  • template_credits
  • template_css
  • template_database_changes
  • template_database_settings
  • template_database_xml
  • template_delete_install
  • template_delete_subscription
  • template_deleteAccount
  • template_download_language
  • template_downloaded
  • template_edit_agreement
  • template_edit_browse
  • template_edit_censored
  • template_edit_comment
  • template_edit_file
  • template_edit_group
  • template_edit_holiday
  • template_edit_list
  • template_edit_options
  • template_edit_profile_field
  • template_edit_profiles
  • template_edit_reserved_words
  • template_edit_scheduled_tasks
  • template_edit_style
  • template_edit_template
  • template_editBuddies
  • template_editicon
  • template_editicons
  • template_editIgnoreList
  • template_editsets
  • template_email_members
  • template_email_members_compose
  • template_email_members_send
  • template_error_log
  • template_error_message
  • template_event_post
  • template_examine
  • template_extract_package
  • template_fatal_error
  • template_file_permissions
  • template_find_members
  • template_folder
  • template_footer
  • template_forum_settings
  • template_ftp_required
  • template_generic
  • template_generic_menu
  • template_generic_menu_dropdown_above
  • template_generic_menu_dropdown_below
  • template_generic_menu_tabs
  • template_generic_xml
  • template_generic_xml_recursive
  • template_group_members
  • template_group_request_reason
  • template_group_requests_block
  • template_groupMembership
  • template_header
  • template_hms
  • template_homepage_sample1
  • template_homepage_sample1_html
  • template_homepage_sample1_php
  • template_html_above
  • template_html_below
  • template_ic_block_calendar
  • template_ic_block_online
  • template_ic_block_recent
  • template_ic_block_stats
  • template_ignoreboards
  • template_include
  • template_info_center
  • template_init
  • template_inline_permissions
  • template_install_above
  • template_install_below
  • template_install_options
  • template_installed
  • template_issueWarning
  • template_javascript
  • template_jump_to
  • template_kick_guest
  • template_labels
  • template_like
  • template_list
  • template_list_themes
  • template_load_warning_variables
  • template_login
  • template_login_tfa
  • template_mailtest
  • template_main
  • template_maint_warning_above
  • template_maint_warning_below
  • template_maintain_database
  • template_maintain_members
  • template_maintain_routine
  • template_maintain_topics
  • template_maintenance
  • template_manual
  • template_max_size
  • template_menu
  • template_merge
  • template_merge_done
  • template_merge_extra_options
  • template_message_icons
  • template_moderation_center
  • template_moderation_settings
  • template_modify_board
  • template_modify_category
  • template_modify_group
  • template_modify_group_display
  • template_modify_language_entries
  • template_modify_subscription
  • template_modify_user_subscription
  • template_modify_weights
  • template_modifydone
  • template_modifyfast
  • template_modifyset
  • template_modifysmiley
  • template_modifytopicdone
  • template_move
  • template_new_group
  • template_news_lists
  • template_newsfader
  • template_not_done
  • template_notes
  • template_notify_board
  • template_omfg
  • template_optimize
  • template_options
  • template_package_confirm
  • template_package_list
  • template_paid_done
  • template_permission_index
  • template_permission_show_contents
  • template_php_info
  • template_pick
  • template_pm
  • template_pm_above
  • template_pm_below
  • template_pm_popup
  • template_populate_database
  • template_popup
  • template_post
  • template_post_header
  • template_postmod_permissions
  • template_print
  • template_print_above
  • template_print_below
  • template_print_options
  • template_profile_above
  • template_profile_avatar_select
  • template_profile_below
  • template_profile_birthdate
  • template_profile_group_manage
  • template_profile_pm_settings
  • template_profile_popup
  • template_profile_save
  • template_profile_signature_modify
  • template_profile_smiley_pick
  • template_profile_tfa
  • template_profile_theme_pick
  • template_profile_theme_settings
  • template_profile_timeformat_modify
  • template_prune
  • template_quickbuttons
  • template_quickreply
  • template_quotefast
  • template_recent
  • template_redirect_options
  • template_registration_agreement
  • template_registration_form
  • template_reminder_pick
  • template_repair_boards
  • template_replies
  • template_report_message
  • template_report_message_complete
  • template_report_type
  • template_reported_members
  • template_reported_members_block
  • template_reported_posts
  • template_reported_posts_block
  • template_reported_users_block
  • template_resend
  • template_reset_list
  • template_results
  • template_retry_activate
  • template_rules
  • template_search
  • template_search_members
  • template_search_results
  • template_select
  • template_select_search_method
  • template_send
  • template_sendbody
  • template_sent
  • template_serialize_json
  • template_serialize_json_xml
  • template_servers
  • template_set_options
  • template_set_password
  • template_set_settings
  • template_setorder
  • template_settings
  • template_show_backtrace
  • template_show_custom_profile
  • template_show_file
  • template_show_list
  • template_show_month_grid
  • template_show_notice
  • template_show_settings
  • template_show_spider_logs
  • template_show_spider_stats
  • template_show_upcoming_list
  • template_show_week_grid
  • template_showAlerts
  • template_showDrafts
  • template_showPermissions
  • template_showPMDrafts
  • template_showPosts
  • template_single_pm
  • template_single_post
  • template_spellcheck
  • template_spider_edit
  • template_split
  • template_ssi_above
  • template_ssi_below
  • template_statPanel
  • template_stats
  • template_subject_list
  • template_summary
  • template_terms
  • template_tfadisable
  • template_tfasetup
  • template_tfasetup_backup
  • template_thetime
  • template_topic_legend
  • template_trackActivity
  • template_trackIP
  • template_unapproved_posts
  • template_unread
  • template_upgrade_above
  • template_upgrade_below
  • template_upgrade_complete
  • template_upgrade_options
  • template_user_subscription
  • template_user_watch_post_callback
  • template_verification_sound
  • template_view_operations
  • template_view_package
  • template_view_scheduled_tasks
  • template_view_versions
  • template_viewmemberreport
  • template_viewmodreport
  • template_viewWarning
  • template_warn_template
  • template_warning
  • template_warning_divs
  • template_watched_users
  • template_welcome_message
  • template_xml_above
  • template_xml_below
  • TestMailSend
  • text2words
  • textfield_alter
  • tfadisable
  • tfasetup
  • theme
  • theme_copyright
  • theme_inline_permissions
  • theme_install
  • theme_linktree
  • ThemeAdmin
  • ThemeInstall
  • ThemeList
  • ThemesMain
  • throw_error
  • time_since
  • timeformat
  • TopicNotify
  • trackActivity
  • trackEdits
  • trackGroupReq
  • tracking
  • TrackIP
  • TrackLogins
  • trackStats
  • trackStatsUsersOnline
  • TransferAttachments
  • truncate_array
  • un_htmlspecialchars
  • un_preparsecode
  • UnapprovedAttachments
  • UnapprovedPosts
  • unescapestring__recursive
  • UnreadTopics
  • updateAdminPreferences
  • updateBanGroup
  • updateBanMembers
  • updateBoardManagers
  • updateChildPermissions
  • updateDbLastError
  • updateLastMessages
  • updateMemberData
  • updateReport
  • updateSettings
  • updateSettingsFile
  • updateStats
  • updateTriggers
  • upgrade_clean_cache
  • upgrade_query
  • upgradeExit
  • upgradeGetColumnInfo
  • UpgradeOptions
  • url_exists
  • url_image_size
  • url_parts
  • url_to_iri
  • urldecode__recursive
  • user_info_callback
  • utf8_strtolower
  • utf8_strtoupper
  • validate_iri
  • validateEventPost
  • validatePassword
  • validatePasswordFlood
  • validateSession
  • validateToken
  • validateTriggers
  • validateUsername
  • VerificationCode
  • VersionDetail
  • ViewBacktrace
  • ViewErrorLog
  • ViewFile
  • ViewMemberlist
  • ViewMembers
  • ViewModlog
  • ViewOperations
  • ViewQuery
  • ViewSpiders
  • ViewSubscribedUsers
  • ViewSubscriptions
  • viewWarning
  • ViewWarningLog
  • ViewWarnings
  • ViewWarningTemplates
  • ViewWatchedUsers
  • Vote
  • warning_preview
  • Welcome
  • WelcomeLogin
  • Who
  • WrapAction
  • writeLog
  • XmlDraft
  • XMLhttpMain
  • Overview
  • Namespace
  • Class
   1:    2:    3:    4:    5:    6:    7:    8:    9:   10:   11:   12:   13:   14:   15:   16:   17:   18:   19:   20:   21:   22:   23:   24:   25:   26:   27:   28:   29:   30:   31:   32:   33:   34:   35:   36:   37:   38:   39:   40:   41:   42:   43:   44:   45:   46:   47:   48:   49:   50:   51:   52:   53:   54:   55:   56:   57:   58:   59:   60:   61:   62:   63:   64:   65:   66:   67:   68:   69:   70:   71:   72:   73:   74:   75:   76:   77:   78:   79:   80:   81:   82:   83:   84:   85:   86:   87:   88:   89:   90:   91:   92:   93:   94:   95:   96:   97:   98:   99:  100:  101:  102:  103:  104:  105:  106:  107:  108:  109:  110:  111:  112:  113:  114:  115:  116:  117:  118:  119:  120:  121:  122:  123:  124:  125:  126:  127:  128:  129:  130:  131:  132:  133:  134:  135:  136:  137:  138:  139:  140:  141:  142:  143:  144:  145:  146:  147:  148:  149:  150:  151:  152:  153:  154:  155:  156:  157:  158:  159:  160:  161:  162:  163:  164:  165:  166:  167:  168:  169:  170:  171:  172:  173:  174:  175:  176:  177:  178:  179:  180:  181:  182:  183:  184:  185:  186:  187:  188:  189:  190:  191:  192:  193:  194:  195:  196:  197:  198:  199:  200:  201:  202:  203:  204:  205:  206:  207:  208:  209:  210:  211:  212:  213:  214:  215:  216:  217:  218:  219:  220:  221:  222:  223:  224:  225:  226:  227:  228:  229:  230:  231:  232:  233:  234:  235:  236:  237:  238:  239:  240:  241:  242:  243:  244:  245:  246:  247:  248:  249:  250:  251:  252:  253:  254:  255:  256:  257:  258:  259:  260:  261:  262:  263:  264:  265:  266:  267:  268:  269:  270:  271:  272:  273:  274:  275:  276:  277:  278:  279:  280:  281:  282:  283:  284:  285:  286:  287:  288:  289:  290:  291:  292:  293:  294:  295:  296:  297:  298:  299:  300:  301:  302:  303:  304:  305:  306:  307:  308:  309:  310:  311:  312:  313:  314:  315:  316:  317:  318:  319:  320:  321:  322:  323:  324:  325:  326:  327:  328:  329:  330:  331:  332:  333:  334:  335:  336:  337:  338:  339:  340:  341:  342:  343:  344:  345:  346:  347:  348:  349:  350:  351:  352:  353:  354:  355:  356:  357:  358:  359:  360:  361:  362:  363:  364:  365:  366:  367:  368:  369:  370:  371:  372:  373:  374:  375:  376:  377:  378:  379:  380:  381:  382:  383:  384:  385:  386:  387:  388:  389:  390:  391:  392:  393:  394:  395:  396:  397:  398:  399:  400:  401:  402:  403:  404:  405:  406:  407:  408:  409:  410:  411:  412:  413:  414:  415:  416:  417:  418:  419:  420:  421:  422:  423:  424:  425:  426:  427:  428:  429:  430:  431:  432:  433:  434:  435:  436:  437:  438:  439:  440:  441:  442:  443:  444:  445:  446:  447:  448:  449:  450:  451:  452:  453:  454:  455:  456:  457:  458:  459:  460:  461:  462:  463:  464:  465:  466:  467:  468:  469:  470:  471:  472:  473:  474:  475:  476:  477:  478:  479:  480:  481:  482:  483:  484:  485:  486:  487:  488:  489:  490:  491:  492:  493:  494:  495:  496:  497:  498:  499:  500:  501:  502:  503:  504:  505:  506:  507:  508:  509:  510:  511:  512:  513:  514:  515:  516:  517:  518:  519:  520:  521:  522:  523:  524:  525:  526:  527:  528:  529:  530:  531:  532:  533:  534:  535:  536:  537:  538:  539:  540:  541:  542:  543:  544:  545:  546:  547:  548:  549:  550:  551:  552:  553:  554:  555:  556:  557:  558:  559:  560:  561:  562:  563:  564:  565:  566:  567:  568:  569:  570:  571:  572:  573:  574:  575:  576:  577:  578:  579:  580:  581:  582:  583:  584:  585:  586:  587:  588:  589:  590:  591:  592:  593:  594:  595:  596:  597:  598:  599:  600:  601:  602:  603:  604:  605:  606:  607:  608:  609:  610:  611:  612:  613:  614:  615:  616:  617:  618:  619:  620:  621:  622:  623:  624:  625:  626:  627:  628:  629:  630:  631:  632:  633:  634:  635:  636:  637:  638:  639:  640:  641:  642:  643:  644:  645:  646:  647:  648:  649:  650:  651:  652:  653:  654:  655:  656:  657:  658:  659:  660:  661:  662:  663:  664:  665:  666:  667:  668:  669:  670:  671:  672:  673:  674:  675:  676:  677:  678:  679:  680:  681:  682:  683:  684:  685:  686:  687:  688:  689:  690:  691:  692:  693:  694:  695:  696:  697:  698:  699:  700:  701:  702:  703:  704:  705:  706:  707:  708:  709:  710:  711:  712:  713:  714:  715:  716:  717:  718:  719:  720:  721:  722:  723:  724:  725:  726:  727:  728:  729:  730:  731:  732:  733:  734:  735:  736:  737:  738:  739:  740:  741:  742:  743:  744:  745:  746:  747:  748:  749:  750:  751:  752:  753:  754:  755:  756:  757:  758:  759:  760:  761:  762:  763:  764:  765:  766:  767:  768:  769:  770:  771:  772:  773:  774:  775:  776:  777:  778:  779:  780:  781:  782:  783:  784:  785:  786:  787:  788:  789:  790:  791:  792:  793:  794:  795:  796:  797:  798:  799:  800:  801:  802:  803:  804:  805:  806:  807:  808:  809:  810:  811:  812:  813:  814:  815:  816:  817:  818:  819:  820:  821:  822:  823:  824:  825:  826:  827:  828:  829:  830:  831:  832:  833:  834:  835:  836:  837:  838:  839:  840:  841:  842:  843:  844:  845:  846:  847:  848:  849:  850:  851:  852:  853:  854:  855:  856:  857:  858:  859:  860:  861:  862:  863:  864:  865:  866:  867:  868:  869:  870:  871:  872:  873:  874:  875:  876:  877:  878:  879:  880:  881:  882:  883:  884:  885:  886:  887:  888:  889:  890:  891:  892:  893:  894:  895:  896:  897:  898:  899:  900:  901:  902:  903:  904:  905:  906:  907:  908:  909:  910:  911:  912:  913:  914:  915:  916:  917:  918:  919:  920:  921:  922:  923:  924:  925:  926:  927:  928:  929:  930:  931:  932:  933:  934:  935:  936:  937:  938:  939:  940:  941:  942:  943:  944:  945:  946:  947:  948:  949:  950:  951:  952:  953:  954:  955:  956:  957:  958:  959:  960:  961:  962:  963:  964:  965:  966:  967:  968:  969:  970:  971:  972:  973:  974:  975:  976:  977:  978:  979:  980:  981:  982:  983:  984:  985:  986:  987:  988:  989:  990:  991:  992:  993:  994:  995:  996:  997:  998:  999: 1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029: 1030: 1031: 1032: 1033: 1034: 1035: 1036: 1037: 1038: 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047: 1048: 1049: 1050: 1051: 1052: 1053: 1054: 1055: 1056: 1057: 1058: 1059: 1060: 1061: 1062: 1063: 1064: 1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073: 1074: 1075: 1076: 1077: 1078: 1079: 1080: 1081: 1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091: 1092: 1093: 1094: 1095: 1096: 1097: 1098: 1099: 1100: 1101: 1102: 1103: 1104: 1105: 1106: 1107: 1108: 1109: 1110: 1111: 1112: 1113: 1114: 1115: 1116: 1117: 1118: 1119: 1120: 1121: 1122: 1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131: 1132: 1133: 1134: 1135: 1136: 1137: 1138: 1139: 1140: 1141: 1142: 1143: 1144: 1145: 1146: 1147: 1148: 1149: 1150: 1151: 1152: 1153: 1154: 1155: 1156: 1157: 1158: 1159: 1160: 1161: 1162: 1163: 1164: 1165: 1166: 1167: 1168: 1169: 1170: 1171: 1172: 1173: 1174: 1175: 1176: 1177: 1178: 1179: 1180: 1181: 1182: 1183: 1184: 1185: 1186: 1187: 1188: 1189: 1190: 1191: 1192: 1193: 1194: 1195: 1196: 1197: 1198: 1199: 1200: 1201: 1202: 1203: 1204: 1205: 1206: 1207: 1208: 1209: 1210: 1211: 1212: 1213: 1214: 1215: 1216: 1217: 1218: 1219: 1220: 1221: 1222: 1223: 1224: 1225: 1226: 1227: 1228: 1229: 1230: 1231: 1232: 1233: 1234: 1235: 1236: 1237: 1238: 1239: 1240: 1241: 1242: 1243: 1244: 1245: 1246: 1247: 1248: 1249: 1250: 1251: 1252: 1253: 1254: 1255: 1256: 1257: 1258: 1259: 1260: 1261: 1262: 1263: 1264: 1265: 1266: 1267: 1268: 1269: 1270: 1271: 1272: 1273: 1274: 1275: 1276: 1277: 1278: 1279: 1280: 1281: 1282: 1283: 1284: 1285: 1286: 1287: 1288: 1289: 1290: 1291: 1292: 1293: 1294: 1295: 1296: 1297: 1298: 1299: 1300: 1301: 1302: 1303: 1304: 1305: 1306: 1307: 1308: 1309: 1310: 1311: 1312: 1313: 1314: 1315: 1316: 1317: 1318: 1319: 1320: 1321: 1322: 1323: 1324: 1325: 1326: 1327: 1328: 1329: 1330: 1331: 1332: 1333: 1334: 1335: 1336: 1337: 1338: 1339: 1340: 1341: 1342: 1343: 1344: 1345: 1346: 1347: 1348: 1349: 1350: 1351: 1352: 1353: 1354: 1355: 1356: 1357: 1358: 1359: 1360: 1361: 1362: 1363: 1364: 1365: 1366: 1367: 1368: 1369: 1370: 1371: 1372: 1373: 1374: 1375: 1376: 1377: 1378: 1379: 1380: 1381: 1382: 1383: 1384: 1385: 1386: 1387: 1388: 1389: 1390: 1391: 1392: 1393: 1394: 1395: 1396: 1397: 1398: 1399: 1400: 1401: 1402: 1403: 1404: 1405: 1406: 1407: 1408: 1409: 1410: 1411: 1412: 1413: 1414: 1415: 1416: 1417: 1418: 1419: 1420: 1421: 1422: 1423: 1424: 1425: 1426: 1427: 1428: 1429: 1430: 1431: 1432: 1433: 1434: 1435: 1436: 1437: 1438: 1439: 1440: 1441: 1442: 1443: 1444: 1445: 1446: 1447: 1448: 1449: 1450: 1451: 1452: 1453: 1454: 1455: 1456: 1457: 1458: 1459: 1460: 1461: 1462: 1463: 1464: 1465: 1466: 1467: 1468: 1469: 1470: 1471: 1472: 1473: 1474: 1475: 1476: 1477: 1478: 1479: 1480: 1481: 1482: 1483: 1484: 1485: 1486: 1487: 1488: 1489: 1490: 1491: 1492: 1493: 1494: 1495: 1496: 1497: 1498: 1499: 1500: 1501: 1502: 1503: 1504: 1505: 1506: 1507: 1508: 1509: 1510: 1511: 1512: 1513: 1514: 1515: 1516: 1517: 1518: 1519: 1520: 1521: 1522: 1523: 1524: 1525: 1526: 1527: 1528: 1529: 1530: 1531: 1532: 1533: 1534: 1535: 1536: 1537: 1538: 1539: 1540: 1541: 1542: 1543: 1544: 1545: 1546: 1547: 1548: 1549: 1550: 1551: 1552: 1553: 1554: 1555: 1556: 1557: 1558: 1559: 1560: 1561: 1562: 1563: 1564: 1565: 1566: 1567: 1568: 1569: 1570: 1571: 1572: 1573: 1574: 1575: 1576: 1577: 1578: 1579: 1580: 1581: 1582: 1583: 1584: 1585: 1586: 1587: 1588: 1589: 1590: 1591: 1592: 1593: 1594: 1595: 1596: 1597: 1598: 1599: 1600: 1601: 1602: 1603: 1604: 1605: 1606: 1607: 1608: 1609: 1610: 1611: 1612: 1613: 1614: 1615: 1616: 1617: 1618: 1619: 1620: 1621: 1622: 1623: 1624: 1625: 1626: 1627: 1628: 1629: 1630: 1631: 1632: 1633: 1634: 1635: 1636: 1637: 1638: 1639: 1640: 1641: 1642: 1643: 1644: 1645: 1646: 1647: 1648: 1649: 1650: 1651: 1652: 1653: 1654: 1655: 1656: 1657: 1658: 1659: 1660: 1661: 1662: 1663: 1664: 1665: 1666: 1667: 1668: 1669: 1670: 1671: 1672: 1673: 1674: 1675: 1676: 1677: 1678: 1679: 1680: 1681: 1682: 1683: 1684: 1685: 1686: 1687: 1688: 1689: 1690: 1691: 1692: 1693: 1694: 1695: 1696: 1697: 1698: 1699: 1700: 1701: 1702: 1703: 1704: 1705: 1706: 1707: 1708: 1709: 1710: 1711: 1712: 1713: 1714: 1715: 1716: 1717: 1718: 1719: 1720: 1721: 1722: 1723: 1724: 1725: 1726: 1727: 1728: 1729: 1730: 1731: 1732: 1733: 1734: 1735: 1736: 1737: 1738: 1739: 1740: 1741: 1742: 1743: 1744: 1745: 1746: 1747: 1748: 1749: 1750: 1751: 1752: 1753: 1754: 1755: 1756: 1757: 1758: 1759: 1760: 1761: 1762: 1763: 1764: 1765: 1766: 1767: 1768: 1769: 1770: 1771: 1772: 1773: 1774: 1775: 1776: 1777: 1778: 1779: 1780: 1781: 1782: 1783: 1784: 1785: 1786: 1787: 1788: 1789: 1790: 1791: 1792: 1793: 1794: 1795: 1796: 1797: 1798: 1799: 1800: 1801: 1802: 1803: 1804: 1805: 1806: 1807: 1808: 1809: 1810: 1811: 1812: 1813: 1814: 1815: 1816: 1817: 1818: 1819: 1820: 1821: 1822: 1823: 1824: 1825: 1826: 1827: 1828: 1829: 1830: 1831: 1832: 1833: 1834: 1835: 1836: 1837: 1838: 1839: 1840: 1841: 1842: 1843: 1844: 1845: 1846: 1847: 1848: 1849: 1850: 1851: 1852: 1853: 1854: 1855: 1856: 1857: 1858: 1859: 1860: 1861: 1862: 1863: 1864: 1865: 1866: 1867: 1868: 1869: 1870: 1871: 1872: 1873: 1874: 1875: 1876: 1877: 1878: 1879: 1880: 1881: 1882: 1883: 1884: 1885: 1886: 1887: 1888: 1889: 1890: 1891: 1892: 1893: 1894: 1895: 1896: 1897: 1898: 1899: 1900: 1901: 1902: 1903: 1904: 1905: 1906: 1907: 1908: 1909: 1910: 1911: 1912: 1913: 1914: 1915: 1916: 1917: 1918: 1919: 1920: 1921: 1922: 1923: 1924: 1925: 1926: 1927: 1928: 1929: 1930: 1931: 1932: 1933: 1934: 1935: 1936: 1937: 1938: 1939: 1940: 1941: 1942: 1943: 1944: 1945: 1946: 1947: 1948: 1949: 1950: 1951: 1952: 1953: 1954: 1955: 1956: 1957: 1958: 1959: 1960: 1961: 1962: 1963: 1964: 1965: 1966: 1967: 1968: 1969: 1970: 1971: 1972: 1973: 1974: 1975: 1976: 1977: 1978: 1979: 1980: 1981: 1982: 1983: 1984: 1985: 1986: 1987: 1988: 1989: 1990: 1991: 1992: 1993: 1994: 1995: 1996: 1997: 1998: 1999: 2000: 2001: 2002: 2003: 2004: 2005: 2006: 2007: 2008: 2009: 2010: 2011: 2012: 2013: 2014: 2015: 2016: 2017: 2018: 2019: 2020: 2021: 2022: 2023: 2024: 2025: 2026: 2027: 2028: 2029: 2030: 2031: 2032: 2033: 2034: 2035: 2036: 2037: 2038: 2039: 2040: 2041: 2042: 2043: 2044: 2045: 2046: 2047: 2048: 2049: 2050: 2051: 2052: 2053: 2054: 2055: 2056: 2057: 2058: 2059: 2060: 2061: 2062: 2063: 2064: 2065: 2066: 2067: 2068: 2069: 2070: 2071: 2072: 2073: 2074: 2075: 2076: 2077: 2078: 2079: 2080: 2081: 2082: 2083: 2084: 2085: 2086: 2087: 2088: 2089: 2090: 2091: 2092: 2093: 2094: 2095: 2096: 2097: 2098: 2099: 2100: 2101: 2102: 2103: 2104: 2105: 2106: 2107: 2108: 2109: 2110: 2111: 2112: 2113: 2114: 2115: 2116: 2117: 2118: 2119: 2120: 2121: 2122: 2123: 2124: 2125: 2126: 2127: 2128: 2129: 2130: 2131: 2132: 2133: 2134: 2135: 2136: 2137: 2138: 2139: 2140: 2141: 2142: 2143: 2144: 2145: 2146: 2147: 2148: 2149: 2150: 2151: 2152: 2153: 2154: 2155: 2156: 2157: 2158: 2159: 2160: 2161: 2162: 2163: 2164: 2165: 2166: 2167: 2168: 
<?php

/**
 * This file contains functions that are specifically done by administrators.
 *
 * Simple Machines Forum (SMF)
 *
 * @package SMF
 * @author Simple Machines https://www.simplemachines.org
 * @copyright 2020 Simple Machines and individual contributors
 * @license https://www.simplemachines.org/about/smf/license.php BSD
 *
 * @version 2.1 RC2
 */

if (!defined('SMF'))
    die('No direct access...');

/**
 * Get a list of versions that are currently installed on the server.
 *
 * @param array $checkFor An array of what to check versions for - can contain one or more of 'gd', 'imagemagick', 'db_server', 'phpa', 'memcache', 'xcache', 'apc', 'php' or 'server'
 * @return array An array of versions (keys are same as what was in $checkFor, values are the versions)
 */
function getServerVersions($checkFor)
{
    global $txt, $db_connection, $_PHPA, $smcFunc, $cache_accelerator, $cache_memcached, $cacheAPI, $modSettings;

    loadLanguage('Admin');

    $versions = array();

    // Is GD available?  If it is, we should show version information for it too.
    if (in_array('gd', $checkFor) && function_exists('gd_info'))
    {
        $temp = gd_info();
        $versions['gd'] = array('title' => $txt['support_versions_gd'], 'version' => $temp['GD Version']);
    }

    // Why not have a look at ImageMagick? If it's installed, we should show version information for it too.
    if (in_array('imagemagick', $checkFor) && (class_exists('Imagick') || function_exists('MagickGetVersionString')))
    {
        if (class_exists('Imagick'))
        {
            $temp = New Imagick;
            $temp2 = $temp->getVersion();
            $im_version = $temp2['versionString'];
            $extension_version = 'Imagick ' . phpversion('Imagick');
        }
        else
        {
            $im_version = MagickGetVersionString();
            $extension_version = 'MagickWand ' . phpversion('MagickWand');
        }

        // We already know it's ImageMagick and the website isn't needed...
        $im_version = str_replace(array('ImageMagick ', ' https://www.imagemagick.org'), '', $im_version);
        $versions['imagemagick'] = array('title' => $txt['support_versions_imagemagick'], 'version' => $im_version . ' (' . $extension_version . ')');
    }

    // Now lets check for the Database.
    if (in_array('db_server', $checkFor))
    {
        db_extend();
        if (!isset($db_connection) || $db_connection === false)
            trigger_error('getServerVersions(): you need to be connected to the database in order to get its server version', E_USER_NOTICE);
        else
        {
            $versions['db_engine'] = array('title' => sprintf($txt['support_versions_db_engine'], $smcFunc['db_title']), 'version' => '');
            $versions['db_engine']['version'] = $smcFunc['db_get_vendor']();

            $versions['db_server'] = array('title' => sprintf($txt['support_versions_db'], $smcFunc['db_title']), 'version' => '');
            $versions['db_server']['version'] = $smcFunc['db_get_version']();
        }
    }

    // If we're using memcache we need the server info.
    $memcache_version = '???';
    if (!empty($cache_accelerator) && ($cache_accelerator == 'memcached' || $cache_accelerator == 'memcache') && !empty($cache_memcached) && !empty($cacheAPI))
        $memcache_version = $cacheAPI->getVersion();

    // Check to see if we have any accelerators installed...
    if (in_array('phpa', $checkFor) && isset($_PHPA))
        $versions['phpa'] = array('title' => 'ionCube PHP-Accelerator', 'version' => $_PHPA['VERSION']);
    if (in_array('apc', $checkFor) && extension_loaded('apc'))
        $versions['apc'] = array('title' => 'Alternative PHP Cache', 'version' => phpversion('apc'));
    if (in_array('memcache', $checkFor) && function_exists('memcache_set'))
        $versions['memcache'] = array('title' => 'Memcached', 'version' => $memcache_version);
    if (in_array('xcache', $checkFor) && function_exists('xcache_set'))
        $versions['xcache'] = array('title' => 'XCache', 'version' => XCACHE_VERSION);

    if (in_array('php', $checkFor))
        $versions['php'] = array('title' => 'PHP', 'version' => PHP_VERSION, 'more' => '?action=admin;area=serversettings;sa=phpinfo');

    if (in_array('server', $checkFor))
        $versions['server'] = array('title' => $txt['support_versions_server'], 'version' => $_SERVER['SERVER_SOFTWARE']);

    return $versions;
}

/**
 * Search through source, theme and language files to determine their version.
 * Get detailed version information about the physical SMF files on the server.
 *
 * - the input parameter allows to set whether to include SSI.php and whether
 *   the results should be sorted.
 * - returns an array containing information on source files, templates and
 *   language files found in the default theme directory (grouped by language).
 *
 * @param array &$versionOptions An array of options. Can contain one or more of 'include_ssi', 'include_subscriptions', 'include_tasks' and 'sort_results'
 * @return array An array of file version info.
 */
function getFileVersions(&$versionOptions)
{
    global $boarddir, $sourcedir, $settings, $tasksdir;

    // Default place to find the languages would be the default theme dir.
    $lang_dir = $settings['default_theme_dir'] . '/languages';

    $version_info = array(
        'file_versions' => array(),
        'default_template_versions' => array(),
        'template_versions' => array(),
        'default_language_versions' => array(),
        'tasks_versions' => array(),
    );

    // Find the version in SSI.php's file header.
    if (!empty($versionOptions['include_ssi']) && file_exists($boarddir . '/SSI.php'))
    {
        $fp = fopen($boarddir . '/SSI.php', 'rb');
        $header = fread($fp, 4096);
        fclose($fp);

        // The comment looks rougly like... that.
        if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
            $version_info['file_versions']['SSI.php'] = $match[1];
        // Not found!  This is bad.
        else
            $version_info['file_versions']['SSI.php'] = '??';
    }

    // Do the paid subscriptions handler?
    if (!empty($versionOptions['include_subscriptions']) && file_exists($boarddir . '/subscriptions.php'))
    {
        $fp = fopen($boarddir . '/subscriptions.php', 'rb');
        $header = fread($fp, 4096);
        fclose($fp);

        // Found it?
        if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
            $version_info['file_versions']['subscriptions.php'] = $match[1];
        // If we haven't how do we all get paid?
        else
            $version_info['file_versions']['subscriptions.php'] = '??';
    }

    // Load all the files in the Sources directory, except this file and the redirect.
    $sources_dir = dir($sourcedir);
    while ($entry = $sources_dir->read())
    {
        if (substr($entry, -4) === '.php' && !is_dir($sourcedir . '/' . $entry) && $entry !== 'index.php')
        {
            // Read the first 4k from the file.... enough for the header.
            $fp = fopen($sourcedir . '/' . $entry, 'rb');
            $header = fread($fp, 4096);
            fclose($fp);

            // Look for the version comment in the file header.
            if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
                $version_info['file_versions'][$entry] = $match[1];
            // It wasn't found, but the file was... show a '??'.
            else
                $version_info['file_versions'][$entry] = '??';
        }
    }
    $sources_dir->close();

    // Load all the files in the tasks directory.
    if (!empty($versionOptions['include_tasks']))
    {
        $tasks_dir = dir($tasksdir);
        while ($entry = $tasks_dir->read())
        {
            if (substr($entry, -4) === '.php' && !is_dir($tasksdir . '/' . $entry) && $entry !== 'index.php')
            {
                // Read the first 4k from the file.... enough for the header.
                $fp = fopen($tasksdir . '/' . $entry, 'rb');
                $header = fread($fp, 4096);
                fclose($fp);

                // Look for the version comment in the file header.
                if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
                    $version_info['tasks_versions'][$entry] = $match[1];
                // It wasn't found, but the file was... show a '??'.
                else
                    $version_info['tasks_versions'][$entry] = '??';
            }
        }
        $tasks_dir->close();
    }

    // Load all the files in the default template directory - and the current theme if applicable.
    $directories = array('default_template_versions' => $settings['default_theme_dir']);
    if ($settings['theme_id'] != 1)
        $directories += array('template_versions' => $settings['theme_dir']);

    foreach ($directories as $type => $dirname)
    {
        $this_dir = dir($dirname);
        while ($entry = $this_dir->read())
        {
            if (substr($entry, -12) == 'template.php' && !is_dir($dirname . '/' . $entry))
            {
                // Read the first 768 bytes from the file.... enough for the header.
                $fp = fopen($dirname . '/' . $entry, 'rb');
                $header = fread($fp, 768);
                fclose($fp);

                // Look for the version comment in the file header.
                if (preg_match('~\*\s@version\s+(.+)[\s]{2}~i', $header, $match) == 1)
                    $version_info[$type][$entry] = $match[1];
                // It wasn't found, but the file was... show a '??'.
                else
                    $version_info[$type][$entry] = '??';
            }
        }
        $this_dir->close();
    }

    // Load up all the files in the default language directory and sort by language.
    $this_dir = dir($lang_dir);
    while ($entry = $this_dir->read())
    {
        if (substr($entry, -4) == '.php' && $entry != 'index.php' && !is_dir($lang_dir . '/' . $entry))
        {
            // Read the first 768 bytes from the file.... enough for the header.
            $fp = fopen($lang_dir . '/' . $entry, 'rb');
            $header = fread($fp, 768);
            fclose($fp);

            // Split the file name off into useful bits.
            list ($name, $language) = explode('.', $entry);

            // Look for the version comment in the file header.
            if (preg_match('~(?://|/\*)\s*Version:\s+(.+?);\s*' . preg_quote($name, '~') . '(?:[\s]{2}|\*/)~i', $header, $match) == 1)
                $version_info['default_language_versions'][$language][$name] = $match[1];
            // It wasn't found, but the file was... show a '??'.
            else
                $version_info['default_language_versions'][$language][$name] = '??';
        }
    }
    $this_dir->close();

    // Sort the file versions by filename.
    if (!empty($versionOptions['sort_results']))
    {
        ksort($version_info['file_versions']);
        ksort($version_info['default_template_versions']);
        ksort($version_info['template_versions']);
        ksort($version_info['default_language_versions']);
        ksort($version_info['tasks_versions']);

        // For languages sort each language too.
        foreach ($version_info['default_language_versions'] as $language => $dummy)
            ksort($version_info['default_language_versions'][$language]);
    }
    return $version_info;
}

/**
 * Update the Settings.php file.
 *
 * The most important function in this file for mod makers happens to be the
 * updateSettingsFile() function, but it shouldn't be used often anyway.
 *
 * - Updates the Settings.php file with the changes supplied in config_vars.
 *
 * - Expects config_vars to be an associative array, with the keys as the
 *   variable names in Settings.php, and the values the variable values.
 *
 * - Correctly formats the values using smf_var_export().
 *
 * - Restores standard formatting of the file, if $rebuild is true.
 *
 * - Checks for changes to db_last_error and passes those off to a separate
 *   handler.
 *
 * - Creates a backup file and will use it should the writing of the
 *   new settings file fail.
 *
 * - Tries to intelligently trim quotes and remove slashes from string values.
 *   This is done for backwards compatibility purposes (old versions of this
 *   function expected strings to have been manually escaped and quoted). This
 *   behaviour can be controlled by the $keep_quotes parameter.
 *
 * @param array $config_vars An array of one or more variables to update.
 * @param bool|null $keep_quotes Whether to strip slashes & trim quotes from string values. Defaults to auto-detection.
 * @param bool $rebuild If true, attempts to rebuild with standard format. Default false.
 * @return bool True on success, false on failure.
 */
function updateSettingsFile($config_vars, $keep_quotes = null, $rebuild = false)
{
    // In this function we intentionally don't declare any global variables.
    // This allows us to work with everything cleanly.

    static $mtime;

    // Should we try to unescape the strings?
    if (empty($keep_quotes))
    {
        foreach ($config_vars as $var => $val)
        {
            if (is_string($val) && ($keep_quotes === false || strpos($val, '\'') === 0 && strrpos($val, '\'') === strlen($val) - 1))
                $config_vars[$var] = trim(stripcslashes($val), '\'');
        }
    }

    // Updating the db_last_error, then don't mess around with Settings.php
    if (isset($config_vars['db_last_error']))
    {
        updateDbLastError($config_vars['db_last_error']);

        if (count($config_vars) === 1 && empty($rebuild))
            return true;

        // Make sure we delete this from Settings.php, if present.
        $config_vars['db_last_error'] = 0;
    }

    // Rebuilding should not be undertaken lightly, so we're picky about the parameter.
    if (!is_bool($rebuild))
        $rebuild = false;

    $mtime = isset($mtime) ? (int) $mtime : (defined('TIME_START') ? TIME_START : $_SERVER['REQUEST_TIME']);

    /*****************
     * PART 1: Setup *
     *****************/

    // Typically Settings.php is in $boarddir, but maybe this is a custom setup...
    foreach (get_included_files() as $settingsFile)
        if (basename($settingsFile) === 'Settings.php')
            break;

    // Fallback in case Settings.php isn't loaded (e.g. while installing)
    if (basename($settingsFile) !== 'Settings.php')
        $settingsFile = (!empty($GLOBALS['boarddir']) && @realpath($GLOBALS['boarddir']) ? $GLOBALS['boarddir'] : (!empty($_SERVER['SCRIPT_FILENAME']) ? dirname($_SERVER['SCRIPT_FILENAME']) : dirname(__DIR__))) . '/Settings.php';

    // File not found? Attempt an emergency on-the-fly fix!
    if (!file_exists($settingsFile))
        @touch($settingsFile);

    // When was Settings.php last changed?
    $last_settings_change = filemtime($settingsFile);

    // Get the current values of everything in Settings.php.
    $settings_vars = get_current_settings($mtime, $settingsFile);

    // If Settings.php is empty for some reason, see if we can use the backup.
    if (empty($settings_vars) && file_exists(dirname($settingsFile) . '/Settings_bak.php'))
        $settings_vars = get_current_settings($mtime, dirname($settingsFile) . '/Settings_bak.php');

    // False means there was a problem with the file and we can't safely continue.
    if ($settings_vars === false)
        return false;

    // It works best to set everything afresh.
    $new_settings_vars = array_merge($settings_vars, $config_vars);

    // Are we using UTF-8?
    $utf8 = isset($GLOBALS['context']['utf8']) ? $GLOBALS['context']['utf8'] : (isset($GLOBALS['utf8']) ? $GLOBALS['utf8'] : (isset($settings_vars['db_character_set']) ? $settings_vars['db_character_set'] === 'utf8' : false));

    /*
     * A big, fat array to define properties of all the Settings.php variables.
     *
     * - String keys are used to identify actual variables.
     *
     * - Integer keys are used for content not connected to any particular
     *   variable, such as code blocks or the license block.
     *
     * - The content of the 'text' element is simply printed out, if it is used
     *   at all. Use it for comments or to insert code blocks, etc.
     *
     * - The 'default' element, not surprisingly, gives a default value for
     *   the variable.
     *
     * - The 'type' element defines the expected variable type or types. If
     *   more than one type is allowed, this should be an array listing them.
     *   Types should match the possible types returned by gettype().
     *
     * - If 'raw_default' is true, the default should be printed directly,
     *   rather than being handled as a string. Use it if the default contains
     *   code, e.g. 'dirname(__FILE__)'
     *
     * - If 'required' is true and a value for the variable is undefined,
     *   the update will be aborted. (The only exception is during the SMF
     *   installation process.)
     *
     * - If 'auto_delete' is 1 or true and the variable is empty, the variable
     *   will be deleted from Settings.php. If 'auto_delete' is 0/false/null,
     *   the variable will never be deleted. If 'auto_delete' is 2, behaviour
     *   depends on $rebuild: if $rebuild is true, 'auto_delete' == 2 behaves
     *   like 'auto_delete' == 1; if $rebuild is false, 'auto_delete' == 2
     *   behaves like 'auto_delete' == 0.
     *
     * - The optional 'search_pattern' element defines a custom regular
     *   expression to search for the existing entry in the file. This is
     *   primarily useful for code blocks rather than variables.
     *
     * - The optional 'replace_pattern' element defines a custom regular
     *   expression to decide where the replacement entry should be inserted.
     *   Note: 'replace_pattern' should be avoided unless ABSOLUTELY necessary.
     */
    $settings_defs = array(
        array(
            'text' => implode("\n", array(
                '',
                '/**',
                ' * The settings file contains all of the basic settings that need to be present when a database/cache is not available.',
                ' *',
                ' * Simple Machines Forum (SMF)',
                ' *',
                ' * @package SMF',
                ' * @author Simple Machines https://www.simplemachines.org',
                ' * @copyright ' . SMF_SOFTWARE_YEAR . ' Simple Machines and individual contributors',
                ' * @license https://www.simplemachines.org/about/smf/license.php BSD',
                ' *',
                ' * @version ' . SMF_VERSION,
                ' */',
                '',
            )),
            'search_pattern' => '~/\*\*.*?@package\h+SMF\b.*?\*/\n{0,2}~s',
        ),
        'maintenance' => array(
            'text' => implode("\n", array(
                '',
                '########## Maintenance ##########',
                '/**',
                ' * The maintenance "mode"',
                ' * Set to 1 to enable Maintenance Mode, 2 to make the forum untouchable. (you\'ll have to make it 0 again manually!)',
                ' * 0 is default and disables maintenance mode.',
                ' *',
                ' * @var int 0, 1, 2',
                ' * @global int $maintenance',
                ' */',
            )),
            'default' => 0,
            'type' => 'integer',
        ),
        'mtitle' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Title for the Maintenance Mode message.',
                ' *',
                ' * @var string',
                ' * @global int $mtitle',
                ' */',
            )),
            'default' => 'Maintenance Mode',
            'type' => 'string',
        ),
        'mmessage' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Description of why the forum is in maintenance mode.',
                ' *',
                ' * @var string',
                ' * @global string $mmessage',
                ' */',
            )),
            'default' => 'Okay faithful users...we\'re attempting to restore an older backup of the database...news will be posted once we\'re back!',
            'type' => 'string',
        ),
        'mbname' => array(
            'text' => implode("\n", array(
                '',
                '########## Forum Info ##########',
                '/**',
                ' * The name of your forum.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'My Community',
            'type' => 'string',
        ),
        'language' => array(
            'text' => implode("\n", array(
                '/**',
                ' * The default language file set for the forum.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'english',
            'type' => 'string',
        ),
        'boardurl' => array(
            'text' => implode("\n", array(
                '/**',
                ' * URL to your forum\'s folder. (without the trailing /!)',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'http://127.0.0.1/smf',
            'type' => 'string',
        ),
        'webmaster_email' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Email address to send emails from. (like noreply@yourdomain.com.)',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'noreply@myserver.com',
            'type' => 'string',
        ),
        'cookiename' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Name of the cookie to set for authentication.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'SMFCookie11',
            'type' => 'string',
        ),
        'db_type' => array(
            'text' => implode("\n", array(
                '',
                '########## Database Info ##########',
                '/**',
                ' * The database type',
                ' * Default options: mysql, postgresql',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'mysql',
            'type' => 'string',
        ),
        'db_port' => array(
            'text' => implode("\n", array(
                '/**',
                ' * The database port',
                ' * 0 to use default port for the database type',
                ' *',
                ' * @var int',
                ' */',
            )),
            'default' => 0,
            'type' => 'integer',
        ),
        'db_server' => array(
            'text' => implode("\n", array(
                '/**',
                ' * The server to connect to (or a Unix socket)',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'localhost',
            'required' => true,
            'type' => 'string',
        ),
        'db_name' => array(
            'text' => implode("\n", array(
                '/**',
                ' * The database name',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'smf',
            'required' => true,
            'type' => 'string',
        ),
        'db_user' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Database username',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'root',
            'required' => true,
            'type' => 'string',
        ),
        'db_passwd' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Database password',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => '',
            'required' => true,
            'type' => 'string',
        ),
        'ssi_db_user' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Database user for when connecting with SSI',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => '',
            'type' => 'string',
        ),
        'ssi_db_passwd' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Database password for when connecting with SSI',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => '',
            'type' => 'string',
        ),
        'db_prefix' => array(
            'text' => implode("\n", array(
                '/**',
                ' * A prefix to put in front of your table names.',
                ' * This helps to prevent conflicts',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'smf_',
            'required' => true,
            'type' => 'string',
        ),
        'db_persist' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Use a persistent database connection',
                ' *',
                ' * @var bool',
                ' */',
            )),
            'default' => false,
            'type' => 'boolean',
        ),
        'db_error_send' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Send emails on database connection error',
                ' *',
                ' * @var bool',
                ' */',
            )),
            'default' => false,
            'type' => 'boolean',
        ),
        'db_mb4' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Override the default behavior of the database layer for mb4 handling',
                ' * null keep the default behavior untouched',
                ' *',
                ' * @var null|bool',
                ' */',
            )),
            'default' => null,
            'type' => array('NULL', 'boolean'),
        ),
        'cache_accelerator' => array(
            'text' => implode("\n", array(
                '',
                '########## Cache Info ##########',
                '/**',
                ' * Select a cache system. You want to leave this up to the cache area of the admin panel for',
                ' * proper detection of apc, memcached, output_cache, smf, or xcache',
                ' * (you can add more with a mod).',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => '',
            'type' => 'string',
        ),
        'cache_enable' => array(
            'text' => implode("\n", array(
                '/**',
                ' * The level at which you would like to cache. Between 0 (off) through 3 (cache a lot).',
                ' *',
                ' * @var int',
                ' */',
            )),
            'default' => 0,
            'type' => 'integer',
        ),
        'cache_memcached' => array(
            'text' => implode("\n", array(
                '/**',
                ' * This is only used for memcache / memcached. Should be a string of \'server:port,server:port\'',
                ' *',
                ' * @var array',
                ' */',
            )),
            'default' => '',
            'type' => 'string',
        ),
        'cachedir' => array(
            'text' => implode("\n", array(
                '/**',
                ' * This is only for the \'smf\' file cache system. It is the path to the cache directory.',
                ' * It is also recommended that you place this in /tmp/ if you are going to use this.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'dirname(__FILE__) . \'/cache\'',
            'raw_default' => true,
            'type' => 'string',
        ),
        'image_proxy_enabled' => array(
            'text' => implode("\n", array(
                '',
                '########## Image Proxy ##########',
                '# This is done entirely in Settings.php to avoid loading the DB while serving the images',
                '/**',
                ' * Whether the proxy is enabled or not',
                ' *',
                ' * @var bool',
                ' */',
            )),
            'default' => true,
            'type' => 'boolean',
        ),
        'image_proxy_secret' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Secret key to be used by the proxy',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'smfisawesome',
            'type' => 'string',
        ),
        'image_proxy_maxsize' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Maximum file size (in KB) for individual files',
                ' *',
                ' * @var int',
                ' */',
            )),
            'default' => 5192,
            'type' => 'integer',
        ),
        'boarddir' => array(
            'text' => implode("\n", array(
                '',
                '########## Directories/Files ##########',
                '# Note: These directories do not have to be changed unless you move things.',
                '/**',
                ' * The absolute path to the forum\'s folder. (not just \'.\'!)',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'dirname(__FILE__)',
            'raw_default' => true,
            'type' => 'string',
        ),
        'sourcedir' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Path to the Sources directory.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'dirname(__FILE__) . \'/Sources\'',
            'raw_default' => true,
            'type' => 'string',
        ),
        'packagesdir' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Path to the Packages directory.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => 'dirname(__FILE__) . \'/Packages\'',
            'raw_default' => true,
            'type' => 'string',
        ),
        'tasksdir' => array(
            'text' => implode("\n", array(
                '/**',
                ' * Path to the tasks directory.',
                ' *',
                ' * @var string',
                ' */',
            )),
            'default' => '$sourcedir . \'/tasks\'',
            'raw_default' => true,
            'type' => 'string',
        ),
        array(
            'text' => implode("\n", array(
                '',
                '# Make sure the paths are correct... at least try to fix them.',
                'if (!is_dir(realpath($boarddir)) && file_exists(dirname(__FILE__) . \'/agreement.txt\'))',
                '   $boarddir = dirname(__FILE__);',
                'if (!is_dir(realpath($sourcedir)) && is_dir($boarddir . \'/Sources\'))',
                '   $sourcedir = $boarddir . \'/Sources\';',
                'if (!is_dir(realpath($tasksdir)) && is_dir($sourcedir . \'/tasks\'))',
                '   $tasksdir = $sourcedir . \'/tasks\';',
                'if (!is_dir(realpath($packagesdir)) && is_dir($boarddir . \'/Packages\'))',
                '   $packagesdir = $boarddir . \'/Packages\';',
                'if (!is_dir(realpath($cachedir)) && is_dir($boarddir . \'/cache\'))',
                '   $cachedir = $boarddir . \'/cache\';',
            )),
            'search_pattern' => '~\n?(#[^\n]+)?(?:\n\h*if\s*\((?:\!file_exists\(\$(?>boarddir|sourcedir|tasksdir|packagesdir|cachedir)\)|\!is_dir\(realpath\(\$(?>boarddir|sourcedir|tasksdir|packagesdir|cachedir)\)\))[^;]+\n\h*\$(?>boarddir|sourcedir|tasksdir|packagesdir|cachedir)[^\n]+;)+~sm',
        ),
        'db_character_set' => array(
            'text' => implode("\n", array(
                '',
                '######### Legacy Settings #########',
                '# UTF-8 is now the only character set supported in 2.1.',
            )),
            'default' => 'utf8',
            'type' => 'string',
        ),
        'db_show_debug' => array(
            'text' => implode("\n", array(
                '',
                '######### Developer Settings #########',
                '# Show debug info.',
            )),
            'default' => false,
            'auto_delete' => 2,
            'type' => 'boolean',
        ),
        array(
            'text' => implode("\n", array(
                '',
                '########## Error-Catching ##########',
                '# Note: You shouldn\'t touch these settings.',
                'if (file_exists((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\'))',
                '   include((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\');',
                '',
                'if (!isset($db_last_error))',
                '{',
                '   // File does not exist so lets try to create it',
                '   file_put_contents((isset($cachedir) ? $cachedir : dirname(__FILE__)) . \'/db_last_error.php\', \'<\' . \'?\' . "php\n" . \'$db_last_error = 0;\' . "\n" . \'?\' . \'>\');',
                '   $db_last_error = 0;',
                '}',
            )),
            // Designed to match both 2.0 and 2.1 versions of this code.
            'search_pattern' => '~\n?#+ Error.Catching #+.*?\$db_last_error = 0;(?' . '>\s*})?(?=\n|\?' . '>|$)~s',
        ),
        // Temporary variable used during the upgrade process.
        'upgradeData' => array(
            'default' => '',
            'auto_delete' => 1,
            'type' => 'string',
        ),
        // This should be removed if found.
        'db_last_error' => array(
            'default' => 0,
            'auto_delete' => 1,
            'type' => 'integer',
        ),
    );

    // Allow mods the option to define comments, defaults, etc., for their settings.
    // Check if function exists, in case we are calling from installer or upgrader.
    if (function_exists('call_integration_hook'))
        call_integration_hook('integrate_update_settings_file', array(&$settings_defs));

    // If Settings.php is empty or invalid, try to recover using whatever is in $GLOBALS.
    if ($settings_vars === array())
    {
        foreach ($settings_defs as $var => $setting_def)
            if (isset($GLOBALS[$var]))
                $settings_vars[$var] = $GLOBALS[$var];

        $new_settings_vars = array_merge($settings_vars, $config_vars);
    }

    // During install/upgrade, don't set anything until we're ready for it.
    if (defined('SMF_INSTALLING') && empty($rebuild))
    {
        foreach ($settings_defs as $var => $setting_def)
            if (!in_array($var, array_keys($new_settings_vars)) && !is_int($var))
                unset($settings_defs[$var]);
    }

    /*******************************
     * PART 2: Build substitutions *
     *******************************/

    $type_regex = array(
        'string' =>
            '(?:' .
                // match the opening quotation mark...
                '(["\'])' .
                // then any number of other characters or escaped quotation marks...
                '(?:.(?!\\1)|\\\(?=\\1))*.?' .
                // then the closing quotation mark.
                '\\1' .
                // Maybe there's a second string concatenated to this one.
                '(?:\s*\.\s*)*' .
            ')+',
        // Some numeric values might have been stored as strings.
        'integer' =>  '["\']?[+-]?\d+["\']?',
        'double' =>  '["\']?[+-]?\d+\.\d+([Ee][+-]\d+)?["\']?',
        // Some boolean values might have been stored as integers.
        'boolean' =>  '(?i:TRUE|FALSE|(["\']?)[01]\b\\1)',
        'NULL' =>  '(?i:NULL)',
        // These use a PCRE subroutine to match nested arrays.
        'array' =>  'array\s*(\((?>[^()]|(?1))*\))',
        'object' =>  '\w+::__set_state\(array\s*(\((?>[^()]|(?1))*\))\)',
    );

    /*
     * The substitutions take place in one of two ways:
     *
     *  1: The search_pattern regex finds a string in Settings.php, which is
     *     temporarily replaced by a placeholder. Once all the placeholders
     *     have been inserted, each is replaced by the final replacement string
     *     that we want to use. This is the standard method.
     *
     *  2: The search_pattern regex finds a string in Settings.php, which is
     *     then deleted by replacing it with an empty placeholder. Then after
     *     all the real placeholders have been dealt with, the replace_pattern
     *     regex finds where to insert the final replacement string that we
     *     want to use. This method is for special cases.
     */
    $prefix = mt_rand() . '-';
    $neg_index = -1;
    $substitutions = array(
        $neg_index-- => array(
            'search_pattern' => '~^\s*<\?(php\b)?\n?~',
            'placeholder' => '',
            'replace_pattern' => '~^~',
            'replacement' => '<' . "?php\n",
        ),
        $neg_index-- => array(
            'search_pattern' => '~\S\K\s*(\?' . '>)?\s*$~',
            'placeholder' => "\n" . md5($prefix . '?' . '>'),
            'replacement' => "\n\n?" . '>',
        ),
        // Remove the code that redirects to the installer.
        $neg_index-- => array(
            'search_pattern' => '~^if\s*\(file_exists\(dirname\(__FILE__\)\s*\.\s*\'/install\.php\'\)\)\s*(?:({(?>[^{}]|(?1))*})\h*|header(\((?' . '>[^()]|(?2))*\));\n)~m',
            'placeholder' => '',
        ),
    );

    if (defined('SMF_INSTALLING'))
        $substitutions[$neg_index--] = array(
            'search_pattern' => '~/\*.*?SMF\s+1\.\d.*?\*/~s',
            'placeholder' => '',
        );

    foreach ($settings_defs as $var => $setting_def)
    {
        $placeholder = md5($prefix . $var);
        $replacement = '';

        if (!empty($setting_def['text']))
        {
            // Special handling for the license block: always at the beginning.
            if (strpos($setting_def['text'], "* @package SMF\n") !== false)
            {
                $substitutions[$var]['search_pattern'] = $setting_def['search_pattern'];
                $substitutions[$var]['placeholder'] = '';
                $substitutions[-1]['replacement'] .= $setting_def['text'] . "\n";
            }
            // Special handling for the Error-Catching block: always at the end.
            elseif (strpos($setting_def['text'], 'Error-Catching') !== false)
            {
                $errcatch_var = $var;
                $substitutions[$var]['search_pattern'] = $setting_def['search_pattern'];
                $substitutions[$var]['placeholder'] = '';
                $substitutions[-2]['replacement'] = "\n" . $setting_def['text'] . $substitutions[-2]['replacement'];
            }
            // The text is the whole thing (code blocks, etc.)
            elseif (is_int($var))
            {
                // Remember the path correcting code for later.
                if (strpos($setting_def['text'], '# Make sure the paths are correct') !== false)
                    $pathcode_var = $var;

                if (!empty($setting_def['search_pattern']))
                    $substitutions[$var]['search_pattern'] = $setting_def['search_pattern'];
                else
                    $substitutions[$var]['search_pattern'] = '~' . preg_quote($setting_def['text'], '~') . '~';

                $substitutions[$var]['placeholder'] = $placeholder;

                $replacement .= $setting_def['text'] . "\n";
            }
            // We only include comments when rebuilding.
            elseif (!empty($rebuild))
                $replacement .= $setting_def['text'] . "\n";
        }

        if (is_string($var))
        {
            // Ensure the value is good.
            if (in_array($var, array_keys($new_settings_vars)))
            {
                // Objects without a __set_state method need a fallback.
                if (is_object($new_settings_vars[$var]) && !method_exists($new_settings_vars[$var], '__set_state'))
                {
                    if (method_exists($new_settings_vars[$var], '__toString'))
                        $new_settings_vars[$var] = (string) $new_settings_vars[$var];
                    else
                        $new_settings_vars[$var] = (array) $new_settings_vars[$var];
                }

                // Normalize the type if necessary.
                if (isset($setting_def['type']))
                {
                    $expected_types = (array) $setting_def['type'];
                    $var_type = gettype($new_settings_vars[$var]);

                    // Variable is not of an expected type.
                    if (!in_array($var_type, $expected_types))
                    {
                        // Passed in an unexpected array.
                        if ($var_type == 'array')
                        {
                            $temp = reset($new_settings_vars[$var]);

                            // Use the first element if there's only one and it is a scalar.
                            if (count($new_settings_vars[$var]) === 1 && is_scalar($temp))
                                $new_settings_vars[$var] = $temp;

                            // Or keep the old value, if that is good.
                            elseif (isset($settings_vars[$var]) && in_array(gettype($settings_vars[$var]), $expected_types))
                                $new_settings_vars[$var] = $settings_vars[$var];

                            // Fall back to the default
                            else
                                $new_settings_vars[$var] = $setting_def['default'];
                        }

                        // Cast it to whatever type was expected.
                        // Note: the order of the types in this loop matters.
                        foreach (array('boolean', 'integer', 'double', 'string', 'array') as $to_type)
                        {
                            if (in_array($to_type, $expected_types))
                            {
                                settype($new_settings_vars[$var], $to_type);
                                break;
                            }
                        }
                    }
                }
            }
            // Abort if a required one is undefined (unless we're installing).
            elseif (!empty($setting_def['required']) && !defined('SMF_INSTALLING'))
                return false;

            // Create the search pattern.
            if (!empty($setting_def['search_pattern']))
                $substitutions[$var]['search_pattern'] = $setting_def['search_pattern'];
            else
            {
                $var_pattern = array();

                if (isset($setting_def['type']))
                {
                    foreach ((array) $setting_def['type'] as $type)
                        $var_pattern[] = $type_regex[$type];
                }

                if (in_array($var, array_keys($config_vars)))
                {
                    $var_pattern[] = @$type_regex[gettype($config_vars[$var])];

                    if (is_string($config_vars[$var]) && strpos($config_vars[$var], dirname($settingsFile)) === 0)
                        $var_pattern[] = '(?:__DIR__|dirname\(__FILE__\)) . \'' . (preg_quote(str_replace(dirname($settingsFile), '', $config_vars[$var]), '~')) . '\'';
                }

                if (in_array($var, array_keys($settings_vars)))
                {
                    $var_pattern[] = @$type_regex[gettype($settings_vars[$var])];

                    if (is_string($settings_vars[$var]) && strpos($settings_vars[$var], dirname($settingsFile)) === 0)
                        $var_pattern[] = '(?:__DIR__|dirname\(__FILE__\)) . \'' . (preg_quote(str_replace(dirname($settingsFile), '', $settings_vars[$var]), '~')) . '\'';
                }

                if (!empty($setting_def['raw_default']) && $setting_def['default'] !== '')
                {
                    $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote($setting_def['default'], '~'));

                    if (strpos($setting_def['default'], 'dirname(__FILE__)') !== false)
                        $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote(str_replace('dirname(__FILE__)', '__DIR__', $setting_def['default']), '~'));

                    if (strpos($setting_def['default'], '__DIR__') !== false)
                        $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote(str_replace('__DIR__', 'dirname(__FILE__)', $setting_def['default']), '~'));
                }

                $var_pattern = array_unique($var_pattern);

                $var_pattern = count($var_pattern) > 1 ? '(?:' . (implode('|', $var_pattern)) . ')' : $var_pattern[0];

                $substitutions[$var]['search_pattern'] = '~(?<=^|\s)\h*\$' . preg_quote($var, '~') . '\s*=\s*' . $var_pattern . ';~' . (!empty($context['utf8']) ? 'u' : '');
            }

            // Next create the placeholder or replace_pattern.
            if (!empty($setting_def['replace_pattern']))
                $substitutions[$var]['replace_pattern'] = $setting_def['replace_pattern'];
            else
                $substitutions[$var]['placeholder'] = $placeholder;

            // Now create the replacement.
            // A setting to delete.
            if (!empty($setting_def['auto_delete']) && empty($new_settings_vars[$var]))
            {
                if ($setting_def['auto_delete'] === 2 && empty($rebuild) && in_array($var, array_keys($new_settings_vars)))
                {
                    $replacement .= '$' . $var . ' = ' . ($new_settings_vars[$var] === $setting_def['default'] && !empty($setting_def['raw_default']) ? sprintf($new_settings_vars[$var]) : smf_var_export($new_settings_vars[$var], true)) . ";";
                }
                else
                {
                    $replacement = '';

                    // This is just for cosmetic purposes. Removes the blank line.
                    $substitutions[$var]['search_pattern'] = str_replace('(?<=^|\s)', '\n?', $substitutions[$var]['search_pattern']);
                }
            }
            // Add this setting's value.
            elseif (in_array($var, array_keys($new_settings_vars)))
            {
                $replacement .= '$' . $var . ' = ' . ($new_settings_vars[$var] === $setting_def['default'] && !empty($setting_def['raw_default']) ? sprintf($new_settings_vars[$var]) : smf_var_export($new_settings_vars[$var], true)) . ";";
            }
            // Fall back to the default value.
            elseif (isset($setting_def['default']))
            {
                $replacement .= '$' . $var . ' = ' . (!empty($setting_def['raw_default']) ? sprintf($setting_def['default']) : smf_var_export($setting_def['default'], true)) . ';';
            }
            // This shouldn't happen, but we've got nothing.
            else
                $replacement .= '$' . $var . ' = null;';
        }

        $substitutions[$var]['replacement'] = $replacement;

        // We're done with this one.
        unset($new_settings_vars[$var]);
    }

    // Any leftovers to deal with?
    foreach ($new_settings_vars as $var => $val)
    {
        $var_pattern = array();

        if (in_array($var, array_keys($config_vars)))
            $var_pattern[] = $type_regex[gettype($config_vars[$var])];

        if (in_array($var, array_keys($settings_vars)))
            $var_pattern[] = $type_regex[gettype($settings_vars[$var])];

        $var_pattern = array_unique($var_pattern);

        $var_pattern = count($var_pattern) > 1 ? '(?:' . (implode('|', $var_pattern)) . ')' : $var_pattern[0];

        $placeholder = md5($prefix . $var);

        $substitutions[$var]['search_pattern'] = '~(?<=^|\s)\h*\$' . preg_quote($var, '~') . '\s*=\s*' . $var_pattern . ';~' . (!empty($context['utf8']) ? 'u' : '');
        $substitutions[$var]['placeholder'] = $placeholder;
        $substitutions[$var]['replacement'] = '$' . $var . ' = ' . smf_var_export($val, true) . ";";
    }

    // During an upgrade, some of the path variables may not have been declared yet.
    if (defined('SMF_INSTALLING') && empty($rebuild))
    {
        preg_match_all('~^\h*\$(\w+)\s*=\s*~m', $substitutions[$pathcode_var]['replacement'], $matches);
        $missing_pathvars = array_diff($matches[1], array_keys($substitutions));

        if (!empty($missing_pathvars))
        {
            foreach ($missing_pathvars as $var)
            {
                $substitutions[$pathcode_var]['replacement'] = preg_replace('~\nif[^\n]+\$' . $var . '[^\n]+\n\h*\$' . $var . ' = [^\n]+~', '', $substitutions[$pathcode_var]['replacement']);
            }
        }
    }

    // It's important to do the numbered ones before the named ones, or messes happen.
    uksort($substitutions, function($a, $b) {
        if (is_int($a) && is_int($b))
            return $a > $b;
        elseif (is_int($a))
            return -1;
        elseif (is_int($b))
            return 1;
        else
            return strcasecmp($b, $a);
    });

    /******************************
     * PART 3: Content processing *
     ******************************/

    /* 3.a: Get the content of Settings.php and make sure it is good. */

    // Retrieve the contents of Settings.php and normalize the line endings.
    $settingsText = trim(strtr(file_get_contents($settingsFile), array("\r\n" => "\n", "\r" => "\n")));

    // If Settings.php is empty or corrupt for some reason, see if we can recover.
    if ($settingsText == '' || substr($settingsText, 0, 5) !== '<' . '?php')
    {
        // Try restoring from the backup.
        if (file_exists(dirname($settingsFile) . '/Settings_bak.php'))
            $settingsText = strtr(file_get_contents(dirname($settingsFile) . '/Settings_bak.php'), array("\r\n" => "\n", "\r" => "\n"));

        // Backup is bad too? Our only option is to create one from scratch.
        if ($settingsText == '' || substr($settingsText, 0, 5) !== '<' . '?php' || substr($settingsText, -2) !== '?' . '>')
        {
            $settingsText = '<' . "?php\n";
            foreach ($settings_defs as $var => $setting_def)
            {
                if (!empty($setting_def['text']) && strpos($substitutions[$var]['replacement'], $setting_def['text']) === false)
                    $substitutions[$var]['replacement'] = $setting_def['text'] . "\n" . $substitutions[$var]['replacement'];

                $settingsText .= $substitutions[$var]['replacement'] . "\n";
            }
            $settingsText .= "\n\n?" . '>';
            $rebuild = true;
        }
    }

    // Settings.php is unlikely to contain any heredocs, but just in case...
    if (preg_match_all('/<<<(\'?)(\w+)\'?\n(.*?)\n\2;$/m', $settingsText, $matches))
    {
        foreach ($matches[0] as $mkey => $heredoc)
        {
            if (!empty($matches[1][$mkey]))
                $heredoc_replacements[$heredoc] = var_export($matches[3][$mkey], true) . ';';
            else
                $heredoc_replacements[$heredoc] = '"' . strtr(substr(var_export($matches[3][$mkey], true), 1, -1), array("\\'" => "'", '"' => '\"')) . '";';
        }

        $settingsText = strtr($settingsText, $heredoc_replacements);
    }

    /* 3.b: Loop through all our substitutions to insert placeholders, etc. */

    $last_var = null;
    $bare_settingsText = $settingsText;
    $force_before_pathcode = array();
    foreach ($substitutions as $var => $substitution)
    {
        $placeholders[$var] = $substitution['placeholder'];

        if (!empty($substitution['placeholder']))
        {
            $simple_replacements[$substitution['placeholder']] = $substitution['replacement'];
        }
        elseif (!empty($substitution['replace_pattern']))
        {
            $replace_patterns[$var] = $substitution['replace_pattern'];
            $replace_strings[$var] = $substitution['replacement'];
        }

        if (strpos($substitutions[$pathcode_var]['replacement'], '$' . $var . ' = ') !== false)
            $force_before_pathcode[] = $var;

        // Look before you leap.
        preg_match_all($substitution['search_pattern'], $bare_settingsText, $matches);

        if ((is_string($var) || $var === $pathcode_var) && count($matches[0]) !== 1 && $substitution['replacement'] !== '')
        {
            // More than one instance of the variable = not good.
            if (count($matches[0]) > 1)
            {
                if (is_string($var))
                {
                    // Maybe we can try something more interesting?
                    $sp = substr($substitution['search_pattern'], 1);

                    if (strpos($sp, '(?<=^|\s)') === 0)
                        $sp = substr($sp, 9);

                    if (strpos($sp, '^') === 0 || strpos($sp, '(?<') === 0)
                        return false;

                    // See if we can exclude `if` blocks, etc., to narrow down the matches.
                    // @todo Multiple layers of nested brackets might confuse this.
                    $sp = '~(?:^|//[^\n]+c\n|\*/|[;}]|' . implode('|', array_filter($placeholders)) . ')\s*' . (strpos($sp, '\K') === false ? '\K' : '') . $sp;

                    preg_match_all($sp, $settingsText, $matches);
                }
                else
                    $sp = $substitution['search_pattern'];

                // Found at least some that are simple assignment statements.
                if (count($matches[0]) > 0)
                {
                    // Remove any duplicates.
                    if (count($matches[0]) > 1)
                        $settingsText = preg_replace($sp, '', $settingsText, count($matches[0]) - 1);

                    // Insert placeholder for the last one.
                    $settingsText = preg_replace($sp, $substitution['placeholder'], $settingsText, 1);
                }

                // All instances are inside more complex code structures.
                else
                {
                    // Only safe option at this point is to skip it.
                    unset($substitutions[$var], $new_settings_vars[$var], $settings_defs[$var], $simple_replacements[$substitution['placeholder']], $replace_patterns[$var], $replace_strings[$var]);

                    continue;
                }
            }
            // No matches found.
            elseif (count($matches[0]) === 0)
            {
                $found = false;
                $in_c = in_array($var, array_keys($config_vars));
                $in_s = in_array($var, array_keys($settings_vars));

                // Is it in there at all?
                if (!preg_match('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*~', $bare_settingsText))
                {
                    // It's defined by Settings.php, but not by code in the file.
                    // Probably done via an include or something. Skip it.
                    if ($in_s)
                        unset($substitutions[$var], $settings_defs[$var]);

                    // Admin is explicitly trying to set this one, so we'll handle
                    // it as if it were a new custom setting being added.
                    elseif ($in_c)
                        $new_settings_vars[$var] = $config_vars[$var];

                    continue;
                }

                // It's in there somewhere, so check if the value changed type.
                foreach (array('scalar', 'object', 'array') as $type)
                {
                    // Try all the other scalar types first.
                    if ($type == 'scalar')
                        $sp = '(?:' . (implode('|', array_diff_key($type_regex, array($in_c ? gettype($config_vars[$var]) : ($in_s ? gettype($settings_vars[$var]) : PHP_INT_MAX) => '', 'array' => '', 'object' => '')))) . ')';

                    // Maybe it's an object? (Probably not, but we should check.)
                    elseif ($type == 'object')
                    {
                        if (strpos($settingsText, '__set_state') === false)
                            continue;

                        $sp = $type_regex['object'];
                    }

                    // Maybe it's an array?
                    else
                        $sp = $type_regex['array'];

                    if (preg_match('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*' . $sp . '~', $bare_settingsText, $derp))
                    {
                        $settingsText = preg_replace('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*' . $sp . '~', $substitution['placeholder'], $settingsText);
                        $found = true;
                        break;
                    }
                }

                // Something weird is going on. Better just leave it alone.
                if (!$found)
                {
                    // $var? What $var? Never heard of it.
                    unset($substitutions[$var], $new_settings_vars[$var], $settings_defs[$var], $simple_replacements[$substitution['placeholder']], $replace_patterns[$var], $replace_strings[$var]);
                    continue;
                }
            }
        }
        // Good to go, so insert our placeholder.
        else
            $settingsText = preg_replace($substitution['search_pattern'], $substitution['placeholder'], $settingsText);

        // Once the code blocks are done, we want to compare to a version without comments.
        if (is_int($last_var) && is_string($var))
            $bare_settingsText = strip_php_comments($settingsText);

        $last_var = $var;
    }

    // Rebuilding requires more work.
    if (!empty($rebuild))
    {
        // Strip out the leading and trailing placeholders to prevent duplication.
        $settingsText = str_replace(array($substitutions[-1]['placeholder'], $substitutions[-2]['placeholder']), '', $settingsText);

        // Strip out all our standard comments.
        foreach ($settings_defs as $var => $setting_def)
        {
            if (isset($setting_def['text']))
                $settingsText = strtr($settingsText, array($setting_def['text'] . "\n" => '', $setting_def['text'] => '',));
        }

        // We need to refresh $bare_settingsText at this point.
        $bare_settingsText = strip_php_comments($settingsText);

        // Fix up whitespace to make comparison easier.
        foreach ($placeholders as $placeholder)
        {
            $bare_settingsText = str_replace(array($placeholder . "\n\n", $placeholder), $placeholder . "\n", $bare_settingsText);
        }
        $bare_settingsText = preg_replace('/\h+$/m', '', rtrim($bare_settingsText));

        /*
         * Divide the existing content into sections.
         * The idea here is to make sure we don't mess with the relative position
         * of any code blocks in the file, since that could break things. Within
         * each section, however, we'll reorganize the content to match the
         * default layout as closely as we can.
         */
        $sections = array(array());
        $section_num = 0;
        $trimmed_placeholders = array_filter(array_map('trim', $placeholders));
        $newsection_placeholders = array();
        $all_custom_content = '';
        foreach ($substitutions as $var => $substitution)
        {
            if (is_int($var) && ($var === -2 || $var > 0) && isset($trimmed_placeholders[$var]) && strpos($bare_settingsText, $trimmed_placeholders[$var]) !== false)
                $newsection_placeholders[$var] = $trimmed_placeholders[$var];
        }
        foreach (preg_split('~(?<=' . implode('|', $trimmed_placeholders) . ')|(?=' . implode('|', $trimmed_placeholders) . ')~', $bare_settingsText) as $part)
        {
            $part = trim($part);

            if (empty($part))
                continue;

            // Build a list of placeholders for this section.
            if (in_array($part, $trimmed_placeholders) && !in_array($part, $newsection_placeholders))
            {
                $sections[$section_num][] = $part;
            }
            // Custom content and newsection_placeholders get their own sections.
            else
            {
                if (!empty($sections[$section_num]))
                    ++$section_num;

                $sections[$section_num][] = $part;

                ++$section_num;

                if (!in_array($part, $trimmed_placeholders))
                    $all_custom_content .= "\n" . $part;
            }
        }

        // And now, rebuild the content!
        $new_settingsText = '';
        $done_defs = array();
        $sectionkeys = array_keys($sections);
        foreach ($sections as $sectionkey => $section)
        {
            // Custom content needs to be preserved.
            if (count($section) === 1 && !in_array($section[0], $trimmed_placeholders))
            {
                $prev_section_end = $sectionkey < 1 ? 0 : strpos($settingsText, end($sections[$sectionkey - 1])) + strlen(end($sections[$sectionkey - 1]));
                $next_section_start = $sectionkey == end($sectionkeys) ? strlen($settingsText) : strpos($settingsText, $sections[$sectionkey + 1][0]);

                $new_settingsText .= "\n" . substr($settingsText, $prev_section_end, $next_section_start - $prev_section_end) . "\n";
            }
            // Put the placeholders in this section into canonical order.
            else
            {
                $section_parts = array_flip($section);
                $pathcode_reached = false;
                foreach ($settings_defs as $var => $setting_def)
                {
                    if ($var === $pathcode_var)
                        $pathcode_reached = true;

                    // Already did this setting, so move on to the next.
                    if (in_array($var, $done_defs))
                        continue;

                    // Stop when we hit a setting definition that will start a later section.
                    if (isset($newsection_placeholders[$var]) && count($section) !== 1)
                        break;

                    // Stop when everything in this section is done, unless it's the last.
                    // This helps maintain the relative position of any custom content.
                    if (empty($section_parts) && $sectionkey < (count($sections) - 1))
                        break;

                    $p = trim($substitutions[$var]['placeholder']);

                    // Can't do anything with an empty placeholder.
                    if ($p === '')
                        continue;

                    // Does this need to be inserted before the path correction code?
                    if (strpos($new_settingsText, trim($substitutions[$pathcode_var]['placeholder'])) !== false && in_array($var, $force_before_pathcode))
                    {
                        $new_settingsText = strtr($new_settingsText, array($substitutions[$pathcode_var]['placeholder'] => $p . "\n" . $substitutions[$pathcode_var]['placeholder']));

                        $bare_settingsText .= "\n" . $substitutions[$var]['placeholder'];
                        $done_defs[] = $var;
                        unset($section_parts[trim($substitutions[$var]['placeholder'])]);
                    }

                    // If it's in this section, add it to the new text now.
                    elseif (in_array($p, $section))
                    {
                        $new_settingsText .= "\n" . $substitutions[$var]['placeholder'];
                        $done_defs[] = $var;
                        unset($section_parts[trim($substitutions[$var]['placeholder'])]);
                    }

                    // Perhaps it is safe to reposition it anyway.
                    elseif (is_string($var) && strpos($new_settingsText, $p) === false && strpos($all_custom_content, '$' . $var) === false)
                    {
                        $new_settingsText .= "\n" . $substitutions[$var]['placeholder'];
                        $done_defs[] = $var;
                        unset($section_parts[trim($substitutions[$var]['placeholder'])]);
                    }

                    // If this setting is missing entirely, fix it.
                    elseif (strpos($bare_settingsText, $p) === false)
                    {
                        // Special case if the path code is missing. Put it near the end,
                        // and also anything else that is missing that normally follows it.
                        if (!isset($newsection_placeholders[$pathcode_var]) && $pathcode_reached === true && $sectionkey < (count($sections) - 1))
                            break;

                        $new_settingsText .= "\n" . $substitutions[$var]['placeholder'];
                        $bare_settingsText .= "\n" . $substitutions[$var]['placeholder'];
                        $done_defs[] = $var;
                        unset($section_parts[trim($substitutions[$var]['placeholder'])]);
                    }
                }
            }
        }
        $settingsText = $new_settingsText;

        // Restore the leading and trailing placeholders as necessary.
        foreach (array(-1, -2) as $var)
        {
            if (!empty($substitutions[$var]['placeholder']) && strpos($settingsText, $substitutions[$var]['placeholder']) === false);
            {
                $settingsText = ($var == -1 ? $substitutions[$var]['placeholder'] : '') . $settingsText . ($var == -2 ? $substitutions[$var]['placeholder'] : '');
            }
        }
    }
    // Even if not rebuilding, there are a few variables that may need to be moved around.
    else
    {
        $pathcode_pos = strpos($settingsText, $substitutions[$pathcode_var]['placeholder']);

        if ($pathcode_pos !== false)
        {
            foreach ($force_before_pathcode as $var)
            {
                if (!empty($substitutions[$var]['placeholder']) && strpos($settingsText, $substitutions[$var]['placeholder']) > $pathcode_pos)
                {
                    $settingsText = strtr($settingsText, array(
                        $substitutions[$var]['placeholder'] => '',
                        $substitutions[$pathcode_var]['placeholder'] => $substitutions[$var]['placeholder'] . "\n" . $substitutions[$pathcode_var]['placeholder'],
                    ));
                }
            }
        }
    }

    /* 3.c: Replace the placeholders with the final values */

    // Where possible, perform simple substitutions.
    $settingsText = strtr($settingsText, $simple_replacements);

    // Deal with any complicated ones.
    if (!empty($replace_patterns))
        $settingsText = preg_replace($replace_patterns, $replace_strings, $settingsText);

    // Make absolutely sure that the path correction code is included.
    if (strpos($settingsText, $substitutions[$pathcode_var]['replacement']) === false)
        $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', "\n" . $substitutions[$pathcode_var]['replacement'] . "\n", $settingsText);

    // If we did not rebuild, do just enough to make sure the thing is viable.
    if (empty($rebuild))
    {
        // We need to refresh $bare_settingsText again, and remove the code blocks from it.
        $bare_settingsText = $settingsText;
        foreach ($substitutions as $var => $substitution)
        {
            if (!is_int($var))
                break;

            if (isset($substitution['replacement']))
                $bare_settingsText = str_replace($substitution['replacement'], '', $bare_settingsText);
        }
        $bare_settingsText = strip_php_comments($bare_settingsText);

        // Now insert any defined settings that are missing.
        $pathcode_reached = false;
        foreach ($settings_defs as $var => $setting_def)
        {
            if ($var === $pathcode_var)
                $pathcode_reached = true;

            if (is_int($var))
                continue;

            // Do nothing if it is already in there.
            if (preg_match($substitutions[$var]['search_pattern'], $bare_settingsText))
                continue;

            // Insert it either before or after the path correction code, whichever is appropriate.
            if (!$pathcode_reached || in_array($var, $force_before_pathcode))
            {
                $settingsText = preg_replace($substitutions[$pathcode_var]['search_pattern'], $substitutions[$var]['replacement'] . "\n$0", $settingsText);
            }
            else
            {
                $settingsText = preg_replace($substitutions[$pathcode_var]['search_pattern'], "$0\n" . $substitutions[$var]['replacement'], $settingsText);
            }
        }
    }

    // If we have any brand new settings to add, do so.
    foreach ($new_settings_vars as $var => $val)
    {
        if (isset($substitutions[$var]) && !preg_match($substitutions[$var]['search_pattern'], $settingsText))
        {
            if (!isset($settings_defs[$var]) && strpos($settingsText, '# Custom Settings #') === false)
                $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', "\n\n######### Custom Settings #########\n", $settingsText);

            $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', $substitutions[$var]['replacement'] . "\n", $settingsText);
        }
    }

    // This is just cosmetic. Get rid of extra lines of whitespace.
    $settingsText = preg_replace('~\n\s*\n~', "\n\n", $settingsText);

    /**************************************
     * PART 4: Check syntax before saving *
     **************************************/

    $temp_sfile = tempnam(sys_get_temp_dir(), md5($prefix . 'Settings.php'));
    file_put_contents($temp_sfile, $settingsText);

    $result = get_current_settings(filemtime($temp_sfile), $temp_sfile);

    unlink($temp_sfile);

    // If the syntax is borked, try rebuilding to see if that fixes it.
    if ($result === false)
        return empty($rebuild) ? updateSettingsFile($config_vars, $keep_quotes, true) : false;

    /******************************************
     * PART 5: Write updated settings to file *
     ******************************************/

    $success = safe_file_write($settingsFile, $settingsText, dirname($settingsFile) . '/Settings_bak.php', $last_settings_change);

    // Remember this in case updateSettingsFile is called twice.
    $mtime = filemtime($settingsFile);

    return $success;
}

/**
 * Retrieves a copy of the current values of all settings defined in Settings.php.
 *
 * Importantly, it does this without affecting our actual global variables at all,
 * and it performs safety checks before acting. The result is an array of the
 * values as recorded in the settings file.
 *
 * @param int $mtime Timestamp of last known good configuration. Defaults to time SMF started.
 * @param string $settingsFile The settings file. Defaults to SMF's standard Settings.php.
 * @return array An array of name/value pairs for all the settings in the file.
 */
function get_current_settings($mtime = null, $settingsFile = null)
{
    $mtime = is_null($mtime) ? (defined('TIME_START') ? TIME_START : $_SERVER['REQUEST_TIME']) : (int) $mtime;

    if (!is_file($settingsFile))
    {
        foreach (get_included_files() as $settingsFile)
            if (basename($settingsFile) === 'Settings.php')
                break;

        if (basename($settingsFile) !== 'Settings.php')
            return false;
    }

    // If the file has been changed since the last known good configuration, bail out.
    clearstatcache();
    if (filemtime($settingsFile) > $mtime)
        return false;

    // Strip out opening and closing PHP tags.
    $settingsText = trim(file_get_contents($settingsFile));
    if (substr($settingsText, 0, 5) == '<' . '?php')
        $settingsText = substr($settingsText, 5);
    if (substr($settingsText, -2) == '?' . '>')
        $settingsText = substr($settingsText, 0, -2);

    // Since we're using eval, we need to manually replace these with strings.
    $settingsText = strtr($settingsText, array(
        '__FILE__' => var_export($settingsFile, true),
        '__DIR__' => var_export(dirname($settingsFile), true),
    ));

    // Prevents warnings about constants that are already defined.
    $settingsText = preg_replace_callback(
        '~\bdefine\s*\(\s*(["\'])(\w+)\1~',
        function ($matches)
        {
            return 'define(\'' . md5(mt_rand()) . '\'';
        },
        $settingsText
    );

    // Handle eval errors gracefully in both PHP 5 and PHP 7
    try
    {
        if($settingsText !== '' && @eval($settingsText) === false)
            throw new ErrorException('eval error');

        unset($mtime, $settingsFile, $settingsText);
        $defined_vars = get_defined_vars();
    }
    catch (Throwable $e) {}
    catch (ErrorException $e) {}
    if (isset($e))
        return false;

    return $defined_vars;
}

/**
 * Writes data to a file, optionally making a backup, while avoiding race conditions.
 *
 * @param string $file The filepath of the file where the data should be written.
 * @param string $data The data to be written to $file.
 * @param string $backup_file The filepath where the backup should be saved. Default null.
 * @param int $mtime If modification time of $file is more recent than this Unix timestamp, the write operation will abort. Defaults to time that the script started execution.
 * @param bool $append If true, the data will be appended instead of overwriting the existing content of the file. Default false.
 * @return bool Whether the write operation succeeded or not.
 */
function safe_file_write($file, $data, $backup_file = null, $mtime = null, $append = false)
{
    global $cachedir;

    // Sanity checks.
    if (!file_exists($file) && !is_dir(dirname($file)))
        return false;

    if (!is_int($mtime))
        $mtime = $_SERVER['REQUEST_TIME'];

    $temp_dir = is_dir(@realpath($cachedir)) ? $cachedir : sys_get_temp_dir();

    // Our temp files.
    $temp_sfile = tempnam($temp_dir, pathinfo($file, PATHINFO_FILENAME) . '.');

    if (!empty($backup_file))
        $temp_bfile = tempnam($temp_dir, pathinfo($backup_file, PATHINFO_FILENAME) . '.');

    // We need write permissions.
    $failed = false;
    foreach (array($file, $backup_file) as $sf)
    {
        if (empty($sf))
            continue;

        if (!file_exists($sf))
            touch($sf);
        elseif (!is_file($sf))
            $failed = true;

        if (!$failed)
            $failed = !smf_chmod($sf);
    }

    // Is there enough free space on the disk?
    if (!$failed && disk_free_space(dirname($file)) < (strlen($data) + filesize($file) + (!empty($backup_file) ? filesize($backup_file) : 0)))
        $failed = true;

    // Now let's see if writing to a temp file succeeds.
    if (!$failed && file_put_contents($temp_sfile, $data, LOCK_EX) !== strlen($data))
        $failed = true;

    // Tests passed, so it's time to do the job.
    if (!$failed)
    {
        // Back up the backup, just in case.
        if (file_exists($backup_file))
            $temp_bfile_saved = @copy($backup_file, $temp_bfile);

        // Make sure no one changed the file while we weren't looking.
        clearstatcache();
        if (filemtime($file) <= $mtime)
        {
            // Attempt to open the file.
            $sfhandle = @fopen($file, 'c');

            // Let's do this thing!
            if ($sfhandle !== false)
            {
                // Immediately get a lock.
                flock($sfhandle, LOCK_EX);

                // Make sure the backup works before we do anything more.
                $temp_sfile_saved = @copy($file, $temp_sfile);

                // Now write our data to the file.
                if ($temp_sfile_saved)
                {
                    if (empty($append))
                    {
                        ftruncate($sfhandle, 0);
                        rewind($sfhandle);
                    }

                    $failed = fwrite($sfhandle, $data) !== strlen($data);
                }
                else
                    $failed = true;

                // If writing failed, put everything back the way it was.
                if ($failed)
                {
                    if (!empty($temp_sfile_saved))
                        @rename($temp_sfile, $file);

                    if (!empty($temp_bfile_saved))
                        @rename($temp_bfile, $backup_file);
                }
                // It worked, so make our temp backup the new permanent backup.
                elseif (!empty($backup_file))
                    @rename($temp_sfile, $backup_file);

                // And we're done.
                flock($sfhandle, LOCK_UN);
                fclose($sfhandle);
            }
        }
    }

    // We're done with these.
    @unlink($temp_sfile);
    @unlink($temp_bfile);

    if ($failed)
        return false;

    // Even though on normal installations the filemtime should invalidate any cached version
    // it seems that there are times it might not. So let's MAKE it dump the cache.
    if (function_exists('opcache_invalidate'))
        opcache_invalidate($file, true);

    return true;
}

/**
 * A wrapper around var_export whose output matches SMF coding conventions.
 *
 * @todo Add special handling for objects?
 *
 * @param mixed $var The variable to export
 * @return mixed A PHP-parseable representation of the variable's value
 */
function smf_var_export($var)
{
    /*
     * Old versions of updateSettingsFile couldn't handle multi-line values.
     * Even though technically we can now, we'll keep arrays on one line for
     * the sake of backwards compatibility.
     */
    if (is_array($var))
    {
        $return = array();

        foreach ($var as $key => $value)
            $return[] = var_export($key, true) . ' => ' . smf_var_export($value);

        return 'array(' . implode(', ', $return) . ')';
    }

    // For the same reason, replace literal returns and newlines with "\r" and "\n"
    elseif (is_string($var) && (strpos($var, "\n") !== false || strpos($var, "\r") !== false))
    {
        return strtr(preg_replace_callback('/[\r\n]+/', function($m) {
            return '\' . "' . strtr($m[0], array("\r" => '\r', "\n" => '\n')) . '" . \'';
        }, $var), array("'' . " => '', " . ''" => ''));
    }

    // We typically use lowercase true/false/null.
    elseif (in_array(gettype($var), array('boolean', 'NULL')))
        return strtolower(var_export($var, true));

    // Nothing special.
    else
        return var_export($var, true);
};

/**
 * Deletes all PHP comments from a string.
 * Useful when analyzing input from file_get_contents, etc.
 *
 * If the code contains any strings in nowdoc or heredoc syntax, they will be
 * converted to single- or double-quote strings.
 *
 * @param string $code_str A string containing PHP code.
 * @param string|null $line_ending One of "\r", "\n", or "\r\n". Leave unset for auto-detect.
 * @return string A string of PHP code with no comments in it.
 */
function strip_php_comments($code_str, $line_ending = null)
{
    // What line ending should we use?
    // Note: this depends on the string, not the host OS, so PHP_EOL isn't what we want.
    if (!in_array($line_ending, array("\r", "\n", "\r\n")))
    {
        if (strpos($code_str, "\r\n") !== false)
            $line_ending = "\r\n";
        elseif (strpos($code_str, "\n") !== false)
            $line_ending = "\n";
        elseif (strpos($code_str, "\r") !== false)
            $line_ending = "\r";
    }

    // Everything is simpler if we convert heredocs to normal strings first.
    if (preg_match_all('/<<<(\'?)(\w+)\'?'. $line_ending . '(.*?)'. $line_ending . '\2;$/m', $code_str, $matches))
    {
        foreach ($matches[0] as $mkey => $heredoc)
        {
            if (!empty($matches[1][$mkey]))
                $heredoc_replacements[$heredoc] = var_export($matches[3][$mkey], true) . ';';
            else
                $heredoc_replacements[$heredoc] = '"' . strtr(substr(var_export($matches[3][$mkey], true), 1, -1), array("\\'" => "'", '"' => '\"')) . '";';
        }

        $code_str = strtr($code_str, $heredoc_replacements);
    }

    // Split before everything that could possibly delimit a comment or a string.
    $parts = preg_split('~(?=#+|/(?=/|\*)|\*/|'. $line_ending . '|(?<!\\\)[\'"])~m', $code_str);

    $in_string = 0;
    $in_comment = 0;
    foreach ($parts as $partkey => $part)
    {
        $one_char = substr($part, 0, 1);
        $two_char = substr($part, 0, 2);
        $to_remove = 0;

        /*
         * Meaning of $in_string values:
         *  0: not in a string
         *  1: in a single quote string
         *  2: in a double quote string
         */
        if ($one_char == "'")
        {
            if (!empty($in_comment))
                $in_string = 0;
            elseif (in_array($in_string, array(0, 1)))
                $in_string = ($in_string ^ 1);
        }
        elseif ($one_char == '"')
        {
            if (!empty($in_comment))
                $in_string = 0;
            elseif (in_array($in_string, array(0, 2)))
                $in_string = ($in_string ^ 2);
        }

        /*
         * Meaning of $in_comment values:
         *  0: not in a comment
         *  1: in a single line comment
         *  2: in a multi-line comment
         */
        elseif ($one_char == '#' || $two_char == '//')
        {
            $in_comment = !empty($in_string) ? 0 : (empty($in_comment) ? 1 : $in_comment);
        }
        elseif (($line_ending === "\r\n" && $two_char === $line_ending) || $one_char == $line_ending)
        {
            if ($in_comment == 1)
            {
                $in_comment = 0;

                // If we've removed everything on this line, take out the line ending, too.
                if ($parts[$partkey - 1] === $line_ending)
                    $to_remove = strlen($line_ending);
            }
        }
        elseif ($two_char == '/*')
        {
            $in_comment = !empty($in_string) ? 0 : (empty($in_comment) ? 2 : $in_comment);
        }
        elseif ($two_char == '*/')
        {
            if ($in_comment == 2)
            {
                $in_comment = 0;

                // Delete the comment closing.
                $to_remove = 2;
            }
        }

        if (empty($in_comment))
            $parts[$partkey] = strlen($part) > $to_remove ? substr($part, $to_remove) : '';
        else
            $parts[$partkey] = '';
    }

    return implode('', $parts);
}

/**
 * Saves the time of the last db error for the error log
 * - Done separately from updateSettingsFile to avoid race conditions
 *   which can occur during a db error
 * - If it fails Settings.php will assume 0
 *
 * @param int $time The timestamp of the last DB error
 */
function updateDbLastError($time)
{
    global $boarddir, $cachedir;

    // Write out the db_last_error file with the error timestamp
    if (!empty($cachedir) && is_writable($cachedir))
        $errorfile = $cachedir . '/db_last_error.php';

    elseif (file_exists(dirname(__DIR__) . '/cache'))
        $errorfile = dirname(__DIR__) . '/cache/db_last_error.php';

    else
        $errorfile = dirname(__DIR__) . '/db_last_error.php';

    file_put_contents($errorfile, '<' . '?' . "php\n" . '$db_last_error = ' . $time . ';' . "\n" . '?' . '>', LOCK_EX);

    @touch($boarddir . '/' . 'Settings.php');
}

/**
 * Saves the admin's current preferences to the database.
 */
function updateAdminPreferences()
{
    global $options, $context, $smcFunc, $settings, $user_info;

    // This must exist!
    if (!isset($context['admin_preferences']))
        return false;

    // This is what we'll be saving.
    $options['admin_preferences'] = $smcFunc['json_encode']($context['admin_preferences']);

    // Just check we haven't ended up with something theme exclusive somehow.
    $smcFunc['db_query']('', '
        DELETE FROM {db_prefix}themes
        WHERE id_theme != {int:default_theme}
            AND variable = {string:admin_preferences}',
        array(
            'default_theme' => 1,
            'admin_preferences' => 'admin_preferences',
        )
    );

    // Update the themes table.
    $smcFunc['db_insert']('replace',
        '{db_prefix}themes',
        array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
        array($user_info['id'], 1, 'admin_preferences', $options['admin_preferences']),
        array('id_member', 'id_theme', 'variable')
    );

    // Make sure we invalidate any cache.
    cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 0);
}

/**
 * Send all the administrators a lovely email.
 * - loads all users who are admins or have the admin forum permission.
 * - uses the email template and replacements passed in the parameters.
 * - sends them an email.
 *
 * @param string $template Which email template to use
 * @param array $replacements An array of items to replace the variables in the template
 * @param array $additional_recipients An array of arrays of info for additional recipients. Should have 'id', 'email' and 'name' for each.
 */
function emailAdmins($template, $replacements = array(), $additional_recipients = array())
{
    global $smcFunc, $sourcedir, $language, $modSettings;

    // We certainly want this.
    require_once($sourcedir . '/Subs-Post.php');

    // Load all members which are effectively admins.
    require_once($sourcedir . '/Subs-Members.php');
    $members = membersAllowedTo('admin_forum');

    // Load their alert preferences
    require_once($sourcedir . '/Subs-Notify.php');
    $prefs = getNotifyPrefs($members, 'announcements', true);

    $request = $smcFunc['db_query']('', '
        SELECT id_member, member_name, real_name, lngfile, email_address
        FROM {db_prefix}members
        WHERE id_member IN({array_int:members})',
        array(
            'members' => $members,
        )
    );
    $emails_sent = array();
    while ($row = $smcFunc['db_fetch_assoc']($request))
    {
        if (empty($prefs[$row['id_member']]['announcements']))
            continue;

        // Stick their particulars in the replacement data.
        $replacements['IDMEMBER'] = $row['id_member'];
        $replacements['REALNAME'] = $row['member_name'];
        $replacements['USERNAME'] = $row['real_name'];

        // Load the data from the template.
        $emaildata = loadEmailTemplate($template, $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);

        // Then send the actual email.
        sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1);

        // Track who we emailed so we don't do it twice.
        $emails_sent[] = $row['email_address'];
    }
    $smcFunc['db_free_result']($request);

    // Any additional users we must email this to?
    if (!empty($additional_recipients))
        foreach ($additional_recipients as $recipient)
        {
            if (in_array($recipient['email'], $emails_sent))
                continue;

            $replacements['IDMEMBER'] = $recipient['id'];
            $replacements['REALNAME'] = $recipient['name'];
            $replacements['USERNAME'] = $recipient['name'];

            // Load the template again.
            $emaildata = loadEmailTemplate($template, $replacements, empty($recipient['lang']) || empty($modSettings['userLanguage']) ? $language : $recipient['lang']);

            // Send off the email.
            sendmail($recipient['email'], $emaildata['subject'], $emaildata['body'], null, $template, $emaildata['is_html'], 1);
        }
}

?>
API documentation generated by ApiGen