Showing preview only (3,169K chars total). Download the full file or copy to clipboard to get everything.
Repository: tigase/siskin-im
Branch: master
Commit: d77818f97597
Files: 348
Total size: 2.9 MB
Directory structure:
gitextract_t2pcpgpq/
├── .bartycrouch.toml
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ ├── bug_report_developer.md
│ ├── bug_report_user.md
│ ├── feature_request.md
│ └── question.md
├── .gitignore
├── COPYING
├── Documentation/
│ ├── css/
│ │ └── docbook-xsl.css
│ ├── index.asciidoc
│ ├── restructured/
│ │ ├── .readthedocs.yaml
│ │ ├── Advanced_Options.rst
│ │ ├── Makefile
│ │ ├── Tigase_Messenger_iOS.rst
│ │ ├── Welcome.rst
│ │ ├── conf.py
│ │ ├── index.rst
│ │ ├── locale/
│ │ │ ├── pl/
│ │ │ │ └── LC_MESSAGES/
│ │ │ │ └── siskin_im_translation.po
│ │ │ └── zh_CN/
│ │ │ └── LC_MESSAGES/
│ │ │ └── siskin_im_translation.po
│ │ └── make.bat
│ └── text/
│ ├── advanced.asciidoc
│ ├── interface.asciidoc
│ └── welcome.asciidoc
├── NotificationService/
│ ├── Info.plist
│ ├── NotificationService.entitlements
│ └── NotificationService.swift
├── README.md
├── Shared/
│ ├── Info.plist
│ ├── NotificationCategory.swift
│ ├── Shared.h
│ ├── database/
│ │ ├── ConversationType.swift
│ │ └── Database.swift
│ ├── notifications/
│ │ ├── ConversationNotifications.swift
│ │ ├── NotificationEncryptionKeys.swift
│ │ └── NotificationsManagerHelper.swift
│ ├── ui/
│ │ └── UIImage.swift
│ └── util/
│ ├── HTTPFileUploadHelper.swift
│ ├── ImageQuality.swift
│ ├── MediaHelper.swift
│ ├── VideoQuality.swift
│ └── crypto/
│ ├── Cipher+AES.swift
│ ├── SSLCertificate.swift
│ ├── SSLContext.swift
│ └── SSLProcessor.swift
├── SiskinIM/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ ├── AppIcon-Simple.appiconset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── appLogo.imageset/
│ │ │ └── Contents.json
│ │ ├── appearance/
│ │ │ ├── Contents.json
│ │ │ ├── chatMessageText.colorset/
│ │ │ │ └── Contents.json
│ │ │ ├── chatslistBackground.colorset/
│ │ │ │ └── Contents.json
│ │ │ ├── chatslistItemSecondaryLabel.colorset/
│ │ │ │ └── Contents.json
│ │ │ ├── chatslistSemiBackground.colorset/
│ │ │ │ └── Contents.json
│ │ │ └── tintColor.colorset/
│ │ │ └── Contents.json
│ │ ├── audioCall.imageset/
│ │ │ └── Contents.json
│ │ ├── defaultAvatar.imageset/
│ │ │ └── Contents.json
│ │ ├── defaultGroupchatAvatar.imageset/
│ │ │ └── Contents.json
│ │ ├── endCall.imageset/
│ │ │ └── Contents.json
│ │ ├── first.imageset/
│ │ │ └── Contents.json
│ │ ├── message.fill.imageset/
│ │ │ └── Contents.json
│ │ ├── messageArchiving.imageset/
│ │ │ └── Contents.json
│ │ ├── mute.imageset/
│ │ │ └── Contents.json
│ │ ├── participants.imageset/
│ │ │ └── Contents.json
│ │ ├── person.crop.circle.fill.imageset/
│ │ │ └── Contents.json
│ │ ├── pushNotifications.imageset/
│ │ │ └── Contents.json
│ │ ├── qrCodeBackground.colorset/
│ │ │ └── Contents.json
│ │ ├── qrCodeForeground.colorset/
│ │ │ └── Contents.json
│ │ ├── second.imageset/
│ │ │ └── Contents.json
│ │ ├── switchCamera.imageset/
│ │ │ └── Contents.json
│ │ ├── tigaseLogo.imageset/
│ │ │ └── Contents.json
│ │ └── videoCall.imageset/
│ │ └── Contents.json
│ ├── Info.plist
│ ├── SiskinIM-Bridging-Header.h
│ ├── bookmarks/
│ │ ├── BookmarkItem.swift
│ │ ├── BookmarkViewCell.swift
│ │ └── BookmarksController.swift
│ ├── channel/
│ │ ├── ChannelBlockedUsersController.swift
│ │ ├── ChannelCreateViewController.swift
│ │ ├── ChannelEditInfoController.swift
│ │ ├── ChannelInviteController.swift
│ │ ├── ChannelJoinViewController.swift
│ │ ├── ChannelParticipantsController.swift
│ │ ├── ChannelSelectAccountAndComponentController.swift
│ │ ├── ChannelSelectNewOwnerViewController.swift
│ │ ├── ChannelSelectToJoinViewController.swift
│ │ ├── ChannelSettingsViewController.swift
│ │ ├── ChannelViewController.swift
│ │ └── ChannelsHelper.swift
│ ├── chat/
│ │ ├── BaseChatViewController+Share.swift
│ │ ├── BaseChatViewController+ShareFile.swift
│ │ ├── BaseChatViewController+ShareMedia.swift
│ │ ├── BaseChatViewController.swift
│ │ ├── BaseChatViewControllerWithDataSource.swift
│ │ ├── BaseChatViewControllerWithDataSourceContextMenuAndToolbar.swift
│ │ ├── ChatAttachementsCellView.swift
│ │ ├── ChatAttachementsController.swift
│ │ ├── ChatViewController.swift
│ │ ├── ChatViewInputBar.swift
│ │ ├── ShareLocationController.swift
│ │ └── ShareLocationSearchResultsController.swift
│ ├── chats/
│ │ ├── ChatsListTableViewCell.swift
│ │ └── ChatsListViewController.swift
│ ├── contacts/
│ │ ├── ContactBasicTableViewCell.swift
│ │ ├── ContactFormTableViewCell.swift
│ │ ├── ContactViewController.swift
│ │ └── OMEMOIdentityTableViewCell.swift
│ ├── conversation/
│ │ ├── AttachmentChatTableViewCell.swift
│ │ ├── BaseChatTableViewCell.swift
│ │ ├── ChatTableViewCell.swift
│ │ ├── ChatTableViewMarkerCell.swift
│ │ ├── ChatTableViewSystemCell.swift
│ │ ├── ConversationDataSource.swift
│ │ ├── ConversationLogController.swift
│ │ ├── InvitationChatTableViewCell.swift
│ │ ├── LinkPreviewChatTableViewCell.swift
│ │ └── LocationChatTableViewCell.swift
│ ├── database/
│ │ ├── DBCapabilitiesCache.swift
│ │ ├── DBChatHistoryStore.swift
│ │ ├── DBChatHistorySyncStore.swift
│ │ ├── DBChatMarkersStore.swift
│ │ ├── DBChatStore+ChannelStore.swift
│ │ ├── DBChatStore+ChatStore.swift
│ │ ├── DBChatStore+RoomStore.swift
│ │ ├── DBChatStore.swift
│ │ ├── DBOMEMOStore.swift
│ │ ├── DBRosterStore.swift
│ │ ├── DBVCardStore.swift
│ │ ├── Database.swift
│ │ ├── DatabaseMigrator.swift
│ │ ├── MessageState.swift
│ │ └── model/
│ │ ├── DisplayableIdProtocol.swift
│ │ ├── conversations/
│ │ │ ├── AccountConversations.swift
│ │ │ ├── Channel.swift
│ │ │ ├── Chat.swift
│ │ │ ├── Conversation.swift
│ │ │ ├── ConversationBase.swift
│ │ │ └── Room.swift
│ │ └── history/
│ │ ├── AppendixProtocol.swift
│ │ ├── ConversationAttachment.swift
│ │ ├── ConversationEntry.swift
│ │ ├── ConversationEntryEncryption.swift
│ │ ├── ConversationEntryRecipient.swift
│ │ ├── ConversationEntrySender.swift
│ │ ├── ConversationEntryState.swift
│ │ ├── ConversationInvitation.swift
│ │ └── ConversationKey.swift
│ ├── db-schema-1.sql
│ ├── db-schema-10.sql
│ ├── db-schema-11.sql
│ ├── db-schema-12.sql
│ ├── db-schema-13.sql
│ ├── db-schema-14.sql
│ ├── db-schema-2.sql
│ ├── db-schema-3.sql
│ ├── db-schema-4.sql
│ ├── db-schema-5.sql
│ ├── db-schema-6.sql
│ ├── db-schema-7.sql
│ ├── db-schema-8.sql
│ ├── db-schema-9.sql
│ ├── groupchat/
│ │ ├── InviteViewController.swift
│ │ ├── MucChatOccupantsTableViewCell.swift
│ │ ├── MucChatOccupantsTableViewController.swift
│ │ ├── MucChatSettingsViewController.swift
│ │ └── MucChatViewController.swift
│ ├── localization/
│ │ ├── Base.lproj/
│ │ │ ├── Account.storyboard
│ │ │ ├── Conversation.storyboard
│ │ │ ├── Groupchat.storyboard
│ │ │ ├── Info.storyboard
│ │ │ ├── LaunchScreen.storyboard
│ │ │ ├── MIX.storyboard
│ │ │ ├── Main.storyboard
│ │ │ ├── Settings.storyboard
│ │ │ └── VoIP.storyboard
│ │ ├── de.lproj/
│ │ │ ├── Account.strings
│ │ │ ├── Conversation.strings
│ │ │ ├── Groupchat.strings
│ │ │ ├── Info.strings
│ │ │ ├── LaunchScreen.strings
│ │ │ ├── Localizable.strings
│ │ │ ├── MIX.strings
│ │ │ ├── Main.strings
│ │ │ ├── Settings.strings
│ │ │ └── VoIP.strings
│ │ ├── en.lproj/
│ │ │ ├── Account.strings
│ │ │ ├── Conversation.strings
│ │ │ ├── Groupchat.strings
│ │ │ ├── Info.strings
│ │ │ ├── Localizable.strings
│ │ │ ├── MIX.strings
│ │ │ ├── Main.strings
│ │ │ ├── Settings.strings
│ │ │ └── VoIP.strings
│ │ ├── es.lproj/
│ │ │ ├── Account.strings
│ │ │ ├── Conversation.strings
│ │ │ ├── Groupchat.strings
│ │ │ ├── Info.strings
│ │ │ ├── LaunchScreen.strings
│ │ │ ├── Localizable.strings
│ │ │ ├── MIX.strings
│ │ │ ├── Main.strings
│ │ │ ├── Settings.strings
│ │ │ └── VoIP.strings
│ │ └── pl.lproj/
│ │ ├── Account.strings
│ │ ├── Conversation.strings
│ │ ├── Groupchat.strings
│ │ ├── Info.strings
│ │ ├── LaunchScreen.strings
│ │ ├── Localizable.strings
│ │ ├── MIX.strings
│ │ ├── Main.strings
│ │ ├── Settings.strings
│ │ └── VoIP.strings
│ ├── notifications/
│ │ ├── NotificationCenterDelegate.swift
│ │ └── NotificationManager.swift
│ ├── roster/
│ │ ├── AbstractRosterViewController.swift
│ │ ├── RosterItemEditViewController.swift
│ │ ├── RosterItemTableViewCell.swift
│ │ ├── RosterProvider.swift
│ │ ├── RosterProviderFlat.swift
│ │ ├── RosterProviderGrouped.swift
│ │ └── RosterViewController.swift
│ ├── service/
│ │ ├── AvatarEventHandler.swift
│ │ ├── BlockedEventHandler.swift
│ │ ├── DNSSrvDiskCache.swift
│ │ ├── MeetEventHandler.swift
│ │ ├── MessageEventHandler.swift
│ │ ├── MixEventHandler.swift
│ │ ├── MucEventHandler.swift
│ │ ├── NewFeaturesDetector.swift
│ │ ├── PresenceRosterEventHandler.swift
│ │ ├── PushEventHandler.swift
│ │ ├── StreamFeaturesCache.swift
│ │ ├── XMPPClient_extension.swift
│ │ ├── XmppService.swift
│ │ └── XmppServiceEventHandler.swift
│ ├── settings/
│ │ ├── AccountConnectivitySettingsViewController.swift
│ │ ├── AccountDomainTableViewCell.swift
│ │ ├── AccountQRCodeController.swift
│ │ ├── AccountSettingsViewController.swift
│ │ ├── AccountTableViewCell.swift
│ │ ├── AddAccountController.swift
│ │ ├── BlockedContactsController.swift
│ │ ├── ChatSettingsViewController.swift
│ │ ├── ContactsSettingsViewController.swift
│ │ ├── DeviceMemoryUsageTableViewCell.swift
│ │ ├── ExperimentalSettingsViewController.swift
│ │ ├── MediaSettingsVIewController.swift
│ │ ├── NotificationSettingsViewController.swift
│ │ ├── OMEMOFingerprintsController.swift
│ │ ├── RegisterAccountController.swift
│ │ ├── ServerFeaturesViewController.swift
│ │ ├── ServerSelectorTableViewCell.swift
│ │ ├── SetAccountSettingsController.swift
│ │ ├── SettingsViewController.swift
│ │ ├── SetupViewController.swift
│ │ └── server_features_list.xml
│ ├── ui/
│ │ ├── AboutController.swift
│ │ ├── AvatarStatusView.swift
│ │ ├── AvatarView.swift
│ │ ├── CertificateErrorAlert.swift
│ │ ├── ChartView.swift
│ │ ├── ChatBottomView.swift
│ │ ├── CustomTabBarController.swift
│ │ ├── DataFormController.swift
│ │ ├── EmptyViewController.swift
│ │ ├── EnumTableViewCell.swift
│ │ ├── GetInTouchViewController.swift
│ │ ├── GlobalSplitViewController.swift
│ │ ├── MainTabBarController.swift
│ │ ├── Markdown.swift
│ │ ├── MessageTextView.swift
│ │ ├── NavigationControllerWrappingSegue.swift
│ │ ├── RoundButton.swift
│ │ ├── StepperTableViewCell.swift
│ │ ├── SwitchTableViewCell.swift
│ │ ├── TablePicketViewController.swift
│ │ └── suggestions/
│ │ └── MultiContactSelectionView.swift
│ ├── util/
│ │ ├── AccountManager.swift
│ │ ├── AccountManagerScramSaltedPasswordCache.swift
│ │ ├── AppStoryboard.swift
│ │ ├── Array+IndexChanges.swift
│ │ ├── AudioSession.swift
│ │ ├── AvatarManager.swift
│ │ ├── AvatarStore.swift
│ │ ├── ContactManager.swift
│ │ ├── CurrentDatePublisher.swift
│ │ ├── DownloadManager.swift
│ │ ├── DownloadStore.swift
│ │ ├── InvitationsManager.swift
│ │ ├── MainNotificationManagerProvider.swift
│ │ ├── MediaHelper.swift
│ │ ├── MessageEncryption.swift
│ │ ├── MetadataCache.swift
│ │ ├── OSLog.swift
│ │ ├── OpenSSL_AES_GCM_Engine.swift
│ │ ├── PresenceStore.swift
│ │ ├── ServerCertificateInfo.swift
│ │ ├── Settings.swift
│ │ ├── SiskinPushNotificationsModuleProvider.swift
│ │ ├── TasksQueue.swift
│ │ ├── UIColor_mix.swift
│ │ ├── VCardManager.swift
│ │ └── combine/
│ │ ├── Publisher+OnlyGetter.swift
│ │ ├── Publisher+ThrottleFixed.swift
│ │ └── Publisher+ThrottledSink.swift
│ ├── vcard/
│ │ ├── VCardAvatarEditCell.swift
│ │ ├── VCardEditAddressTableViewCell.swift
│ │ ├── VCardEditEmailTableViewCell.swift
│ │ ├── VCardEditPhoneTableViewCell.swift
│ │ ├── VCardEditViewController.swift
│ │ ├── VCardEntryTypeAwareTableViewCell.swift
│ │ └── VCardTextEditCell.swift
│ ├── voip/
│ │ ├── CallManager.swift
│ │ ├── CameraPreviewView.swift
│ │ ├── CreateMeetingViewController.swift
│ │ ├── ExternalServiceDiscovery_Service_extension.swift
│ │ ├── InviteToMeetingViewController.swift
│ │ ├── JingleManager.swift
│ │ ├── JingleManager_Session.swift
│ │ ├── MeetController.swift
│ │ ├── MeetManager.swift
│ │ ├── RTCCameraVideoCapturer_Format.swift
│ │ └── VideoCallController.swift
│ └── xmpp/
│ ├── HttpFileUploadModule.swift
│ └── SiskinPushNotificationsModule.swift
├── SiskinIM - Share/
│ ├── Assets.xcassets/
│ │ ├── AppIcon-Simple.appiconset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Info.plist
│ ├── ShareViewController.swift
│ ├── SiskinIM - Share.entitlements
│ └── localization/
│ ├── Base.lproj/
│ │ └── MainInterface.storyboard
│ ├── de.lproj/
│ │ └── MainInterface.strings
│ ├── en.lproj/
│ │ └── MainInterface.strings
│ ├── es.lproj/
│ │ └── MainInterface.strings
│ └── pl.lproj/
│ └── MainInterface.strings
├── SiskinIM.entitlements
├── SiskinIM.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata/
│ └── xcschemes/
│ └── NotificationService.xcscheme
├── pom.xml
├── siskin-im.doap
├── swiftScript.swift
├── trim.sh
└── update-frameworks.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .bartycrouch.toml
================================================
[update]
tasks = ["interfaces", "code", "normalize"]
[update.interfaces]
paths = ["."]
defaultToBase = true
ignoreEmptyStrings = true
unstripped = false
[update.code]
codePaths = ["."]
localizablePaths = ["SiskinIM/localization"]
defaultToKeys = true
additive = true
unstripped = false
plistArguments = true
[update.normalize]
paths = ["."]
sourceLocale = "en"
harmonizeWithSource = true
sortByKeys = true
[lint]
paths = ["."]
duplicateKeys = true
emptyValues = true
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [tigase]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report_developer.md
================================================
---
name: Bug report (Developer)
about: Reports from app developers
title: ''
labels: 'bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Open '....'
3. Do '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Details (please complete the following information):**
- Siskin Version: [e.g. 6.0.0]
- Branch: [e.g. master]
- Xcode version: [e.g. 11]
- iOS version [e.g. 11.0]
- iPhone model [e.g. iPhone 11]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report_user.md
================================================
---
name: Bug report (App user)
about: Reports for app download from App Store
title: ''
labels: 'bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Open '....'
3. Click on '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Details (please complete the following information):**
- Siskin Version: [e.g. 6.0.0]
- iOS version [e.g. 11.0]
- iPhone model [e.g. iPhone 11]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea to improve SiskinIM
title: ''
labels: 'enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: General question
about: Just a question
title: ''
labels: 'question'
assignees: ''
---
**I have a problem with…**
A clear and concise description of what the problem is.
**Details (please complete the following information):**
- Tigase version: [e.g. 8.1.0]
- JVM flavour and version [e.g. AdoptOpenJDK11]
- Operating system/distribution/version [e.g. Linux Ubuntu 20.04]
================================================
FILE: .gitignore
================================================
docs
Frameworks
#########################
# **.gitignore** file for Xcode4 / OS X Source projects
#
# NB: if you are storing "built" products, this WILL NOT WORK,
# and you should use a different **.gitignore** (or none at all)
# This file is for SOURCE projects, where there are many extra
# files that we want to exclude
#
# For updates, see: http://stackoverflow.com/questions/49478/git-ignore-file-for-xcode-projects
#########################
#####
# OS X temporary files that should never be committed
.DS_Store
*.swp
profile
####
# Xcode temporary files that should never be committed
#
# NB: NIB/XIB files still exist even on Storyboard projects, so we want this...
*~.nib
####
# Xcode build files -
#
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "DerivedData"
DerivedData/
# NB: slash on the end, so we only remove the FOLDER, not any files that were badly named "build"
build/
#####
# Xcode private settings (window sizes, bookmarks, breakpoints, custom executables, smart groups)
#
# This is complicated:
#
# SOMETIMES you need to put this file in version control.
# Apple designed it poorly - if you use "custom executables", they are
# saved in this file.
# 99% of projects do NOT use those, so they do NOT want to version control this file.
# ..but if you're in the 1%, comment out the line "*.pbxuser"
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
# NB: also, whitelist the default ones, some projects need to use these
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
####
# Xcode 4 - semi-personal settings, often included in workspaces
#
# You can safely ignore the xcuserdata files - but do NOT ignore the files next to them
#
xcuserdata
####
# XCode 4 workspaces - more detailed
#
# Workspaces are important! They are a core feature of Xcode - don't exclude them :)
#
# Workspace layout is quite spammy. For reference:
#
# (root)/
# (project-name).xcodeproj/
# project.pbxproj
# project.xcworkspace/
# contents.xcworkspacedata
# xcuserdata/
# (your name)/xcuserdatad/
# xcuserdata/
# (your name)/xcuserdatad/
#
#
#
# Xcode 4 workspaces - SHARED
#
# This is UNDOCUMENTED (google: "developer.apple.com xcshareddata" - 0 results
# But if you're going to kill personal workspaces, at least keep the shared ones...
#
#
!xcshareddata
####
# XCode 4 build-schemes
#
# PRIVATE ones are stored inside xcuserdata
!xcschemes
####
# Xcode 4 - Deprecated classes
#
# Allegedly, if you manually "deprecate" your classes, they get moved here.
#
# We're using source-control, so this is a "feature" that we do not want!
*.moved-aside
Documentation/restructured/locale/*/LC_MESSAGES/*.mo
Documentation/restructured/_build/
================================================
FILE: COPYING
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: Documentation/css/docbook-xsl.css
================================================
/*
CSS stylesheet for XHTML produced by DocBook XSL stylesheets.
*/
body {
font-family: Georgia,serif;
}
code, pre {
font-family: "Courier New", Courier, monospace;
}
span.strong {
font-weight: bold;
}
body blockquote {
margin-top: .75em;
line-height: 1.5;
margin-bottom: .75em;
}
html body {
margin: 1em 5% 1em 5%;
line-height: 1.2;
}
body div {
margin: 0;
}
h1, h2, h3, h4, h5, h6
{
color: #527bbd;
font-family: Arial,Helvetica,sans-serif;
}
div.toc p:first-child,
div.list-of-figures p:first-child,
div.list-of-tables p:first-child,
div.list-of-examples p:first-child,
div.example p.title,
div.sidebar p.title
{
font-weight: bold;
color: #527bbd;
font-family: Arial,Helvetica,sans-serif;
margin-bottom: 0.2em;
}
body h1 {
margin: .0em 0 0 -4%;
line-height: 1.3;
border-bottom: 2px solid silver;
}
body h2 {
margin: 0.5em 0 0 -4%;
line-height: 1.3;
border-bottom: 2px solid silver;
}
body h3 {
margin: .8em 0 0 -3%;
line-height: 1.3;
}
body h4 {
margin: .8em 0 0 -3%;
line-height: 1.3;
}
body h5 {
margin: .8em 0 0 -2%;
line-height: 1.3;
}
body h6 {
margin: .8em 0 0 -1%;
line-height: 1.3;
}
body hr {
border: none; /* Broken on IE6 */
}
div.footnotes hr {
border: 1px solid silver;
}
div.navheader th, div.navheader td, div.navfooter td {
font-family: Arial,Helvetica,sans-serif;
font-size: 0.9em;
font-weight: bold;
color: #527bbd;
}
div.navheader img, div.navfooter img {
border-style: none;
}
div.navheader a, div.navfooter a {
font-weight: normal;
}
div.navfooter hr {
border: 1px solid silver;
}
body td {
line-height: 1.2
}
body th {
line-height: 1.2;
}
ol {
line-height: 1.2;
}
ul, body dir, body menu {
line-height: 1.2;
}
html {
margin: 0;
padding: 0;
}
body h1, body h2, body h3, body h4, body h5, body h6 {
margin-left: 0
}
body pre {
margin: 0.5em 10% 0.5em 1em;
line-height: 1.0;
color: navy;
}
tt.literal, code.literal {
color: navy;
}
.programlisting, .screen {
border: 1px solid silver;
background: #f4f4f4;
margin: 0.5em 10% 0.5em 0;
padding: 0.5em 1em;
}
div.sidebar {
background: #ffffee;
margin: 1.0em 10% 0.5em 0;
padding: 0.5em 1em;
border: 1px solid silver;
}
div.sidebar * { padding: 0; }
div.sidebar div { margin: 0; }
div.sidebar p.title {
margin-top: 0.5em;
margin-bottom: 0.2em;
}
div.bibliomixed {
margin: 0.5em 5% 0.5em 1em;
}
div.glossary dt {
font-weight: bold;
}
div.glossary dd p {
margin-top: 0.2em;
}
dl {
margin: .8em 0;
line-height: 1.2;
}
dt {
margin-top: 0.5em;
}
dt span.term {
font-style: normal;
color: navy;
}
div.variablelist dd p {
margin-top: 0;
}
div.itemizedlist li, div.orderedlist li {
margin-left: -0.8em;
margin-top: 0.5em;
}
ul, ol {
list-style-position: outside;
}
div.sidebar ul, div.sidebar ol {
margin-left: 2.8em;
}
div.itemizedlist p.title,
div.orderedlist p.title,
div.variablelist p.title
{
margin-bottom: -0.8em;
}
div.revhistory table {
border-collapse: collapse;
border: none;
}
div.revhistory th {
border: none;
color: #527bbd;
font-family: Arial,Helvetica,sans-serif;
}
div.revhistory td {
border: 1px solid silver;
}
/* Keep TOC and index lines close together. */
div.toc dl, div.toc dt,
div.list-of-figures dl, div.list-of-figures dt,
div.list-of-tables dl, div.list-of-tables dt,
div.indexdiv dl, div.indexdiv dt
{
line-height: normal;
margin-top: 0;
margin-bottom: 0;
}
/*
Table styling does not work because of overriding attributes in
generated HTML.
*/
div.table table,
div.informaltable table
{
margin-left: 0;
margin-right: 5%;
margin-bottom: 0.8em;
}
div.informaltable table
{
margin-top: 0.4em
}
div.table thead,
div.table tfoot,
div.table tbody,
div.informaltable thead,
div.informaltable tfoot,
div.informaltable tbody
{
/* No effect in IE6. */
border-top: 3px solid #527bbd;
border-bottom: 3px solid #527bbd;
}
div.table thead, div.table tfoot,
div.informaltable thead, div.informaltable tfoot
{
font-weight: bold;
}
div.mediaobject img {
margin-bottom: 0.8em;
}
div.figure p.title,
div.table p.title
{
margin-top: 1em;
margin-bottom: 0.4em;
}
div.calloutlist p
{
margin-top: 0em;
margin-bottom: 0.4em;
}
a img {
border-style: none;
}
@media print {
div.navheader, div.navfooter { display: none; }
}
span.aqua { color: aqua; }
span.black { color: black; }
span.blue { color: blue; }
span.fuchsia { color: fuchsia; }
span.gray { color: gray; }
span.green { color: green; }
span.lime { color: lime; }
span.maroon { color: maroon; }
span.navy { color: navy; }
span.olive { color: olive; }
span.purple { color: purple; }
span.red { color: red; }
span.silver { color: silver; }
span.teal { color: teal; }
span.white { color: white; }
span.yellow { color: yellow; }
span.aqua-background { background: aqua; }
span.black-background { background: black; }
span.blue-background { background: blue; }
span.fuchsia-background { background: fuchsia; }
span.gray-background { background: gray; }
span.green-background { background: green; }
span.lime-background { background: lime; }
span.maroon-background { background: maroon; }
span.navy-background { background: navy; }
span.olive-background { background: olive; }
span.purple-background { background: purple; }
span.red-background { background: red; }
span.silver-background { background: silver; }
span.teal-background { background: teal; }
span.white-background { background: white; }
span.yellow-background { background: yellow; }
span.big { font-size: 2em; }
span.small { font-size: 0.6em; }
span.underline { text-decoration: underline; }
span.overline { text-decoration: overline; }
span.line-through { text-decoration: line-through; }
================================================
FILE: Documentation/index.asciidoc
================================================
= Tigase Messenger for iOS
Tigase Team <team@tigase.net>
:toc:
:numbered:
:website: http://tigase.net
:Date: 2017-04-10
Welcome to Tigase Messenger for iOS
:leveloffset: 1
include::text/welcome.asciidoc[]
include::text/interface.asciidoc[]
include::text/advanced.asciidoc[]
================================================
FILE: Documentation/restructured/.readthedocs.yaml
================================================
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: conf.py
================================================
FILE: Documentation/restructured/Advanced_Options.rst
================================================
Advanced Options
=================
This section contains information about advanced settings and options that are available to the application, but may not be typically considered for users.
- | activation of end-to-end-encryption
- | requesting delivery receipts
- | activation of auto-authorization of contacts
- | optimization of settings for file exchange (files and pictures)
- | activation of group chat synchronization
Chats
-------
First, you visit the settings menu by selecting Chats. Then you need to tap the upper left. In this submenu you can activate the end-to-end-encryption (called OMEMO). Afterwards, you go back to the settings menu by clicking Settings.
|images/setting01| |images/setting02| |images/setting03|
Contacts
---------
In the settings menu you have to select Contacts. You activate the switch Auto-authorize contacts and you could return to the settings menu by clicking Settings.
|images/setting04| |images/setting05|
Media
------
In the settings menu you have to select Media.In this submenu you can optimize the settings for file exchange. You should select **File sharing via HTTP** and set the **File download limit** to 4 MB. Now you can send files and upload them to your XMPP server (instead of sending them directly to your communication partner). Files which you receive will be downloaded automatically if their size is below 4 MB.
|images/setting07| |images/setting06|
Experimental
------------
In the settings menu you have to select select Experimental. Here you can activate the Groupchat bookmark sync to be able to see your group chats on multiple devices. Additionally, I recommend to deactive the usage of public STUN servers. Most todays XMPP servers already provide a STUN-service.
|images/setting08|
After this step you can go back to the settings menu and close it. The optimization of settings is done.
.. |images/setting01| image:: images/setting01.png
.. |images/setting02| image:: images/setting02.png
.. |images/setting03| image:: images/setting03.png
.. |images/setting04| image:: images/setting04.png
.. |images/setting05| image:: images/setting05.png
.. |images/setting06| image:: images/setting06.png
.. |images/setting07| image:: images/setting07.png
.. |images/setting08| image:: images/setting08.png
================================================
FILE: Documentation/restructured/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: Documentation/restructured/Tigase_Messenger_iOS.rst
================================================
Tigase Messenger for iOS Interface
======================================
The menu interface for Tigase Messenger for iOS is broken up into three main panels; Chats, Contacts and Bookmarks.
Contacts
---------
The contacts panel serves as your Roster, displaying all the contacts you have on your roster, and displaying statuses along with their names. Tigase Messenger for iOS supports vCard-Temp Avatars and will retrieve them if they are uploaded by a user.
Contacts with green icons are available or free to chat status.
Contacts with yellow icons are away or extended away.
Contacts with red icons are in do not disturb status.
Contacts with gray icons are offline or unavailable.
Note that contacts will remain gray if you decide not to allow presence notifications in the settings.
You may delete or edit contacts by tapping a contact and tapping Delete. You also have the ability to edit a contact, explained in the next section. Deleting the contact will remove them from your roster, and remove any presence sharing permissions from the contact.
Adding a contact
^^^^^^^^^^^^^^^^^
To add a contact, you have to click on Contacts and select the +-sign at the top of the screen. Select the account friends list you wish the new contact to be added too. Then type in the JID of the user, do not use resources, just bare JID. You may enter a friendly nickname for the contact to be added to your friend list.In this tutorial *my friend* is selected as the name for the contact. When adding users, you have two options to select:
|images/siskin03| |images/join01|
|images/join02|
- Disclose my online status - This will allow sending of presence status and changes to this user on your roster. You may disable this to reduce network usage, however you will not be able to obtain status information.
- Ask for presence updates - Turning this on will enable the applications to send presence changes to this person on the roster. You may disable this to reduce network usage, however they will not receive notifications if you turn off the phone
.. Note::
These options are on by default and enable Tigase siskin IM for iOS to behave like a traditional client.
Editing a contact
^^^^^^^^^^^^^^^^^^^^^
When editing a contact, you may chose to change the account that has friended the user, XMPP name, edit a roster name (which will be shown on your roster). Here, you may also decide to selectively approve or deny subscription requests to and from the user. If you do not send presence updates, they will not know whether you are online, busy, or away. If you elect not to receive presence updates, you will not receive information if they are online, busy or away.
Tap the contact you want to edit, click "edit", after it is done, click "save"
|images/editcontacts01| |images/editcontacts02|
Settings
---------
click "Chats" on the bottom of mian panel and click the upper left, below are settings for the operation and behavior of the application.
Automatic
^^^^^^^^^^
To save data usage, your account status will be managed automatically using the following rules by default
+-----------+--------------------------------------------------------------------------------------------------------------------------------+
| Status | Behavior |
+-----------+--------------------------------------------------------------------------------------------------------------------------------+
| Online | Application has focus on the device. |
+-----------+--------------------------------------------------------------------------------------------------------------------------------+
| Away / XA | Application is running in the background. |
+-----------+--------------------------------------------------------------------------------------------------------------------------------+
| Offline | Application is killed or disconnected. If the device is turned off for a period of time, this will also set status to offline. |
+-----------+--------------------------------------------------------------------------------------------------------------------------------+
However, you may override this logic by tapping Automatic and selecting a status manually.
|images/status|
Apperance
^^^^^^^^^^
- | auto, light and dark
| adjust background brightness
Chats
^^^^^^^
- | Lines of preview:
| Sets the lines of preview text to keep within the chat window without using internal or message archive.
- | Send messages on return:
| If you are offline or away from connection, messages may be resent when you are back online or back in connection if this option is checked.
- | Chat markers & reeipts:
| Whether or not the message has been read by the receipts.
Contacts
^^^^^^^^^
- | Contacts in groups:
| Allows contacts to be displayed in groups as defined by the roster. Disabling this will show contacts in a flat organization.
- | "Hidden" group:
| Whether or not to display contacts that are added to the "hidden" group.
- | Auto-authorize contacts:
| Selecting this will automatically request subscription to users added to contacts.
Notifications
^^^^^^^^^^^^^
- | Notifications from unknown
| whether or not notifications from unknown sources will be sent to the native notification section of the device.
- | Push notifications
| whether or not notificaitons of new messages or calls will be received
This section has one option: Whether to accept notifications from unknown.
Media
^^^^^^^^^^^^^
- | File sharing via HTTP:
| This setting turns on the use of HTTP file sharing using the application. The server you are connected to must support this component to enable this option.
- | Simplified link to HTTP file:
| This creates a simplified link to the file after uploading rather than directly sending the file. This may be useful for intermittent communications.
- | File download limit:
| Sets the maximum size of files being sent to the user which may be automatically donwload.
- | Clear download cache:
| User can choose clears the devices cache of all downloaded and saved files retrieved from HTTP upload component or older than 7 days.
- | Clear link previews cache:
| User can choose clears the devices cache of all previews or older than 7 days.
Vcard
^^^^^^
You can set and change vCard data for your account. Tap the account you wish to edit and you will be presented with a number of fields that may be filled out. Click "change avatar" at the top where you may upload a photo as your avatar.
.. |images/siskin03| image:: images/siskin03.png
.. |images/join01| image:: images/join01.png
.. |images/join02| image:: images/join02.png
.. |images/editcontacts01| image:: images/editcontacts01.png
.. |images/editcontacts02| image:: images/editcontacts02.png
.. |images/status| image:: images/status.png
================================================
FILE: Documentation/restructured/Welcome.rst
================================================
Welcome
========
Welcome to the documentation for Siskin IM for iOS.
Siskin IM has some nice feature:
encrypted chats and group chats
sending and receiving files/pictures
audio- and videocalls
recording and sending voice messages
Sending your geolocation
Minimum Requirements
--------------------------
**iPhone**
Requires iOS 13.0 or later.
**iPad**
Requires iPadOS 13.0 or later.
**iPod touch**
Requires iOS 13.0 or later.
**Mac**
Requires macOS 11.0 or later and a Mac with Apple M1 chip or later.
Installation
-------------
Siskin IM is a good choice if you want to use an XMPP account on your iPhone or iPad. You can get Siskin IM from the App Store (`external <https://apps.apple.com/us/app/siskin-im/id1153516838>`__ ). Please keep in mind that Siskin IM is available in English only.
Account Setup
----------------------------
After downloading Siskin IM from the App Store you can start it by clicking the Siskin IM icon. At first Siskin IM asks if it is allowed to send notifications. You should allow Siskin IM to do so.
Your options now are to creat new XMPP account, or to use an existing XMPP account(if you do not already have one).
Registering for a New Account
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You have the choice between a lot of different XMPP providers. Your XMPP address will be the username you choose followed by the @-sign and the domain of the chosen provider.
Some examples for XMPP providers are:
magicbroccoli.de: Registration (`external <https://magicbroccoli.de/register/>`__ ).
wiuwiu.de: Registration (`external link <https://wiuwiu.de/>`__ ).
You can also choose a provider by looking at this list: (`external <https://apps.apple.com/us/app/siskin-im/id1153516838>`__ ).
If you do not know any XMPP server domain names, then you could select one of trusted servers from the list of sisin IM provided.
|images/register01|
After you select trusted servers, Fill out the fields for username, password, and E-mail. You do not need to add the domain to your username, it will be added for you so your JID will look like yourusername@domain.com
|images/register02|
An E-mail is required in case a server administrator needs to get in contact with you, or you lose your password and might need recovery.
Once you tap Register, the application will connect and register your account with the server. And you will receive the email confirmation with the link to confirm the new XMPP account.
Use an Existing Account
^^^^^^^^^^^^^^^^^^^^^^^^
Now you select Sign in to an existing XMPP account since you already registered an address in the previous section. Afterwards, you have to enter your XMPP address, your password and finish these steps by clicking Save. Please keep in mind that in this tutorial the XMPP address userfortest@tigase.im is used as an example account.
|images/siskin01|
|images/siskin02|
You will see a notification, which asks if you want to allow the Siskin server to send you Push Notifications. You should **enable** this setting to get notifications (even if the app is in the background). Siskin will now show you your XMPP address and that the setting Message Synchronization is activated. You can simply click on Done.
Your XMPP address is now configured to be used in Siskin IM.
Final Steps
------------
Once your account is verified, the application will log you in as online and display the chat screen.
.. |images/register01| image:: images/register01.PNG
.. |images/register02| image:: images/register02.PNG
.. |images/siskin01| image:: images/siskin01.jpg
.. |images/siskin02| image:: images/siskin02.png
================================================
FILE: Documentation/restructured/conf.py
================================================
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
#import os
#import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'TigaseDoc'
copyright = '2004-2022, Tigase, Inc'
author = 'Tigase, Inc.'
# The full version, including alpha/beta/rc tags
release = '0.1'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
html_theme_options = {'collapse_navigation': True,
'sticky_navigation': True,
'navigation_depth': 0,
'includehidden': True,
'titles_only': False,
'display_version': True,
}
gettext_compact = "siskin_im_translation"
language = "zh_CN"
locale_dirs = ["locale/"]
gettext_compact = "siskin_im_translation"
language = "pl"
locale_dirs = ["locale/"]
gettext_allow_fuzzy_translations = True
================================================
FILE: Documentation/restructured/index.rst
================================================
==========================================
Tigase Messenger for iOS - Version 1.0
==========================================
.. toctree::
:titlesonly:
:numbered: 3
Welcome
Tigase_Messenger_iOS
Advanced_Options
================================================
FILE: Documentation/restructured/locale/pl/LC_MESSAGES/siskin_im_translation.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2004-2022, Tigase, Inc
# This file is distributed under the same license as the TigaseDoc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: TigaseDoc \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-17 00:18-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.11.0\n"
#: ../../Advanced_Options.rst:2
msgid "Advanced Options"
msgstr ""
#: ../../Advanced_Options.rst:4
msgid ""
"This section contains information about advanced settings and options "
"that are available to the application, but may not be typically "
"considered for users."
msgstr ""
#: ../../Advanced_Options.rst
msgid "activation of end-to-end-encryption"
msgstr ""
#: ../../Advanced_Options.rst
msgid "requesting delivery receipts"
msgstr ""
#: ../../Advanced_Options.rst
msgid "activation of auto-authorization of contacts"
msgstr ""
#: ../../Advanced_Options.rst
msgid "optimization of settings for file exchange (files and pictures)"
msgstr ""
#: ../../Advanced_Options.rst
msgid "activation of group chat synchronization"
msgstr ""
#: ../../Advanced_Options.rst:13 ../../Tigase_Messenger_iOS.rst:83
msgid "Chats"
msgstr ""
#: ../../Advanced_Options.rst:15
msgid ""
"First, you visit the settings menu by selecting Chats. Then you need to "
"tap the upper left. In this submenu you can activate the end-to-end-"
"encryption (called OMEMO). Afterwards, you go back to the settings menu "
"by clicking Settings."
msgstr ""
#: ../../Advanced_Options.rst:17
msgid "|images/setting01| |images/setting02| |images/setting03|"
msgstr ""
#: ../../Advanced_Options.rst:42
msgid "images/setting01"
msgstr ""
#: ../../Advanced_Options.rst:43
msgid "images/setting02"
msgstr ""
#: ../../Advanced_Options.rst:44
msgid "images/setting03"
msgstr ""
#: ../../Advanced_Options.rst:20 ../../Tigase_Messenger_iOS.rst:7
#: ../../Tigase_Messenger_iOS.rst:95
msgid "Contacts"
msgstr ""
#: ../../Advanced_Options.rst:22
msgid ""
"In the settings menu you have to select Contacts. You activate the switch"
" Auto-authorize contacts and you could return to the settings menu by "
"clicking Settings."
msgstr ""
#: ../../Advanced_Options.rst:24
msgid "|images/setting04| |images/setting05|"
msgstr ""
#: ../../Advanced_Options.rst:45
msgid "images/setting04"
msgstr ""
#: ../../Advanced_Options.rst:46
msgid "images/setting05"
msgstr ""
#: ../../Advanced_Options.rst:27 ../../Tigase_Messenger_iOS.rst:119
msgid "Media"
msgstr ""
#: ../../Advanced_Options.rst:29
msgid ""
"In the settings menu you have to select Media.In this submenu you can "
"optimize the settings for file exchange. You should select **File sharing"
" via HTTP** and set the **File download limit** to 4 MB. Now you can send"
" files and upload them to your XMPP server (instead of sending them "
"directly to your communication partner). Files which you receive will be "
"downloaded automatically if their size is below 4 MB."
msgstr ""
#: ../../Advanced_Options.rst:31
msgid "|images/setting07| |images/setting06|"
msgstr ""
#: ../../Advanced_Options.rst:48
msgid "images/setting07"
msgstr ""
#: ../../Advanced_Options.rst:47
msgid "images/setting06"
msgstr ""
#: ../../Advanced_Options.rst:34
msgid "Experimental"
msgstr ""
#: ../../Advanced_Options.rst:36
msgid ""
"In the settings menu you have to select select Experimental. Here you can"
" activate the Groupchat bookmark sync to be able to see your group chats "
"on multiple devices. Additionally, I recommend to deactive the usage of "
"public STUN servers. Most todays XMPP servers already provide a STUN-"
"service."
msgstr ""
#: ../../Advanced_Options.rst:38
msgid "|images/setting08|"
msgstr ""
#: ../../Advanced_Options.rst:49
msgid "images/setting08"
msgstr ""
#: ../../Advanced_Options.rst:40
msgid ""
"After this step you can go back to the settings menu and close it. The "
"optimization of settings is done."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:2
msgid "Tigase Messenger for iOS Interface"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:4
msgid ""
"The menu interface for Tigase Messenger for iOS is broken up into three "
"main panels; Chats, Contacts and Bookmarks."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:9
msgid ""
"The contacts panel serves as your Roster, displaying all the contacts you"
" have on your roster, and displaying statuses along with their names. "
"Tigase Messenger for iOS supports vCard-Temp Avatars and will retrieve "
"them if they are uploaded by a user."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:11
msgid ""
"Contacts with green icons are available or free to chat status. Contacts "
"with yellow icons are away or extended away. Contacts with red icons are "
"in do not disturb status. Contacts with gray icons are offline or "
"unavailable."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:16
msgid ""
"Note that contacts will remain gray if you decide not to allow presence "
"notifications in the settings."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:18
msgid ""
"You may delete or edit contacts by tapping a contact and tapping Delete. "
"You also have the ability to edit a contact, explained in the next "
"section. Deleting the contact will remove them from your roster, and "
"remove any presence sharing permissions from the contact."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:21
msgid "Adding a contact"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:24
msgid ""
"To add a contact, you have to click on Contacts and select the +-sign at "
"the top of the screen. Select the account friends list you wish the new "
"contact to be added too. Then type in the JID of the user, do not use "
"resources, just bare JID. You may enter a friendly nickname for the "
"contact to be added to your friend list.In this tutorial *my friend* is "
"selected as the name for the contact. When adding users, you have two "
"options to select:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:26
msgid "|images/siskin03| |images/join01|"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:141
msgid "images/siskin03"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:142
msgid "images/join01"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:28
msgid "|images/join02|"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:143
msgid "images/join02"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:31
msgid ""
"Disclose my online status - This will allow sending of presence status "
"and changes to this user on your roster. You may disable this to reduce "
"network usage, however you will not be able to obtain status information."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:33
msgid ""
"Ask for presence updates - Turning this on will enable the applications "
"to send presence changes to this person on the roster. You may disable "
"this to reduce network usage, however they will not receive notifications"
" if you turn off the phone"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:37
msgid ""
"These options are on by default and enable Tigase siskin IM for iOS to "
"behave like a traditional client."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:40
msgid "Editing a contact"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:42
msgid ""
"When editing a contact, you may chose to change the account that has "
"friended the user, XMPP name, edit a roster name (which will be shown on "
"your roster). Here, you may also decide to selectively approve or deny "
"subscription requests to and from the user. If you do not send presence "
"updates, they will not know whether you are online, busy, or away. If you"
" elect not to receive presence updates, you will not receive information "
"if they are online, busy or away."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:44
msgid ""
"Tap the contact you want to edit, click \"edit\", after it is done, click"
" \"save\""
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:46
msgid "|images/editcontacts01| |images/editcontacts02|"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:144
msgid "images/editcontacts01"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:145
msgid "images/editcontacts02"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:50
msgid "Settings"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:53
msgid ""
"click \"Chats\" on the bottom of mian panel and click the upper left, "
"below are settings for the operation and behavior of the application."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:56
msgid "Automatic"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:58
msgid ""
"To save data usage, your account status will be managed automatically "
"using the following rules by default"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:61
msgid "Status"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:61
msgid "Behavior"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:63
msgid "Online"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:63
msgid "Application has focus on the device."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:65
msgid "Away / XA"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:65
msgid "Application is running in the background."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:67
msgid "Offline"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:67
msgid ""
"Application is killed or disconnected. If the device is turned off for a "
"period of time, this will also set status to offline."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:70
msgid ""
"However, you may override this logic by tapping Automatic and selecting a"
" status manually."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:72
msgid "|images/status|"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:146
msgid "images/status"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:76
msgid "Apperance"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "auto, light and dark"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "adjust background brightness"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Lines of preview:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Sets the lines of preview text to keep within the chat window without "
"using internal or message archive."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Send messages on return:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"If you are offline or away from connection, messages may be resent when "
"you are back online or back in connection if this option is checked."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Chat markers & reeipts:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Whether or not the message has been read by the receipts."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Contacts in groups:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Allows contacts to be displayed in groups as defined by the roster. "
"Disabling this will show contacts in a flat organization."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "\"Hidden\" group:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Whether or not to display contacts that are added to the \"hidden\" group."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Auto-authorize contacts:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Selecting this will automatically request subscription to users added to "
"contacts."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:107
msgid "Notifications"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Notifications from unknown"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"whether or not notifications from unknown sources will be sent to the "
"native notification section of the device."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Push notifications"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "whether or not notificaitons of new messages or calls will be received"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:116
msgid "This section has one option: Whether to accept notifications from unknown."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "File sharing via HTTP:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"This setting turns on the use of HTTP file sharing using the application."
" The server you are connected to must support this component to enable "
"this option."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Simplified link to HTTP file:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"This creates a simplified link to the file after uploading rather than "
"directly sending the file. This may be useful for intermittent "
"communications."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "File download limit:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Sets the maximum size of files being sent to the user which may be "
"automatically donwload."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Clear download cache:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"User can choose clears the devices cache of all downloaded and saved "
"files retrieved from HTTP upload component or older than 7 days."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid "Clear link previews cache:"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"User can choose clears the devices cache of all previews or older than 7 "
"days."
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:137
msgid "Vcard"
msgstr ""
#: ../../Tigase_Messenger_iOS.rst:139
msgid ""
"You can set and change vCard data for your account. Tap the account you "
"wish to edit and you will be presented with a number of fields that may "
"be filled out. Click \"change avatar\" at the top where you may upload a "
"photo as your avatar."
msgstr ""
#: ../../Welcome.rst:2
msgid "Welcome"
msgstr ""
#: ../../Welcome.rst:4
msgid "Welcome to the documentation for Siskin IM for iOS."
msgstr ""
#: ../../Welcome.rst:6
msgid "Siskin IM has some nice feature:"
msgstr ""
#: ../../Welcome.rst:8
msgid ""
"encrypted chats and group chats sending and receiving files/pictures "
"audio- and videocalls recording and sending voice messages Sending your "
"geolocation"
msgstr ""
#: ../../Welcome.rst:15
msgid "Minimum Requirements"
msgstr ""
#: ../../Welcome.rst:17
msgid "**iPhone** Requires iOS 13.0 or later."
msgstr ""
#: ../../Welcome.rst:20
msgid "**iPad** Requires iPadOS 13.0 or later."
msgstr ""
#: ../../Welcome.rst:23
msgid "**iPod touch** Requires iOS 13.0 or later."
msgstr ""
#: ../../Welcome.rst:26
msgid ""
"**Mac** Requires macOS 11.0 or later and a Mac with Apple M1 chip or "
"later."
msgstr ""
#: ../../Welcome.rst:31
msgid "Installation"
msgstr ""
#: ../../Welcome.rst:33
msgid ""
"Siskin IM is a good choice if you want to use an XMPP account on your "
"iPhone or iPad. You can get Siskin IM from the App Store (`external "
"<https://apps.apple.com/us/app/siskin-im/id1153516838>`__ ). Please keep "
"in mind that Siskin IM is available in English only."
msgstr ""
#: ../../Welcome.rst:37
msgid "Account Setup"
msgstr ""
#: ../../Welcome.rst:39
msgid ""
"After downloading Siskin IM from the App Store you can start it by "
"clicking the Siskin IM icon. At first Siskin IM asks if it is allowed to "
"send notifications. You should allow Siskin IM to do so."
msgstr ""
#: ../../Welcome.rst:41
msgid ""
"Your options now are to creat new XMPP account, or to use an existing "
"XMPP account(if you do not already have one)."
msgstr ""
#: ../../Welcome.rst:44
msgid "Registering for a New Account"
msgstr ""
#: ../../Welcome.rst:46
msgid ""
"You have the choice between a lot of different XMPP providers. Your XMPP "
"address will be the username you choose followed by the @-sign and the "
"domain of the chosen provider."
msgstr ""
#: ../../Welcome.rst:48
msgid "Some examples for XMPP providers are:"
msgstr ""
#: ../../Welcome.rst:50
msgid ""
"magicbroccoli.de: Registration (`external "
"<https://magicbroccoli.de/register/>`__ ). wiuwiu.de: Registration "
"(`external link <https://wiuwiu.de/>`__ ). You can also choose a provider"
" by looking at this list: (`external <https://apps.apple.com/us/app"
"/siskin-im/id1153516838>`__ )."
msgstr ""
#: ../../Welcome.rst:54
msgid ""
"If you do not know any XMPP server domain names, then you could select "
"one of trusted servers from the list of sisin IM provided."
msgstr ""
#: ../../Welcome.rst:56
msgid "|images/register01|"
msgstr ""
#: ../../Welcome.rst:85
msgid "images/register01"
msgstr ""
#: ../../Welcome.rst:58
msgid ""
"After you select trusted servers, Fill out the fields for username, "
"password, and E-mail. You do not need to add the domain to your username,"
" it will be added for you so your JID will look like "
"yourusername@domain.com"
msgstr ""
#: ../../Welcome.rst:60
msgid "|images/register02|"
msgstr ""
#: ../../Welcome.rst:86
msgid "images/register02"
msgstr ""
#: ../../Welcome.rst:62
msgid ""
"An E-mail is required in case a server administrator needs to get in "
"contact with you, or you lose your password and might need recovery."
msgstr ""
#: ../../Welcome.rst:64
msgid ""
"Once you tap Register, the application will connect and register your "
"account with the server. And you will receive the email confirmation with"
" the link to confirm the new XMPP account."
msgstr ""
#: ../../Welcome.rst:67
msgid "Use an Existing Account"
msgstr ""
#: ../../Welcome.rst:69
msgid ""
"Now you select Sign in to an existing XMPP account since you already "
"registered an address in the previous section. Afterwards, you have to "
"enter your XMPP address, your password and finish these steps by clicking"
" Save. Please keep in mind that in this tutorial the XMPP address "
"userfortest@tigase.im is used as an example account."
msgstr ""
#: ../../Welcome.rst:71
msgid "|images/siskin01|"
msgstr ""
#: ../../Welcome.rst:87
msgid "images/siskin01"
msgstr ""
#: ../../Welcome.rst:74
msgid "|images/siskin02|"
msgstr ""
#: ../../Welcome.rst:88
msgid "images/siskin02"
msgstr ""
#: ../../Welcome.rst:76
msgid ""
"You will see a notification, which asks if you want to allow the Siskin "
"server to send you Push Notifications. You should **enable** this setting"
" to get notifications (even if the app is in the background). Siskin will"
" now show you your XMPP address and that the setting Message "
"Synchronization is activated. You can simply click on Done."
msgstr ""
#: ../../Welcome.rst:78
msgid "Your XMPP address is now configured to be used in Siskin IM."
msgstr ""
#: ../../Welcome.rst:82
msgid "Final Steps"
msgstr ""
#: ../../Welcome.rst:83
msgid ""
"Once your account is verified, the application will log you in as online "
"and display the chat screen."
msgstr ""
#: ../../index.rst:4
msgid "Tigase Messenger for iOS - Version 1.0"
msgstr ""
#~ msgid ""
#~ "There is a bug in Siskin 7.0. "
#~ "Siskin ignores the default encryption "
#~ "setting and sends messages unencrypted "
#~ "instead. You need to activate the "
#~ "encryption in each chat manually. This"
#~ " bug will hopefully be fixed in "
#~ "version 7.0.1.."
#~ msgstr ""
#~ msgid ""
#~ "encrypted chats and group chats sending"
#~ " and receiving files/pictures audio- and"
#~ " videocalls recording and sending voice "
#~ "messages (Siskin IM 7.0) Sending your"
#~ " geolocation (Siskin IM 7.0)"
#~ msgstr ""
================================================
FILE: Documentation/restructured/locale/zh_CN/LC_MESSAGES/siskin_im_translation.po
================================================
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2004-2022, Tigase, Inc
# This file is distributed under the same license as the TigaseDoc package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
#
msgid ""
msgstr ""
"Project-Id-Version: TigaseDoc\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-17 00:18-0800\n"
"PO-Revision-Date: 2023-01-19 06:49+0000\n"
"Last-Translator: Qian Luo <qian.luo@tigase.net>\n"
"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/"
"siskin-im-documentation/siskin-im-documentation/zh_Hans/>\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.11.2\n"
"Generated-By: Babel 2.11.0\n"
#: ../../Advanced_Options.rst:2
msgid "Advanced Options"
msgstr "高级选项"
#: ../../Advanced_Options.rst:4
msgid ""
"This section contains information about advanced settings and options "
"that are available to the application, but may not be typically "
"considered for users."
msgstr "本部分包含有关应用程序可用的高级设置和选项的信息,但用户通常不会考虑这些设置"
"和选项。"
#: ../../Advanced_Options.rst
msgid "activation of end-to-end-encryption"
msgstr "激活端到端加密"
#: ../../Advanced_Options.rst
msgid "requesting delivery receipts"
msgstr "要求交付凭据"
#: ../../Advanced_Options.rst
msgid "activation of auto-authorization of contacts"
msgstr "激活联系人的自动授权"
#: ../../Advanced_Options.rst
msgid "optimization of settings for file exchange (files and pictures)"
msgstr "优化文件交换设置(文件和图片)"
#: ../../Advanced_Options.rst
msgid "activation of group chat synchronization"
msgstr "激活群聊同步"
#: ../../Advanced_Options.rst:13 ../../Tigase_Messenger_iOS.rst:83
msgid "Chats"
msgstr "聊天"
#: ../../Advanced_Options.rst:15
msgid ""
"First, you visit the settings menu by selecting Chats. Then you need to "
"tap the upper left. In this submenu you can activate the end-to-end-"
"encryption (called OMEMO). Afterwards, you go back to the settings menu "
"by clicking Settings."
msgstr ""
"首先,您可以通过选择“聊天”来访问设置菜单。然后你需要点击左上角。在此子菜单中"
",您可以激活端到端加密(称为 "
"OMEMO)。之后,您可以通过单击“设置”返回设置菜单。"
#: ../../Advanced_Options.rst:17
msgid "|images/setting01| |images/setting02| |images/setting03|"
msgstr "|images/setting01| |images/setting02| |images/setting03|"
#: ../../Advanced_Options.rst:42
msgid "images/setting01"
msgstr "images/setting01"
#: ../../Advanced_Options.rst:43
msgid "images/setting02"
msgstr "images/setting02"
#: ../../Advanced_Options.rst:44
msgid "images/setting03"
msgstr "images/setting03"
#: ../../Advanced_Options.rst:20 ../../Tigase_Messenger_iOS.rst:7
#: ../../Tigase_Messenger_iOS.rst:95
msgid "Contacts"
msgstr "联系人"
#: ../../Advanced_Options.rst:22
msgid ""
"In the settings menu you have to select Contacts. You activate the switch"
" Auto-authorize contacts and you could return to the settings menu by "
"clicking Settings."
msgstr "在设置菜单中,您必须选择“联系人”。您激活开关自动授权联系人,您可以通过单击“设"
"置”返回设置菜单。"
#: ../../Advanced_Options.rst:24
msgid "|images/setting04| |images/setting05|"
msgstr "|images/setting04| |images/setting05|"
#: ../../Advanced_Options.rst:45
msgid "images/setting04"
msgstr "images/setting04"
#: ../../Advanced_Options.rst:46
msgid "images/setting05"
msgstr "images/setting05"
#: ../../Advanced_Options.rst:27 ../../Tigase_Messenger_iOS.rst:119
msgid "Media"
msgstr "媒体"
#: ../../Advanced_Options.rst:29
msgid ""
"In the settings menu you have to select Media.In this submenu you can "
"optimize the settings for file exchange. You should select **File sharing"
" via HTTP** and set the **File download limit** to 4 MB. Now you can send"
" files and upload them to your XMPP server (instead of sending them "
"directly to your communication partner). Files which you receive will be "
"downloaded automatically if their size is below 4 MB."
msgstr ""
"在设置菜单中,您必须选择“媒体”。在此子菜单中,您可以优化文件交换设置。您应该"
"选择**通过 HTTP 共享文件**并将**文件下载限制**设置为 4 MB。"
"现在您可以发送文件并将它们上传到您的 XMPP "
"服务器(而不是直接将它们发送给您的通信伙伴)。如果文件大小低于 4 "
"MB,您收到的文件将自动下载。"
#: ../../Advanced_Options.rst:31
msgid "|images/setting07| |images/setting06|"
msgstr "|images/setting07| |images/setting06|"
#: ../../Advanced_Options.rst:48
msgid "images/setting07"
msgstr "images/setting07"
#: ../../Advanced_Options.rst:47
msgid "images/setting06"
msgstr "images/setting06"
#: ../../Advanced_Options.rst:34
msgid "Experimental"
msgstr "实验"
#: ../../Advanced_Options.rst:36
msgid ""
"In the settings menu you have to select select Experimental. Here you can"
" activate the Groupchat bookmark sync to be able to see your group chats "
"on multiple devices. Additionally, I recommend to deactive the usage of "
"public STUN servers. Most todays XMPP servers already provide a STUN-"
"service."
msgstr ""
"在设置菜单中,您必须选择“实验”。您可以在此处激活群聊书签同步,以便能够在多台"
"设备上查看您的群聊。此外,我建议停用公共 STUN 服务器的使用。大多数当今的 "
"XMPP 服务器已经提供了 STUN 服务。"
#: ../../Advanced_Options.rst:38
msgid "|images/setting08|"
msgstr "|images/setting08|"
#: ../../Advanced_Options.rst:49
msgid "images/setting08"
msgstr "images/setting08"
#: ../../Advanced_Options.rst:40
msgid ""
"After this step you can go back to the settings menu and close it. The "
"optimization of settings is done."
msgstr "完成此步骤后,您可以返回设置菜单并将其关闭。设置优化完成。"
#: ../../Tigase_Messenger_iOS.rst:2
msgid "Tigase Messenger for iOS Interface"
msgstr "iOS 界面的 Tigase Messenger"
#: ../../Tigase_Messenger_iOS.rst:4
msgid ""
"The menu interface for Tigase Messenger for iOS is broken up into three "
"main panels; Chats, Contacts and Bookmarks."
msgstr "iOS版 Tigase Messenger 的菜单界面分为三个主要面板;聊天、联系人和书签。"
#: ../../Tigase_Messenger_iOS.rst:9
msgid ""
"The contacts panel serves as your Roster, displaying all the contacts you"
" have on your roster, and displaying statuses along with their names. "
"Tigase Messenger for iOS supports vCard-Temp Avatars and will retrieve "
"them if they are uploaded by a user."
msgstr ""
"联系人面板用作您的花名册,显示您名册上的所有联系人,并显示状态及其姓名。"
"适用于 iOS 的 Tigase Messenger 支持 vCard-Temp "
"Avatars,如果用户上传它们,将会检索它们。"
#: ../../Tigase_Messenger_iOS.rst:11
msgid ""
"Contacts with green icons are available or free to chat status. Contacts "
"with yellow icons are away or extended away. Contacts with red icons are "
"in do not disturb status. Contacts with gray icons are offline or "
"unavailable."
msgstr ""
"带有绿色图标的联系人表示此用户可以聊天。带有黄色图标的联系人表示已离开或长时"
"间离开。带有红色图标的联系人处于请勿打扰状态。带有灰色图标的联系人处于离线状"
"态或不可用。"
#: ../../Tigase_Messenger_iOS.rst:16
msgid ""
"Note that contacts will remain gray if you decide not to allow presence "
"notifications in the settings."
msgstr "请注意,如果您决定不允许在设置中显示状态通知,那么联系人将保持灰色。"
#: ../../Tigase_Messenger_iOS.rst:18
msgid ""
"You may delete or edit contacts by tapping a contact and tapping Delete. "
"You also have the ability to edit a contact, explained in the next "
"section. Deleting the contact will remove them from your roster, and "
"remove any presence sharing permissions from the contact."
msgstr ""
"您可以通过点击联系人并点击删除来删除联系人。您还可以编辑联系人,这将在下一节"
"中进行说明。删除联系人会将他们从您的花名册中移除,并移除该联系人的所有状态共"
"享权限。"
#: ../../Tigase_Messenger_iOS.rst:21
msgid "Adding a contact"
msgstr "添加联系人"
#: ../../Tigase_Messenger_iOS.rst:24
msgid ""
"To add a contact, you have to click on Contacts and select the +-sign at "
"the top of the screen. Select the account friends list you wish the new "
"contact to be added too. Then type in the JID of the user, do not use "
"resources, just bare JID. You may enter a friendly nickname for the "
"contact to be added to your friend list.In this tutorial *my friend* is "
"selected as the name for the contact. When adding users, you have two "
"options to select:"
msgstr ""
"要添加联系人,您必须单击联系人并选择屏幕顶部的 +号。选择您希望添加新联系人的"
"帐户好友列表。然后输入用户的JID,不使用资源,只是裸JID。您可以为要添加到您的"
"朋友列表中的联系人输入一个友好的昵称。在本教程中,选择 *my friend* "
"作为联系人的姓名。添加用户时,您有两个选项可供选择:"
#: ../../Tigase_Messenger_iOS.rst:26
msgid "|images/siskin03| |images/join01|"
msgstr "|images/siskin03| |images/join01|"
#: ../../Tigase_Messenger_iOS.rst:141
msgid "images/siskin03"
msgstr "images/siskin03"
#: ../../Tigase_Messenger_iOS.rst:142
msgid "images/join01"
msgstr "images/join01"
#: ../../Tigase_Messenger_iOS.rst:28
msgid "|images/join02|"
msgstr "|images/join02|"
#: ../../Tigase_Messenger_iOS.rst:143
msgid "images/join02"
msgstr "images/join02"
#: ../../Tigase_Messenger_iOS.rst:31
msgid ""
"Disclose my online status - This will allow sending of presence status "
"and changes to this user on your roster. You may disable this to reduce "
"network usage, however you will not be able to obtain status information."
msgstr "公开我的在线状态 - 这将允许向您名册上的该用户发送在线状态和更改。您可以禁用此"
"功能以减少网络使用,但是您将无法获得状态信息。"
#: ../../Tigase_Messenger_iOS.rst:33
msgid ""
"Ask for presence updates - Turning this on will enable the applications "
"to send presence changes to this person on the roster. You may disable "
"this to reduce network usage, however they will not receive notifications"
" if you turn off the phone"
msgstr ""
"询问在线状态更新 - 打开此功能将使应用程序能够将在线状态更改发送给名册上的此人"
"。您可以禁用此功能以减少网络使用,但是如果您关闭手机,他们将不会收到通知"
#: ../../Tigase_Messenger_iOS.rst:37
msgid ""
"These options are on by default and enable Tigase siskin IM for iOS to "
"behave like a traditional client."
msgstr "这些选项在默认情况下处于启用状态,并使适用于 iOS 的 Tigase siskin IM "
"能够像传统客户端一样运行。"
#: ../../Tigase_Messenger_iOS.rst:40
msgid "Editing a contact"
msgstr "编辑联系人"
#: ../../Tigase_Messenger_iOS.rst:42
msgid ""
"When editing a contact, you may chose to change the account that has "
"friended the user, XMPP name, edit a roster name (which will be shown on "
"your roster). Here, you may also decide to selectively approve or deny "
"subscription requests to and from the user. If you do not send presence "
"updates, they will not know whether you are online, busy, or away. If you"
" elect not to receive presence updates, you will not receive information "
"if they are online, busy or away."
msgstr ""
"编辑联系人时,您可以选择更改已加好友的帐户、XMPP 名称、编辑名册名称(将显示在"
"您的名册中)。在这里,您还可以决定有选择地批准或拒绝用户的订阅请求。如果您不"
"发送状态更新,他们将不知道您是否在线、忙碌或离开。如果您选择不接收状态更新,"
"您将不会收到他们在线、忙碌或离开时的信息。"
#: ../../Tigase_Messenger_iOS.rst:44
msgid ""
"Tap the contact you want to edit, click \"edit\", after it is done, click"
" \"save\""
msgstr "点击要编辑的联系人,点击\"edit\",完成后点击\"save\""
#: ../../Tigase_Messenger_iOS.rst:46
msgid "|images/editcontacts01| |images/editcontacts02|"
msgstr "|images/editcontacts01| |images/editcontacts02|"
#: ../../Tigase_Messenger_iOS.rst:144
msgid "images/editcontacts01"
msgstr "images/editcontacts01"
#: ../../Tigase_Messenger_iOS.rst:145
msgid "images/editcontacts02"
msgstr "images/editcontacts02"
#: ../../Tigase_Messenger_iOS.rst:50
msgid "Settings"
msgstr "设置"
#: ../../Tigase_Messenger_iOS.rst:53
msgid ""
"click \"Chats\" on the bottom of mian panel and click the upper left, "
"below are settings for the operation and behavior of the application."
msgstr "单击主面板底部的 \"Chats\",然后单击左上角,下面是应用程序操作和行为的设置。"
#: ../../Tigase_Messenger_iOS.rst:56
msgid "Automatic"
msgstr "自动"
#: ../../Tigase_Messenger_iOS.rst:58
msgid ""
"To save data usage, your account status will be managed automatically "
"using the following rules by default"
msgstr "为了节省数据使用量,您的帐户状态将默认使用以下规则自动管理"
#: ../../Tigase_Messenger_iOS.rst:61
msgid "Status"
msgstr "状态"
#: ../../Tigase_Messenger_iOS.rst:61
msgid "Behavior"
msgstr "行为"
#: ../../Tigase_Messenger_iOS.rst:63
msgid "Online"
msgstr "在线"
#: ../../Tigase_Messenger_iOS.rst:63
msgid "Application has focus on the device."
msgstr "应用程序已将焦点放在设备上。"
#: ../../Tigase_Messenger_iOS.rst:65
msgid "Away / XA"
msgstr "离开/ XA"
#: ../../Tigase_Messenger_iOS.rst:65
msgid "Application is running in the background."
msgstr "应用程序正在后台运行。"
#: ../../Tigase_Messenger_iOS.rst:67
msgid "Offline"
msgstr "离线"
#: ../../Tigase_Messenger_iOS.rst:67
msgid ""
"Application is killed or disconnected. If the device is turned off for a "
"period of time, this will also set status to offline."
msgstr "应用程序被中止或断开连接。如果设备关闭一段时间,这也会将状态设置为离线。"
#: ../../Tigase_Messenger_iOS.rst:70
msgid ""
"However, you may override this logic by tapping Automatic and selecting a"
" status manually."
msgstr "但是,您可以通过点击Automatic并手动选择状态来覆盖此默认状态。"
#: ../../Tigase_Messenger_iOS.rst:72
msgid "|images/status|"
msgstr "|images/status|"
#: ../../Tigase_Messenger_iOS.rst:146
msgid "images/status"
msgstr "images/status"
#: ../../Tigase_Messenger_iOS.rst:76
msgid "Apperance"
msgstr "外观"
#: ../../Tigase_Messenger_iOS.rst
msgid "auto, light and dark"
msgstr "自动,明和暗"
#: ../../Tigase_Messenger_iOS.rst
msgid "adjust background brightness"
msgstr "调整背景亮度"
#: ../../Tigase_Messenger_iOS.rst
msgid "Lines of preview:"
msgstr "预览行:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Sets the lines of preview text to keep within the chat window without "
"using internal or message archive."
msgstr "将预览文本行设置为保留在聊天窗口中,而不使用内部或消息存档。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Send messages on return:"
msgstr "返回时发送消息:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"If you are offline or away from connection, messages may be resent when "
"you are back online or back in connection if this option is checked."
msgstr "如果您处于离线或断开连接状态,当选中此选项时,那么当您重新在线或重新连接时可"
"能会重新发送消息。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Chat markers & reeipts:"
msgstr "聊天标记和凭证:"
#: ../../Tigase_Messenger_iOS.rst
msgid "Whether or not the message has been read by the receipts."
msgstr "消息是否已被收信人阅读。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Contacts in groups:"
msgstr "群组联系人:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Allows contacts to be displayed in groups as defined by the roster. "
"Disabling this will show contacts in a flat organization."
msgstr "允许按花名册定义的组显示联系人。禁用此功能将显示扁平组织中的联系人。"
#: ../../Tigase_Messenger_iOS.rst
msgid "\"Hidden\" group:"
msgstr "\"Hidden\" 组:"
#: ../../Tigase_Messenger_iOS.rst
msgid "Whether or not to display contacts that are added to the \"hidden\" group."
msgstr "是否显示添加到\"hidden\" 组的联系人。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Auto-authorize contacts:"
msgstr "自动授权联系人:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Selecting this will automatically request subscription to users added to "
"contacts."
msgstr "选择此项将自动请求订阅添加到联系人的用户。"
#: ../../Tigase_Messenger_iOS.rst:107
msgid "Notifications"
msgstr "通知"
#: ../../Tigase_Messenger_iOS.rst
msgid "Notifications from unknown"
msgstr "来自未知来源的通知"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"whether or not notifications from unknown sources will be sent to the "
"native notification section of the device."
msgstr "来自未知来源的通知是否会发送到设备的本机通知部分。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Push notifications"
msgstr "推送通知"
#: ../../Tigase_Messenger_iOS.rst
msgid "whether or not notificaitons of new messages or calls will be received"
msgstr "是否收到新消息或来电通知"
#: ../../Tigase_Messenger_iOS.rst:116
msgid "This section has one option: Whether to accept notifications from unknown."
msgstr "本节有一个选项:是否接受来自未知来源的通知。"
#: ../../Tigase_Messenger_iOS.rst
msgid "File sharing via HTTP:"
msgstr "通过 HTTP 共享文件:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"This setting turns on the use of HTTP file sharing using the application."
" The server you are connected to must support this component to enable "
"this option."
msgstr "此设置启用使用应用程序的 HTTP "
"文件共享。您连接的服务器必须支持此组件才能启用此选项。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Simplified link to HTTP file:"
msgstr "HTTP 文件的简化链接:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"This creates a simplified link to the file after uploading rather than "
"directly sending the file. This may be useful for intermittent "
"communications."
msgstr "这会在上传后创建指向文件的简化链接,而不是直接发送文件。这可能对间歇性通信有"
"用。"
#: ../../Tigase_Messenger_iOS.rst
msgid "File download limit:"
msgstr "文件下载限制:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"Sets the maximum size of files being sent to the user which may be "
"automatically donwload."
msgstr "设置发送给用户的可以自动下载的最大文件大小。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Clear download cache:"
msgstr "清除下载缓存:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"User can choose clears the devices cache of all downloaded and saved "
"files retrieved from HTTP upload component or older than 7 days."
msgstr "用户可以选择清除设备缓存中从 HTTP 上传组件检索到的或早于 7 "
"天的所有下载和保存的文件。"
#: ../../Tigase_Messenger_iOS.rst
msgid "Clear link previews cache:"
msgstr "清除链接预览缓存:"
#: ../../Tigase_Messenger_iOS.rst
msgid ""
"User can choose clears the devices cache of all previews or older than 7 "
"days."
msgstr "用户可以选择清除所有预览或早于 7 天的设备缓存。"
#: ../../Tigase_Messenger_iOS.rst:137
msgid "Vcard"
msgstr "电子名片"
#: ../../Tigase_Messenger_iOS.rst:139
msgid ""
"You can set and change vCard data for your account. Tap the account you "
"wish to edit and you will be presented with a number of fields that may "
"be filled out. Click \"change avatar\" at the top where you may upload a "
"photo as your avatar."
msgstr ""
"您可以为您的帐户设置和更改电子名片数据。点击您要编辑的帐户,您将看到许多可以"
"填写的字段。点击顶部的\"change avatar\",您可以上传照片作为您的头像。"
#: ../../Welcome.rst:2
msgid "Welcome"
msgstr "欢迎"
#: ../../Welcome.rst:4
msgid "Welcome to the documentation for Siskin IM for iOS."
msgstr "欢迎使用 iOS 版 Siskin IM 文档。"
#: ../../Welcome.rst:6
msgid "Siskin IM has some nice feature:"
msgstr "Siskin IM 有一些不错的功能:"
#: ../../Welcome.rst:8
msgid ""
"encrypted chats and group chats sending and receiving files/pictures "
"audio- and videocalls recording and sending voice messages Sending your "
"geolocation"
msgstr "加密聊天和群组聊天发送和接收文件/图片 ,音频和视频通话 "
",录制和发送语音消息并发送您的地理位置"
#: ../../Welcome.rst:15
msgid "Minimum Requirements"
msgstr "最低要求"
#: ../../Welcome.rst:17
msgid "**iPhone** Requires iOS 13.0 or later."
msgstr "**iPhone** 需要 iOS 13.0 或更高版本。"
#: ../../Welcome.rst:20
msgid "**iPad** Requires iPadOS 13.0 or later."
msgstr "**iPad** 需要 iPadOS 13.0 或更高版本。"
#: ../../Welcome.rst:23
msgid "**iPod touch** Requires iOS 13.0 or later."
msgstr "**iPod touch** 需要 iOS 13.0 或更高版本。"
#: ../../Welcome.rst:26
msgid ""
"**Mac** Requires macOS 11.0 or later and a Mac with Apple M1 chip or "
"later."
msgstr "**Mac** 需要 macOS 11.0 或更高版本以及配备 Apple M1 芯片或更高版本的 Mac。"
#: ../../Welcome.rst:31
msgid "Installation"
msgstr "安装"
#: ../../Welcome.rst:33
msgid ""
"Siskin IM is a good choice if you want to use an XMPP account on your "
"iPhone or iPad. You can get Siskin IM from the App Store (`external "
"<https://apps.apple.com/us/app/siskin-im/id1153516838>`__ ). Please keep "
"in mind that Siskin IM is available in English only."
msgstr ""
"如果您想在 iPhone 或 iPad 上使用 XMPP 帐户,Siskin IM 是一个不错的选择。"
"您可以从 App Store 获取 Siskin IM(`external <https://apps.apple.com/us/app/"
"siskin-im/id1153516838>`__)。请记住,Siskin IM 仅提供英文版本。"
#: ../../Welcome.rst:37
msgid "Account Setup"
msgstr "帐户设置"
#: ../../Welcome.rst:39
msgid ""
"After downloading Siskin IM from the App Store you can start it by "
"clicking the Siskin IM icon. At first Siskin IM asks if it is allowed to "
"send notifications. You should allow Siskin IM to do so."
msgstr ""
"从 App Store 下载 Siskin IM 后,您可以通过单击 Siskin IM 图标启动它。起初 "
"Siskin IM 询问是否允许发送通知。您应该允许 Siskin IM 这样做"
#: ../../Welcome.rst:41
msgid ""
"Your options now are to creat new XMPP account, or to use an existing "
"XMPP account(if you do not already have one)."
msgstr "您现在的选择是创建新的 XMPP 帐户(如果您还没有), 或使用现有的 XMPP 帐户。"
#: ../../Welcome.rst:44
msgid "Registering for a New Account"
msgstr "注册一个新帐户"
#: ../../Welcome.rst:46
msgid ""
"You have the choice between a lot of different XMPP providers. Your XMPP "
"address will be the username you choose followed by the @-sign and the "
"domain of the chosen provider."
msgstr "您可以在许多不同的 XMPP 提供商之间进行选择。您的 XMPP "
"地址将是您选择的用户名,后跟 @ 符号和所选提供商的域。"
#: ../../Welcome.rst:48
msgid "Some examples for XMPP providers are:"
msgstr "XMPP 提供商的一些示例是:"
#: ../../Welcome.rst:50
msgid ""
"magicbroccoli.de: Registration (`external "
"<https://magicbroccoli.de/register/>`__ ). wiuwiu.de: Registration "
"(`external link <https://wiuwiu.de/>`__ ). You can also choose a provider"
" by looking at this list: (`external <https://apps.apple.com/us/app"
"/siskin-im/id1153516838>`__ )."
msgstr ""
"magicbroccoli.de: 注册 (`external <https://magicbroccoli.de/register/>`__ "
")。wiuwiu.de: 注册 (`external link <https://wiuwiu.de/>`__ "
").。您还可以通过查看此列表来选择提供商: (`external <https://apps.apple.com/"
"us/app/siskin-im/id1153516838>`__ )。"
#: ../../Welcome.rst:54
msgid ""
"If you do not know any XMPP server domain names, then you could select "
"one of trusted servers from the list of sisin IM provided."
msgstr "如果您不知道任何 XMPP 服务器域名,那么您可以从提供的 sisin IM "
"列表中选择一个受信任的服务器。"
#: ../../Welcome.rst:56
msgid "|images/register01|"
msgstr "|images/register01|"
#: ../../Welcome.rst:85
msgid "images/register01"
msgstr "images/register01"
#: ../../Welcome.rst:58
msgid ""
"After you select trusted servers, Fill out the fields for username, "
"password, and E-mail. You do not need to add the domain to your username,"
" it will be added for you so your JID will look like "
"yourusername@domain.com"
msgstr ""
"选择受信任的服务器后,填写用户名、密码和电子邮件字段。您不需要将域添加到您的"
"用户名,它将为您添加,因此您的 JID 看起来像 yourusername@domain.com"
#: ../../Welcome.rst:60
msgid "|images/register02|"
msgstr "|images/register02|"
#: ../../Welcome.rst:86
msgid "images/register02"
msgstr "images/register02"
#: ../../Welcome.rst:62
msgid ""
"An E-mail is required in case a server administrator needs to get in "
"contact with you, or you lose your password and might need recovery."
msgstr "需要一个电子邮件以防服务器管理员需要与您联系,或者您丢失密码并可能需要恢复。"
#: ../../Welcome.rst:64
msgid ""
"Once you tap Register, the application will connect and register your "
"account with the server. And you will receive the email confirmation with"
" the link to confirm the new XMPP account."
msgstr "点击注册后,应用程序将连接并向服务器注册您的帐户。您将收到带有确认新 XMPP "
"帐户链接的电子邮件确认。"
#: ../../Welcome.rst:67
msgid "Use an Existing Account"
msgstr "使用现有帐户"
#: ../../Welcome.rst:69
msgid ""
"Now you select Sign in to an existing XMPP account since you already "
"registered an address in the previous section. Afterwards, you have to "
"enter your XMPP address, your password and finish these steps by clicking"
" Save. Please keep in mind that in this tutorial the XMPP address "
"userfortest@tigase.im is used as an example account."
msgstr ""
"现在您选择登录现有的 XMPP 帐户,因为您已经在上一节中注册了一个地址。之后,"
"您必须输入您的 XMPP "
"地址、密码并通过单击保存完成这些步骤。请记住,在本教程中,XMPP 地址 "
"userfortest@tigase.im 用作示例帐户。"
#: ../../Welcome.rst:71
msgid "|images/siskin01|"
msgstr "|images/siskin01|"
#: ../../Welcome.rst:87
msgid "images/siskin01"
msgstr "images/siskin01"
#: ../../Welcome.rst:74
msgid "|images/siskin02|"
msgstr "|images/siskin02|"
#: ../../Welcome.rst:88
msgid "images/siskin02"
msgstr "images/siskin02"
#: ../../Welcome.rst:76
msgid ""
"You will see a notification, which asks if you want to allow the Siskin "
"server to send you Push Notifications. You should **enable** this setting"
" to get notifications (even if the app is in the background). Siskin will"
" now show you your XMPP address and that the setting Message "
"Synchronization is activated. You can simply click on Done."
msgstr ""
"您将看到一条通知,询问您是否要允许 Siskin 服务器向您发送推送通知。您应该**启"
"用**此设置以获取通知(即使应用程序在后台)。 Siskin 现在将向您显示您的 XMPP "
"地址,并且消息同步设置已激活。您只需单击完成即可。"
#: ../../Welcome.rst:78
msgid "Your XMPP address is now configured to be used in Siskin IM."
msgstr "您的 XMPP 地址现已配置并在 Siskin IM 中使用。"
#: ../../Welcome.rst:82
msgid "Final Steps"
msgstr "最后的步骤"
#: ../../Welcome.rst:83
msgid ""
"Once your account is verified, the application will log you in as online "
"and display the chat screen."
msgstr "一旦验证您的帐户后,该应用程序将使您在线登录并显示聊天屏幕。"
#: ../../index.rst:4
msgid "Tigase Messenger for iOS - Version 1.0"
msgstr "适用于 iOS 的 Tigase Messenger - 版本 1.0"
#~ msgid ""
#~ "There is a bug in Siskin 7.0. "
#~ "Siskin ignores the default encryption "
#~ "setting and sends messages unencrypted "
#~ "instead. You need to activate the "
#~ "encryption in each chat manually. This"
#~ " bug will hopefully be fixed in "
#~ "version 7.0.1.."
#~ msgstr ""
#~ msgid ""
#~ "encrypted chats and group chats sending"
#~ " and receiving files/pictures audio- and"
#~ " videocalls recording and sending voice "
#~ "messages (Siskin IM 7.0) Sending your"
#~ " geolocation (Siskin IM 7.0)"
#~ msgstr ""
================================================
FILE: Documentation/restructured/make.bat
================================================
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
================================================
FILE: Documentation/text/advanced.asciidoc
================================================
[[iOS_Advanced]]
= Advanced Options
:author: Tigase Team <team@tigase.net>
:toc:
:numbered:
:website: http://tigase.net
This section contains information about advanced settings and options that are available to the application, but may not be typically considered for users.
[[acctSettings]]
== Account Settings
For each connected account, there are sever-specific settings that are available. This may be brought up by selecting More... and then choosing the account you wish to edit.
image::images\acctsetting.png[]
*General*
- Enabled: +
Whether or not to enable this account. If it is disabled, it will be considered unavailable and offline. +
TIP: Push notifications will not work if the account is disabled!
- Change account settings: +
This screen allows changing of the account password if needed.
*Push Notifications*
Tigase Messenger for iOS supports link:https://xmpp.org/extensions/xep-0357.html[XEP-0357 Push Notifications] which will receive notifications when a device may be inactive, or the application is closed by the system.
Devices must be registered for push notifications and must register them VIA the Tigase XMPP Push Component, enabling push components will register the device you are using.
- Enabled: +
Enables Push notification support. Enabling this will register the device, and enable notifcations. +
- When in Away/XA/DND state: +
When enabled, push notifications will be delivered when in Away, Extended away, or Do not disturb statuses which may exist while the device is inactive. +
*Message Archiving*
- Enabled: +
Enabling this will allow the device to use the server's message archive component. This will allow storage and retrieval of messages. +
- Automatic synchronization: +
If this is enabled, it will synchronize with the server upon connection, sharing and retrieving message history. +
- Synchronization: +
Choose the level of synchronization that the device will retrieve and send to the server. +
================================================
FILE: Documentation/text/interface.asciidoc
================================================
[[Interface]]
= Tigase Messenger for iOS Interface
:author: Tigase Team <team@tigase.net>
:toc:
:numbered:
:website: http://tigase.net
The menu interface for Tigase Messenger for iOS is broken up into three main panels; xref:recent[Recent], xref:contacts[Contacts] and xref:more[More]. This can be brought up from any screen by swiping right from the left side of the screen, or tapping the back option on the top left.
[recent]
== Recent
The recent menu displays recent conversations with other users, and also serves as a way to navigate between multi-user chatrooms (MUCs). Each conversation will be displayed here along with an icon indicating user or room status.
image::images\recent.png[]
Tapping one of these conversations will bring up the chat, whether it is MUC or one on one. This panel also serves as an archive of sorts, and previous conversations with users will be accessible in this panel.
NOTE: Conversations will only be saved if they took place on this device, or if message archive is active.
You may clear conversations from the archive by dragging the name or MUC conversation to the left and selecting delete. If you are removing a MUC chat, you will leave the chatroom.
image:images\delchat.png[]
=== New/Join MUC
Tapping the plus button on the top right will bring up the new/join muc panel. This interface will allow you to either join an existing or create a new MUC on your chosen server.
image::images\join.png[]
- Account: This is the account that will handle data for the MUC chatroom. This is available for users who have multiple accounts logged in.
- Server: The server the chatroom is located on, in many cases the muc server will be muc.servername.com, but may be different.
- Room: The name of the chatroom you wish to create or join.
- Nickname: Your name for use inside the MUC. This will become `yournickname@muc.server.com`. MUC conversations do not leak your XMPP account, so a nickname is required.
- Password: The password for the MUC room. If you are creating a new chatroom, this will serve as the chat room password.
Once you are finished, tap Join and you will join, or the room will be opened for you.
The recent panel will now display the chatroom, you may tap it to enter the MUC interface.
When in a chatroom, you may view the occupants by tapping Occupants, and will be given a list and statuses of the room participants.
image::images\occu.png[]
[contacts]
== Contacts
The contacts panel serves as your Roster, displaying all the contacts you have on your roster, and displaying statuses along with their names. Tigase Messenger for iOS supports vCard-Temp Avatars and will retrieve them if they are uploaded by a user.
image::images\roster.png[]
Contacts with green icons are available or free to chat status. +
Contacts with yellow icons are away or extended away. +
Contacts with red icons are in do not disturb status. +
Contacts with gray icons are offline or unavailable. +
Note that contacts will remain gray if you decide not to allow presence notifications in the settings.
You may remove or edit contacts by dragging a contact to the left and tapping Delete. You also have the ability to edit a contact, explained in the next section.
Deleting the contact will remove them from your roster, and remove any presence sharing permissions from the contact.
image::images\deluser.PNG[]
You may also filter contacts by status by selecting All to display all users, or Available to hide users that are offline or unavailable.
=== Editing a contact
When editing a contact, you may chose to change the account that has friended the user, XMPP name, edit a roster name (which will be shown on your roster).
Here, you may also decide to selectively approve or deny subscription requests to and from the user.
If you do not send presence updates, they will not know whether you are online, busy, or away.
If you elect not to receive presence updates, you will not receive information if they are online, busy or away.
image::\images\edituser.png[]
=== Adding a contact
To add a contact, tap the plus button in the upper left and the add contact screen will show.
image::images\adduser.png[]
First, select the account friends list you wish the new contact to be added too. Then type in the JID of the user, do not use resources, just bare JID. You may enter a friendly nickname for the contact to be added to your friend list, this is optional.
When adding users, you have two options to select:
- Send presence updates - This will allow sending of presence status and changes to this user on your roster. You may disable this to reduce network usage, however you will not be able to obtain status information.
- Receive presence updates - Turning this on will enable the applications to send presence changes to this person on the roster. You may disable this to reduce network usage, however they will not receive notifications if you turn off the phone
NOTE: These options are on by default and enable Tigase Messenger for iOS to behave like a traditional client.
If you do decide to receive presence updates when adding a new contact, you will be presented with this screen when they add you back:
image::images\presreq.png[]
By tapping yes, you will receive notifications of presence changes from your contact. This subscription will be maintained by the server, and will stay active with your friends list.
NOTE: You will only receive this option if 'automatically accept presence requests' is set to yes in account settings.
TIP: If somebody not on your friends list adds you, you will receive this same message.
[more]
== More
The more panel is your program and account settings panel, from here you can change program settings and general account information.
image::images\settings.png[]
=== Accounts
This will list your current accounts, if an avatar has been defined for the account, it will show on the left side but by default the Tigase logo will be used.
=== vCard data
You can set and change vCard data for your account. Tap the account you wish to edit and you will be presented with a number of fields that may be filled out.
There is a blank space in the upper left corner where you may upload a photo as your avatar.
=== Badge descriptions
We have included a badging system on accounts to help indicate if connections issues are present with any account setup.
|===
|Icon | Meaning
|No icon | If account is disabled and will not try to connect
|Red icon with a cross |Account is disabled and will not try to connect due to server reporting an error (persistent error, i.e. authentication error).
|Grey |Account attempts to connect but is unable to connect to server (usually it means client is unable to establish TCP connection with the server) In this state, account tries to reconnect every few seconds if the client is in the foreground.
|Orange with dots |TCP connection is established but XMPP stream is not ready yet (not authorized yet, awaiting resource binding, etc).
|Green |XMPP client is connected and XMPP stream is established and ready to send/receive stanzas.
|===
=== Delete an account
If you wish to remove an account, swipe left and select Delete.
You will be asked for a confirmation whether you want to remove it from the application, and if the server supports it, you may delete it from the server removing roster, presence subscriptions, and potentially saved history.
image::images\delacct.png[]
WARNING: Deleting your account from the server is a permanent and non-reversible action.
You may also add multiple XMPP accounts from this screen. The add account screen looks identical to the one seen in the xref:existing[existing account] section.
To change settings for an individual account, tap that account name. Those options are covered under xref:acctSettings[Account Settings] section.
=== Status
Below accounts is a status setting for all connected and online accounts.
To save data usage, your account status will be managed automatically using the following rules by default
|===
|Status | Behavior
|Online | Application has focus on the device.
|Away / XA | Application is running in the background.
|Offline | Application is killed or disconnected. If the device is turned off for a period of time, this will also set status to offline.
|===
However, you may override this logic by tapping Automatic and selecting a status manually.
image::images\setstatus.png[]
==== Show tag
Underneath is a blank space where you can set your show tag
Editing this text section will change the `<show>` tags in your status. Once you press OK, your new show tag will display.
[settings]
=== Settings
Below are settings for the operation and behavior of the application.
image:\images\chatsettings.png[]
==== Chats
*List of Messages*
- Lines of preview: +
Sets the lines of preview text to keep within the chat window without using internal or message archive. +
- Sorting: +
Allows sorting of recent messages by Time, or by status and time (with unavailable resources at the bottom). +
*Messages*
- Send messages on return: +
If you are offline or away from connection, messages may be resent when you are back online or back in connection if this option is checked. +
- Clear chat on close: +
If this is enabled, when you close chats from the recent screen, all local history on the device will be deleted. This does not affect operation of offline or server-stored message archives. +
- Message carbons: +
Enables or disables message carbons to deliver to all resources. This is on by default, however some servers may not support this. +
- Request delivery receipts: +
Whether or not to request delivery receipts of messages sent. +
*Attachments*
- File sharing via HTTP: +
This setting turns on the use of HTTP file sharing using the application. The server you are connected too must support this component to enable this option.
- Simplified link to HTTP file: +
This creates a simplified link to the file after uploading rather than directly sending the file. This may be useful for intermittent communications. +
- Max image preview size: +
Sets the maximum size of image previews to download before fully downloading files. Setting this at 0 prevents previews from retrieving files. +
- Clear cache: +
This clears the devices cache of all downloaded and saved files retrieved from HTTP upload component. +
==== Contacts
*Display*
- Contacts in groups: +
Allows contacts to be displayed in groups as defined by the roster. Disabling this will show contacts in a flat organization. +
- "Hidden" group: +
Whether or not to display contacts that are added to the "hidden" group. +
*General*
- Auto-authorize contacts: +
Selecting this will automatically request subscription to users added to contacts. +
==== Notifications
This section has one option: Whether to accept notifications from unknown. If left disabled, notifications from unknown sources (including server administrators) will not be sent to the native notification section of the device. Instead, you will have to see them under the Recent menu.
================================================
FILE: Documentation/text/welcome.asciidoc
================================================
[[Welcome]]
= Welcome
:author: Tigase Team <team@tigase.net>
:toc:
:numbered:
:website: http://tigase.net
Welcome to the documentation for Tigase Messenger for iOS.
== Minimum Requirements
Tigase Messenger for iOS requires an apple device running iOS v10 or later. Compatible devices are listed below:
*iPhone* +
- iPhone 5 +
- iPhone 5C +
- iPhone 5S +
- iPhone 6 +
- iPhone 6 Plus +
- iPhone 6S +
- iPhone 6S Plus +
- iPhone 7 +
- iPhone 7 Plus +
- iPhone SE +
*iPod Touch* +
- iPod Touch (6th generation)
*iPad* +
- iPad (4th generation) +
- iPad (5th generation) +
- iPad Air +
- iPad Air 2 +
- iPad Mini 2 +
- iPad Mini 3 +
- iPad Mini 4 +
- iPad Pro +
== Installation
Tigase Messenger for iOS can be installed the same way any apple approved app can be found: through the appstore. Search for Tigase in the store search function and then tap install and follow the prompts to install Tigase Messenger.
== Account Setup
Upon running Tigase Messenger for iOS for the first time, you will be greeted with the following screen:
image::images\home.png[]
Your options are to xref:reg[register] for a new account, or to use an xref:existing[existing] account.
[register]
=== Registering for a New Account
The application supports creating a new account registration using in-band registration. This means that on servers supporting it, you can sign up for a new account straight from the client! A list of servers that support this is located link:https://list.jabber.at/[here].
We have provided quick-links to Tigase maintained servers where you can register an account. However, you may use another domain if you wish.
If you wish to use a custom domain, enter the domain address in the top bar, the application will then check with the server to ensure registration is supported.
You will be presented with an error message if it is not supported.
image::images\regfailure.png[]
If registration is supported, you will see the following prompts:
image::images\registernew.png[]
Fill out the fields for username, password, and E-mail. You do not need to add the domain to your username, it will be added for you so your JID will look like `yourusername@domain.com`
An E-mail is required in case a server administrator needs to get in contact with you, or you lose your password and might need recovery.
Once you tap Register, the application will connect and register your account with the server.
[existing]
=== Use an Existing Account
If you already have an XMPP account on a server, select this option to login using Tigase Messenger for iOS. Enter your username and password as normal and tap Save to add the account.
NOTE: Your device name will serve as the resource for your account. iPad or iPhone will automatically be used as the resource.
=== Certificate Errors
You may receive certificate errors from servers that may not have certificate chains installed, invalid, or expired certificates.
You will receive an unable to connect to server error, however servers with these errors will ask the user to accept or deny these security exceptions but they will show up at system notifications.
After doing so you may reattempt the connection to the server.
== Final Steps
Once your account is verified, the application will log you in as online and display the recent screen.
================================================
FILE: NotificationService/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>NotificationService</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
</dict>
</plist>
================================================
FILE: NotificationService/NotificationService.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.siskinim.shared</string>
<string>group.siskinim.notifications</string>
</array>
</dict>
</plist>
================================================
FILE: NotificationService/NotificationService.swift
================================================
//
// NotificationService.swift
//
// Siskin IM
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import BackgroundTasks
import UserNotifications
import UIKit
import Shared
import Martin
import os.log
import TigaseSQLite3
import Intents
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)? {
didSet {
debug("content handler set!");
}
}
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
debug("Received push!");
if let bestAttemptContent = bestAttemptContent {
bestAttemptContent.sound = UNNotificationSound.default;
bestAttemptContent.categoryIdentifier = "MESSAGE";
if let account = BareJID(bestAttemptContent.userInfo["account"] as? String) {
DispatchQueue.main.async {
let provider = ExtensionNotificationManagerProvider();
self.debug("push for account:", account);
if let encryped = bestAttemptContent.userInfo["encrypted"] as? String, let ivStr = bestAttemptContent.userInfo["iv"] as? String {
if let key = NotificationEncryptionKeys.key(for: account), let data = Data(base64Encoded: encryped), let iv = Data(base64Encoded: ivStr) {
self.debug("got encrypted push with known key");
let cipher = Cipher.AES_GCM();
var decoded = Data();
if cipher.decrypt(iv: iv, key: key, encoded: data, auth: nil, output: &decoded) {
self.debug("got decrypted data:", String(data: decoded, encoding: .utf8) as Any);
if let payload = try? JSONDecoder().decode(Payload.self, from: decoded) {
self.debug("decoded payload successfully!");
NotificationsManagerHelper.prepareNewMessageNotification(content: bestAttemptContent, account: account, sender: payload.sender.bareJid, nickname: payload.nickname, body: payload.message, provider: provider, completionHandler: { content in
DispatchQueue.main.async {
contentHandler(content);
}
});
return;
}
}
}
contentHandler(bestAttemptContent)
} else {
self.debug("got plain push with", bestAttemptContent.userInfo[AnyHashable("sender")] as? String as Any, bestAttemptContent.userInfo[AnyHashable("body")] as? String as Any, bestAttemptContent.userInfo[AnyHashable("unread-messages")] as? Int as Any, bestAttemptContent.userInfo[AnyHashable("nickname")] as? String as Any);
NotificationsManagerHelper.prepareNewMessageNotification(content: bestAttemptContent, account: account, sender: JID(bestAttemptContent.userInfo[AnyHashable("sender")] as? String)?.bareJid, nickname: bestAttemptContent.userInfo[AnyHashable("nickname")] as? String, body: bestAttemptContent.userInfo[AnyHashable("body")] as? String, provider: provider, completionHandler: { content in
DispatchQueue.main.async {
contentHandler(content);
}
});
}
}
return;
} else {
contentHandler(bestAttemptContent);
}
} else {
contentHandler(request.content);
}
// if #available(iOS 13.0, *) {
// let taskRequest = BGAppRefreshTaskRequest(identifier: "org.tigase.messenger.mobile.refresh");
// taskRequest.earliestBeginDate = nil
// do {
// debug("scheduling background app refresh")
// BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: "org.tigase.messenger.mobile.refresh")
// try BGTaskScheduler.shared.submit(taskRequest);
// } catch {
// debug("Could not schedule app refresh: \(error)")
// }
// }
}
// func updateNotification(content: UNMutableNotificationContent, account: BareJID, unread: Int, sender: JID, type kind: Payload.Kind, nickname: String?, body: String) {
// let tmp = try! DBConnection.main.prepareStatement(NotificationService.GET_NAME_QUERY).findFirst(["account": account, "jid": sender.bareJid] as [String: Any?], map: { (cursor) -> (String?, Int)? in
// return (cursor["name"], cursor["type"]!);
// });
// let name = tmp?.0;
// let type: Payload.Kind = tmp?.1 == 1 ? .groupchat : .chat;
// switch type {
// case .chat:
// content.title = name ?? sender.stringValue;
// content.body = body;
// content.userInfo = ["account": account.stringValue, "sender": sender.bareJid.stringValue];
// case .groupchat:
// if let nickname = nickname {
// content.title = "\(nickname) mentioned you in \(name ?? sender.bareJid.stringValue)";
// } else {
// content.title = "\(name ?? sender.bareJid.stringValue)";
// }
// content.body = body;
// content.userInfo = ["account": account.stringValue, "sender": sender.bareJid.stringValue];
// default:
// break;
// }
// content.categoryIdentifier = NotificationCategory.MESSAGE.rawValue;
// //content.badge = 2;
//
// }
func debug(_ data: Any...) {
os_log("%{public}@", log: OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "SiskinPush"), "\(Date()): \(data)");
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
extension Database {
private static var mainReaderInstance: DatabaseReader?;
static func mainReader() throws -> DatabaseReader {
if mainReaderInstance == nil {
mainReaderInstance = try Database(path: Database.mainDatabaseUrl().path, flags: SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX);
}
return mainReaderInstance!;
}
}
extension Query {
static let buddyName = Query("select name from roster_items where account = :account and jid = :jid");
static let conversationNotificationDetails = Query("SELECT c.type as type, c.options as options FROM chats c WHERE c.account = :account AND c.jid = :jid")
static let listUnreadThreads = Query("select c.account, c.jid from chats c inner join chat_history ch where ch.account = c.account and ch.jid = c.jid and ch.state in (2,6,7) group by c.account, c.jid");
static let findAvatar = Query("select ac.hash FROM avatars_cache ac WHERE ac.account = :account AND ac.jid = :jid ORDER BY ac.type ASC");
}
class ExtensionNotificationManagerProvider: NotificationManagerProvider {
static let GET_UNREAD_CHATS = "s";
func avatar(on account: BareJID, for sender: BareJID) -> INImage? {
guard let hash = try? Database.mainReader().select(query: .findAvatar, params: ["account": account, "jid": sender]).mapFirst({ $0.string(for: "hash") }) else {
return nil;
}
let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.siskinim.shared")!.appendingPathComponent("Library", isDirectory: true).appendingPathComponent("Caches", isDirectory: true).appendingPathComponent("avatars", isDirectory: true).appendingPathComponent(hash);
return UIImage(contentsOfFile: url.path)?.inImage();
}
func conversationNotificationDetails(for account: BareJID, with jid: BareJID, completionHandler: @escaping (ConversationNotificationDetails)->Void) {
let (type, options) = try! Database.mainReader().select(query: .conversationNotificationDetails, cached: false, params: ["account": account, "jid": jid]).mapFirst({ cursor -> (ConversationType, ConversationOptions) in
let type = ConversationType(rawValue: cursor.int(for: "type")!) ?? .chat;
let options: ConversationOptions = cursor.object(for: "options") ?? ConversationOptions();
return (type, options);
}) ?? (.chat, ConversationOptions());
switch type {
case .chat:
completionHandler(ConversationNotificationDetails(name: try! Database.mainReader().select(query: .buddyName, cached: false, params: ["account": account, "jid": jid]).mapFirst({ $0.string(for: "name") }) ?? jid.stringValue, notifications: options.notifications ?? .always, type: type, nick: nil));
case .channel, .room:
completionHandler(ConversationNotificationDetails(name: options.name ?? jid.stringValue, notifications: options.notifications ?? .always, type: type, nick: options.nick));
}
}
func countBadge(withThreadId: String?, completionHandler: @escaping (Int) -> Void) {
NotificationsManagerHelper.unreadChatsThreadIds { (result) in
var unreadChats = result;
try? Database.mainReader().select(query: .listUnreadThreads, cached: false, params: []).mapAll({ cursor in
if let account = cursor.bareJid(for: "account"), let jid = cursor.bareJid(for: "jid") {
return "account=\(account.stringValue)|sender=\(jid.stringValue)"
}
return nil;
}).forEach({ unreadChats.insert($0) });
if let threadId = withThreadId {
unreadChats.insert(threadId);
}
completionHandler(unreadChats.count);
}
completionHandler(-1);
}
func shouldShowNotification(account: BareJID, sender: BareJID?, body: String?, completionHandler: @escaping (Bool)->Void) {
completionHandler(true);
}
}
class Provider {
}
public struct ConversationOptions: Codable {
var name: String?;
var nick: String?;
var notifications: ConversationNotification?;
init(name: String? = nil, nick: String? = nil, notifications: ConversationNotification? = nil) {
self.name = name;
self.nick = nick;
self.notifications = notifications;
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self);
name = try container.decodeIfPresent(String.self, forKey: .name);
nick = try container.decode(String.self, forKey: .nick);
if let notificationsString = try container.decodeIfPresent(String.self, forKey: .notifications) {
notifications = ConversationNotification(rawValue: notificationsString);
} else {
notifications = nil;
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self);
try container.encodeIfPresent(name, forKey: .name);
try container.encodeIfPresent(nick, forKey: .nick);
try container.encodeIfPresent(notifications?.rawValue, forKey: .notifications);
}
enum CodingKeys: String, CodingKey {
case name = "name";
case notifications = "notifications";
case nick = "nick";
}
}
================================================
FILE: README.md
================================================
<p align="center">
<a href="https://siskin.im/">
<img
alt="SiskinIM"
src="https://siskin.im/img/services/colors.jpg"
width="600"
/>
</a>
</p>
<p align="center">
The XMPP client for iOS <img src="https://github.com/tigaseinc/website-assets/blob/master/tigase/images/tigase-logo.png?raw=true" width="25px"/>
</p>
<p align="center">
<a href="https://itunes.apple.com/us/app/tigase-messenger/id1153516838">
<img src="https://siskin.im/img/appstore-download.svg"/>
</a>
</p>
# What it is
Siskin IM by Tigase, Inc. is a lightweight and powerful XMPP client for iPhone and iPad.
# Features
Siskin IM is easy to use and lightweight XMPP client. It has support for file and image sharing, group chats, end-to-end encryption and many [more](https://siskin.im).
# Support
When looking for support, please first search existing issues and pull-requests. If you didn't find an answer in the resources above, feel free to submit your question as [new issue on GitHub](https://github.com/tigase/siskin-im/issues/new/choose).
# Downloads
Sikin IM may be downloaded from the [App Store](https://itunes.apple.com/us/app/tigase-messenger/id1153516838).
## TestFlight
**WARNING: Testing version of the Siskin can be unstable and cause data loss, you are using it at your discretion.**
To sign up to TestFlight beta testing please use following link: https://testflight.apple.com/join/zDSl1D3s to join the testing program.
# Using software
After installation of Siskin IM it will suggest you to add or register the XMPP account, which you should do.
After that you can start chatting with your friends using your XMPP account.
# License
<img alt="Tigase Tigase Logo" src="https://github.com/tigase/website-assets/blob/master/tigase/images/tigase-logo.png?raw=true" width="25"/> Official <a href="https://tigase.net/">Tigase</a> repository is available at: https://github.com/tigase/siskin-im/.
Copyright (c) 2004 Tigase, Inc.
Licensed under GPL License Version 3. Other licensing options available upon request.
================================================
FILE: Shared/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
================================================
FILE: Shared/NotificationCategory.swift
================================================
//
// NotificationCategory.swift
//
// Siskin IM
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
public enum NotificationCategory: String {
case UNKNOWN
case ERROR
case MESSAGE
case SUBSCRIPTION_REQUEST
case MUC_ROOM_INVITATION
case CALL
case UNSENT_MESSAGES
public static func from(identifier: String?) -> NotificationCategory {
guard let str = identifier else {
return .UNKNOWN;
}
return NotificationCategory(rawValue: str) ?? .UNKNOWN;
}
}
================================================
FILE: Shared/Shared.h
================================================
//
// Shared.h
//
// Siskin IM
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
#import <Foundation/Foundation.h>
//! Project version number for Shared.
//FOUNDATION_EXPORT double SharedVersionNumber;
//! Project version string for Shared.
//FOUNDATION_EXPORT const unsigned char SharedVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Shared/PublicHeader.h>
================================================
FILE: Shared/database/ConversationType.swift
================================================
//
// ConversationType.swift
//
// Siskin IM
// Copyright (C) 2021 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
public enum ConversationType: Int {
case chat = 0
case room = 1
case channel = 2
}
================================================
FILE: Shared/database/Database.swift
================================================
//
// Database.swift
//
// Siskin IM
// Copyright (C) 2021 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import TigaseSQLite3
import Martin
extension Database {
public static func mainDatabaseUrl() -> URL {
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.siskinim.shared")!.appendingPathComponent("siskinim_main.db");
}
}
extension JID: DatabaseConvertibleStringValue {
public func encode() -> String {
return self.stringValue;
}
}
extension BareJID: DatabaseConvertibleStringValue {
public func encode() -> String {
return self.stringValue;
}
}
extension Element: DatabaseConvertibleStringValue {
public func encode() -> String {
return self.stringValue;
}
}
extension Cursor {
public func jid(for column: String) -> JID? {
return JID(string(for: column));
}
public func jid(at column: Int) -> JID? {
return JID(string(at: column));
}
public subscript(index: Int) -> JID? {
return JID(string(at: index));
}
public subscript(column: String) -> JID? {
return JID(string(for: column));
}
}
extension Cursor {
public func bareJid(for column: String) -> BareJID? {
return BareJID(string(for: column));
}
public func bareJid(at column: Int) -> BareJID? {
return BareJID(string(at: column));
}
public subscript(index: Int) -> BareJID? {
return BareJID(string(at: index));
}
public subscript(column: String) -> BareJID? {
return BareJID(string(for: column));
}
}
================================================
FILE: Shared/notifications/ConversationNotifications.swift
================================================
//
// ConversationNotifications.swift
//
// Siskin IM
// Copyright (C) 2021 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
public enum ConversationNotification: String {
case none
case mention
case always
}
================================================
FILE: Shared/notifications/NotificationEncryptionKeys.swift
================================================
//
// NotificationEncryptionKeys.swift
//
// Siskin IM
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import Martin
public class NotificationEncryptionKeys {
private static let storage = UserDefaults(suiteName: "group.siskinim.notifications")!;
public static func key(for account: BareJID) -> Data? {
storage.data(forKey: account.stringValue)
}
public static func set(key: Data?, for account: BareJID) {
storage.setValue(key, forKey: account.stringValue);
}
}
================================================
FILE: Shared/notifications/NotificationsManagerHelper.swift
================================================
//
// NotificationsManagerHelper.swift
//
// Siskin IM
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import Martin
import UserNotifications
import os
import Intents
import UIKit
public struct ConversationNotificationDetails {
public let name: String;
public let notifications: ConversationNotification;
public let type: ConversationType;
public let nick: String?;
public init(name: String, notifications: ConversationNotification, type: ConversationType, nick: String?) {
self.name = name;
self.notifications = notifications;
self.type = type;
self.nick = nick;
}
}
public class NotificationsManagerHelper {
public static func unreadChatsThreadIds(completionHandler: @escaping (Set<String>)->Void) {
unreadThreadIds(for: [.MESSAGE], completionHandler: completionHandler);
}
public static func unreadThreadIds(for categories: [NotificationCategory], completionHandler: @escaping (Set<String>)->Void) {
UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
let unreadChats = Set(notifications.filter({(notification) in
let category = NotificationCategory.from(identifier: notification.request.content.categoryIdentifier);
return categories.contains(category);
}).map({ (notification) in
return notification.request.content.threadIdentifier;
}));
completionHandler(unreadChats);
}
}
public static func generateMessageUID(account: BareJID, sender: BareJID?, body: String?) -> String? {
if let sender = sender, let body = body {
return Digest.sha256.digest(toHex: "\(account)|\(sender)|\(body)".data(using: .utf8));
}
return nil;
}
public static func prepareNewMessageNotification(content: UNMutableNotificationContent, account: BareJID, sender jid: BareJID?, nickname: String?, body msg: String?, provider: NotificationManagerProvider, completionHandler: @escaping (UNNotificationContent)->Void) {
let timestamp = Date();
content.sound = .default;
content.categoryIdentifier = NotificationCategory.MESSAGE.rawValue;
if let sender = jid, let body = msg {
let uid = generateMessageUID(account: account, sender: sender, body: body)!;
content.threadIdentifier = "account=\(account.stringValue)|sender=\(sender.stringValue)";
provider.conversationNotificationDetails(for: account, with: sender, completionHandler: { details in
os_log("%{public}@", log: OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "SiskinPush"), "Found: name: \(details.name), type: \(String(describing: details.type.rawValue))");
var senderId: String = sender.stringValue;
var group: INSpeakableString?;
switch details.type {
case .chat:
content.title = details.name;
if body.starts(with: "/me ") {
content.body = String(body.dropFirst(4));
} else {
content.body = body;
}
case .channel, .room:
content.title = details.name
group = INSpeakableString(spokenPhrase: details.name);
if body.starts(with: "/me ") {
if let nickname = nickname {
content.body = "\(nickname) \(body.dropFirst(4))";
} else {
content.body = String(body.dropFirst(4));
}
} else {
content.body = body;
if let nickname = nickname {
content.subtitle = nickname;
senderId = sender.with(resource: nickname).stringValue;
}
}
}
content.userInfo = ["account": account.stringValue, "sender": sender.stringValue, "uid": uid, "timestamp": timestamp];
provider.countBadge(withThreadId: content.threadIdentifier, completionHandler: { count in
content.badge = count as NSNumber;
if #available(iOS 15.0, *) {
do {
let recipient = INPerson(personHandle: INPersonHandle(value: account.stringValue, type: .unknown), nameComponents: nil, displayName: nil, image: nil, contactIdentifier: nil, customIdentifier: nil, isMe: true, suggestionType: .none);
let avatar = provider.avatar(on: account, for: sender);
let sender = INPerson(personHandle: INPersonHandle(value: senderId, type: .unknown), nameComponents: nil, displayName: group == nil ? details.name : nickname, image: avatar, contactIdentifier: nil, customIdentifier: senderId, isMe: false, suggestionType: .instantMessageAddress);
let intent = INSendMessageIntent(recipients: group == nil ? [recipient] : [recipient, sender], outgoingMessageType: .outgoingMessageText, content: nil, speakableGroupName: group, conversationIdentifier: content.threadIdentifier, serviceName: "Siskin IM", sender: sender, attachments: nil);
if details.type == .chat {
intent.setImage(avatar, forParameterNamed: \.sender);
} else {
intent.setImage(avatar, forParameterNamed: \.speakableGroupName);
}
let interaction = INInteraction(intent: intent, response: nil);
interaction.direction = .incoming;
interaction.donate(completion: nil);
completionHandler(try content.updating(from: intent));
} catch {
// some error happened
completionHandler(content);
}
} else {
completionHandler(content);
}
});
})
} else {
content.threadIdentifier = "account=\(account.stringValue)";
content.body = NSLocalizedString("New message!", comment: "new message without content notification");
provider.countBadge(withThreadId: content.threadIdentifier, completionHandler: { count in
content.badge = count as NSNumber;
completionHandler(content);
});
}
}
}
public protocol NotificationManagerProvider {
func conversationNotificationDetails(for account: BareJID, with jid: BareJID, completionHandler: @escaping (ConversationNotificationDetails)->Void);
func countBadge(withThreadId: String?, completionHandler: @escaping (Int)->Void);
func shouldShowNotification(account: BareJID, sender: BareJID?, body: String?, completionHandler: @escaping (Bool)->Void);
func avatar(on account: BareJID, for sender: BareJID) -> INImage?;
}
public class Payload: Decodable {
public var unread: Int;
public var sender: JID;
public var type: Kind;
public var nickname: String?;
public var message: String?;
public var sid: String?;
public var media: [String]?;
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self);
unread = try container.decode(Int.self, forKey: .unread);
sender = try container.decode(JID.self, forKey: .sender);
type = Kind(rawValue: (try container.decodeIfPresent(String.self, forKey: .type)) ?? Kind.unknown.rawValue)!;
nickname = try container.decodeIfPresent(String.self, forKey: .nickname);
message = try container.decodeIfPresent(String.self, forKey: .message);
sid = try container.decodeIfPresent(String.self, forKey: .sid)
media = try container.decodeIfPresent([String].self, forKey: .media);
// -- and so on...
}
public enum Kind: String {
case unknown
case groupchat
case chat
case call
}
public enum CodingKeys: String, CodingKey {
case unread
case sender
case type
case nickname
case message
case sid
case media
}
}
================================================
FILE: Shared/ui/UIImage.swift
================================================
//
// UIImage.swift
//
// Siskin IM
// Copyright (C) 2020 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import UIKit
import Intents
extension UIImage {
public func scaled(maxWidthOrHeight: CGFloat, isOpaque: Bool = false) -> UIImage? {
guard maxWidthOrHeight < size.height || maxWidthOrHeight < size.width else {
return self;
}
let newSize = size.height > size.width ? CGSize(width: (size.width / size.height) * maxWidthOrHeight, height: maxWidthOrHeight) : CGSize(width: maxWidthOrHeight, height: (size.height / size.width) * maxWidthOrHeight);
let format = imageRendererFormat;
if isOpaque {
format.opaque = isOpaque;
}
return UIGraphicsImageRenderer(size: newSize, format: format).image { _ in
draw(in: CGRect(origin: .zero, size: newSize));
};
// UIGraphicsBeginImageContextWithOptions(newSize, false, 0);
// self.imageRendererFormat
// self.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height));
// defer {
// UIGraphicsEndImageContext();
// }
// return UIGraphicsGetImageFromCurrentImageContext();
}
public func inImage() -> INImage? {
guard let data = self.jpegData(compressionQuality: 0.7) else {
return nil;
}
return INImage(imageData: data);
}
}
================================================
FILE: Shared/util/HTTPFileUploadHelper.swift
================================================
//
// HTTPFileUploadHelper.swift
//
// Siskin IM
// Copyright (C) 2017 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import Martin
import TigaseLogging
open class HTTPFileUploadHelper {
private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "HTTPFileUploadHelper")
public static func upload(for context: Context, filename: String, inputStream: InputStream, filesize size: Int, mimeType: String, delegate: URLSessionDelegate?, completionHandler: @escaping (Result<URL,ShareError>)->Void) {
let httpUploadModule = context.module(.httpFileUpload);
httpUploadModule.findHttpUploadComponent(completionHandler: { result in
switch result {
case .success(let components):
guard let component = components.first(where: { $0.maxSize > size }) else {
completionHandler(.failure(.fileTooBig));
return;
}
httpUploadModule.requestUploadSlot(componentJid: component.jid, filename: filename, size: size, contentType: mimeType, completionHandler: { result in
switch result {
case .success(let slot):
var request = URLRequest(url: slot.putUri);
slot.putHeaders.forEach({ (k,v) in
request.addValue(v, forHTTPHeaderField: k);
});
request.httpMethod = "PUT";
request.httpBodyStream = inputStream;
request.addValue(String(size), forHTTPHeaderField: "Content-Length");
request.addValue(mimeType, forHTTPHeaderField: "Content-Type");
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: delegate, delegateQueue: OperationQueue.main);
session.dataTask(with: request) { (data, response, error) in
let code = (response as? HTTPURLResponse)?.statusCode ?? 500;
guard error == nil && (code == 200 || code == 201) else {
logger.error("upload of file \(filename) failed, error: \(error as Any), response: \(response as Any)");
completionHandler(.failure(.httpError));
return;
}
if code == 200 {
completionHandler(.failure(.invalidResponseCode(url: slot.getUri)));
} else {
completionHandler(.success(slot.getUri));
}
}.resume();
case .failure(let error):
logger.error("upload of file \(filename) failed, upload component returned error: \(error as Any)");
completionHandler(.failure(.unknownError));
}
});
case .failure(let error):
completionHandler(.failure(error.errorCondition == .item_not_found ? .notSupported : .unknownError));
}
})
}
public enum UploadResult {
case success(url: URL, filesize: Int, mimeType: String?)
case failure(ShareError)
}
}
================================================
FILE: Shared/util/ImageQuality.swift
================================================
//
// ImageQuality.swift
//
// Siskin IM
// Copyright (C) 2020 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import UIKit
public enum ImageQuality: String {
case original
case highest
case high
case medium
case low
public var label: String {
switch self {
case .original:
return NSLocalizedString("Original", comment: "video quality")
case .highest:
return NSLocalizedString("Highest", comment: "video quality")
case .high:
return NSLocalizedString("High", comment: "video quality")
case .medium:
return NSLocalizedString("Medium", comment: "video quality")
case .low:
return NSLocalizedString("Low", comment: "video quality")
}
}
public var size: CGFloat {
switch self {
case .original:
return CGFloat.greatestFiniteMagnitude;
case .highest:
return CGFloat.greatestFiniteMagnitude;
case .high:
return 2048;
case .medium:
return 1536;
case .low:
return 1024;
}
}
public var quality: CGFloat {
switch self {
case .original:
return 1;
case .highest:
return 1;
case .high:
return 0.85;
case .medium:
return 0.7;
case .low:
return 0.6;
}
}
}
================================================
FILE: Shared/util/MediaHelper.swift
================================================
//
// MediaHelper.swift
//
// Siskin IM
// Copyright (C) 2020 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import UIKit
import AVKit
public struct ShareFileInfo {
public let filename: String;
public let suffix: String?;
public var filenameWithSuffix: String {
if let suffix = suffix {
return "\(filename).\(suffix)";
} else {
return filename;
}
}
public init(filename: String, suffix: String?) {
self.filename = filename;
if suffix?.isEmpty ?? true {
self.suffix = nil;
} else {
self.suffix = suffix;
}
}
public func with(suffix: String?) -> ShareFileInfo {
return .init(filename: filename, suffix: suffix);
}
public func with(filename: String) -> String {
if let suffix = self.suffix {
return "\(filename).\(suffix)";
}
return filename;
}
public static func from(url: URL, defaultSuffix: String?) -> ShareFileInfo {
let name = url.lastPathComponent;
var startOffset = name.startIndex;
if name.hasPrefix("trim.") {
startOffset = name.index(startOffset, offsetBy: "trim.".count);
}
if let idx = name.lastIndex(of: "."), idx > startOffset {
return ShareFileInfo(filename: String(name[startOffset..<idx]), suffix: String(name[name.index(after: idx)..<name.endIndex]));
} else {
return ShareFileInfo(filename: String(name[startOffset..<name.endIndex]), suffix: defaultSuffix);
}
}
}
open class MediaHelper {
public static func compressImage(url: URL, fileInfo: ShareFileInfo, quality: ImageQuality, completionHandler: @escaping (Result<(URL,ShareFileInfo),ShareError>)->Void) {
guard quality != .original else {
let tempUrl = FileManager.default.temporaryDirectory.appendingPathComponent(fileInfo.with(filename: UUID().uuidString));
do {
try FileManager.default.copyItem(at: url, to: tempUrl);
} catch {
completionHandler(.failure(.noAccessError))
return;
}
completionHandler(.success((tempUrl, fileInfo)));
return;
}
guard let inData = try? Data(contentsOf: url), let image = UIImage(data: inData) else {
completionHandler(.failure(.notSupported));
return;
}
compressImage(image: image, fileInfo: fileInfo, quality: quality, completionHandler: completionHandler);
}
public static func compressImage(image: UIImage, fileInfo: ShareFileInfo, quality: ImageQuality, completionHandler: @escaping(Result<(URL,ShareFileInfo),ShareError>)->Void) {
let newFileInfo = fileInfo.with(suffix: "jpg");
let fileUrl = FileManager.default.temporaryDirectory.appendingPathComponent(newFileInfo.filenameWithSuffix, isDirectory: false);
guard let outData = image.scaled(maxWidthOrHeight: quality.size)?.jpegData(compressionQuality: quality.quality) else {
return;
}
do {
try outData.write(to: fileUrl);
completionHandler(.success((fileUrl,newFileInfo)));
} catch {
completionHandler(.failure(.noAccessError));
return;
}
}
public static func compressMovie(url: URL, fileInfo: ShareFileInfo, quality: VideoQuality, progressCallback: @escaping (Float)->Void, completionHandler: @escaping (Result<(URL,ShareFileInfo),Error>)->Void) {
guard quality != .original else {
let tempUrl = FileManager.default.temporaryDirectory.appendingPathComponent(fileInfo.with(filename: UUID().uuidString));
do {
try FileManager.default.copyItem(at: url, to: tempUrl);
} catch {
completionHandler(.failure(ShareError.noAccessError))
return;
}
completionHandler(.success((tempUrl,fileInfo)));
return;
}
let video = AVAsset(url: url);
let exportSession = AVAssetExportSession(asset: video, presetName: quality.preset)!;
exportSession.shouldOptimizeForNetworkUse = true;
exportSession.outputFileType = .mp4;
let newFileInfo = fileInfo.with(suffix: "mp4");
let fileUrl = FileManager.default.temporaryDirectory.appendingPathComponent(newFileInfo.filenameWithSuffix, isDirectory: false);
exportSession.outputURL = fileUrl;
let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in
progressCallback(exportSession.progress);
})
exportSession.exportAsynchronously {
timer.invalidate();
if let error = exportSession.error {
completionHandler(.failure(error));
} else {
completionHandler(.success((fileUrl,newFileInfo)));
}
}
}
}
public enum ShareError: Error, LocalizedError {
case unknownError
case noAccessError
case noFileSizeError
case noMimeTypeError
case notSupported
case fileTooBig
case httpError
case invalidResponseCode(url: URL)
public var message: String {
switch self {
case .invalidResponseCode:
return NSLocalizedString("Server did not confirm file upload correctly.", comment: "sharing error")
case .unknownError:
return NSLocalizedString("Please try again later.", comment: "sharing error")
case .noAccessError:
return NSLocalizedString("It was not possible to access the file.", comment: "sharing error")
case .noFileSizeError:
return NSLocalizedString("Could not retrieve file size.", comment: "sharing error")
case .noMimeTypeError:
return NSLocalizedString("Could not detect MIME type of a file.", comment: "sharing error")
case .notSupported:
return NSLocalizedString("Feature not supported by XMPP server", comment: "sharing error")
case .fileTooBig:
return NSLocalizedString("File is too big to share", comment: "sharing error")
case .httpError:
return NSLocalizedString("Upload to HTTP server failed.", comment: "sharing error")
}
}
}
================================================
FILE: Shared/util/VideoQuality.swift
================================================
//
// VideoQuality.swift
//
// Siskin IM
// Copyright (C) 2020 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import AVKit
public enum VideoQuality: String {
case original
case high
case medium
case low
public var label: String {
switch self {
case .original:
return NSLocalizedString("Original", comment: "video quality")
case .high:
return NSLocalizedString("High", comment: "video quality")
case .medium:
return NSLocalizedString("Medium", comment: "video quality")
case .low:
return NSLocalizedString("Low", comment: "video quality")
}
}
public var preset: String {
switch self {
case .original:
return AVAssetExportPresetPassthrough;
case .high:
return AVAssetExportPresetHighestQuality;
case .medium:
return AVAssetExportPresetMediumQuality;
case .low:
return AVAssetExportPresetLowQuality;
}
}
}
================================================
FILE: Shared/util/crypto/Cipher+AES.swift
================================================
//
// Cipher+AES.swift
//
// Siskin IM
// Copyright (C) 2019 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import OpenSSL
import TigaseLogging
open class Cipher {
}
extension Cipher {
open class AES_GCM {
private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "aesgcm")
public init() {
}
public static func generateKey(ofSize: Int) -> Data? {
var key = Data(count: ofSize/8);
let result = key.withUnsafeMutableBytes({ (ptr: UnsafeMutableRawBufferPointer) -> Int32 in
return SecRandomCopyBytes(kSecRandomDefault, ofSize/8, ptr.baseAddress!);
});
guard result == errSecSuccess else {
AES_GCM.logger.error("failed to generated AES encryption key: \(result)");
return nil;
}
return key;
}
open func encrypt(iv: Data, key: Data, message data: Data, output: UnsafeMutablePointer<Data>?, tag: UnsafeMutablePointer<Data>?) -> Bool {
let ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, key.count == 32 ? EVP_aes_256_gcm() : EVP_aes_128_gcm(), nil, nil, nil);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, Int32(iv.count), nil);
iv.withUnsafeBytes({ (ivBytes: UnsafeRawBufferPointer) -> Void in
key.withUnsafeBytes({ (keyBytes: UnsafeRawBufferPointer) -> Void in
EVP_EncryptInit_ex(ctx, nil, nil, keyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), ivBytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
})
});
EVP_CIPHER_CTX_set_padding(ctx, 1);
var outbuf = Array(repeating: UInt8(0), count: data.count);
var outbufLen: Int32 = 0;
let encryptedBody = data.withUnsafeBytes { ( bytes) -> Data in
EVP_EncryptUpdate(ctx, &outbuf, &outbufLen, bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(data.count));
return Data(bytes: &outbuf, count: Int(outbufLen));
}
EVP_EncryptFinal_ex(ctx, &outbuf, &outbufLen);
var tagData = Data(count: 16);
tagData.withUnsafeMutableBytes({ (bytes) -> Void in
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, bytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
});
EVP_CIPHER_CTX_free(ctx);
tag?.initialize(to: tagData);
output?.initialize(to: encryptedBody);
return true;
}
open func encrypt(iv: Data, key: Data, provider: CipherDataProvider, consumer: CipherDataConsumer, chunkSize: Int = 512*1024) -> Data {
let ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, key.count == 32 ? EVP_aes_256_gcm() : EVP_aes_128_gcm(), nil, nil, nil);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, Int32(iv.count), nil);
iv.withUnsafeBytes({ (ivBytes: UnsafeRawBufferPointer) -> Void in
key.withUnsafeBytes({ (keyBytes: UnsafeRawBufferPointer) -> Void in
EVP_EncryptInit_ex(ctx, nil, nil, keyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), ivBytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
})
});
EVP_CIPHER_CTX_set_padding(ctx, 1);
var ended: Bool = false;
var buffer = Data(count: chunkSize * 2);
repeat {
switch provider.chunk(size: chunkSize) {
case .data(let data):
let result = data.withUnsafeBytes { ( bytes) -> Data in
let wrote = buffer.withUnsafeMutableBytes { (outbuf) -> Int in
var outbufLen: Int32 = 0;
EVP_EncryptUpdate(ctx, outbuf.baseAddress!.assumingMemoryBound(to: UInt8.self), &outbufLen, bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(data.count));
return Int(outbufLen)
}
return buffer.subdata(in: 0..<wrote);
}
_ = consumer.consume(data: result);
case .ended:
buffer.withUnsafeMutableBytes { (outbuf) -> Void in
var outbufLen: Int32 = 0;
EVP_EncryptFinal_ex(ctx, outbuf.baseAddress!.assumingMemoryBound(to: UInt8.self), &outbufLen);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, outbuf.baseAddress!.assumingMemoryBound(to: UInt8.self));
}
ended = true;
}
} while !ended;
return buffer.subdata(in: 0..<16);
}
open func decrypt(iv: Data, key: Data, encoded payload: Data, auth tag: Data?, output: UnsafeMutablePointer<Data>?) -> Bool {
let ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, key.count == 32 ? EVP_aes_256_gcm() : EVP_aes_128_gcm(), nil, nil, nil);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, Int32(iv.count), nil);
key.withUnsafeBytes({ (keyBytes) -> Void in
iv.withUnsafeBytes({ (ivBytes) -> Void in
EVP_DecryptInit_ex(ctx, nil, nil, keyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), ivBytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
})
})
EVP_CIPHER_CTX_set_padding(ctx, 1);
var auth = tag;
var encoded = payload;
if auth == nil {
auth = payload.subdata(in: (payload.count - 16)..<payload.count);
encoded = payload.subdata(in: 0..<(payload.count-16));
}
var outbuf = Array(repeating: UInt8(0), count: encoded.count);
var outbufLen: Int32 = 0;
let decoded = encoded.withUnsafeBytes({ (bytes) -> Data in
EVP_DecryptUpdate(ctx, &outbuf, &outbufLen, bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(encoded.count));
return Data(bytes: &outbuf, count: Int(outbufLen));
});
if auth != nil {
auth!.withUnsafeMutableBytes({ [count = auth!.count] (bytes: UnsafeMutableRawBufferPointer) -> Void in
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, Int32(count), bytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
});
}
let ret = EVP_DecryptFinal_ex(ctx, &outbuf, &outbufLen);
EVP_CIPHER_CTX_free(ctx);
guard ret >= 0 else {
AES_GCM.logger.error("authentication of encrypted message failed: \(ret)");
return false;
}
output?.initialize(to: decoded);
return true;
}
open func decrypt(iv: Data, key: Data, provider: CipherDataProvider, consumer: CipherDataConsumer, chunkSize: Int = 512 * 1024) -> Bool {
let ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, key.count == 32 ? EVP_aes_256_gcm() : EVP_aes_128_gcm(), nil, nil, nil);
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, Int32(iv.count), nil);
key.withUnsafeBytes({ (keyBytes) -> Void in
iv.withUnsafeBytes({ (ivBytes) -> Void in
EVP_DecryptInit_ex(ctx, nil, nil, keyBytes.baseAddress!.assumingMemoryBound(to: UInt8.self), ivBytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
})
})
EVP_CIPHER_CTX_set_padding(ctx, 1);
var ended: Bool = false;
var buffer = Data(count: chunkSize * 2);
var result = false;
repeat {
switch provider.chunk(size: chunkSize) {
case .data(let data):
let result = data.withUnsafeBytes { ( bytes) -> Data in
let wrote = buffer.withUnsafeMutableBytes { (outbuf) -> Int in
var outbufLen: Int32 = 0;
EVP_DecryptUpdate(ctx, outbuf.baseAddress!.assumingMemoryBound(to: UInt8.self), &outbufLen, bytes.baseAddress!.assumingMemoryBound(to: UInt8.self), Int32(data.count));
return Int(outbufLen)
}
return buffer.subdata(in: 0..<wrote);
}
_ = consumer.consume(data: result);
case .ended:
if var auth = (provider as? CipherDataProviderWithAuth)?.authTag() {
auth.withUnsafeMutableBytes({ [count = auth.count] (bytes: UnsafeMutableRawBufferPointer) -> Void in
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, Int32(count), bytes.baseAddress!.assumingMemoryBound(to: UInt8.self));
});
}
result = buffer.withUnsafeMutableBytes { (outbuf) -> Bool in
var outbufLen: Int32 = 0;
return EVP_DecryptFinal_ex(ctx, outbuf.baseAddress!.assumingMemoryBound(to: UInt8.self), &outbufLen) >= 0;
}
ended = true;
}
} while !ended;
return result;
}
}
public class DataDataProvider: CipherDataProvider {
let data: Data;
private(set) var offset: Int = 0;
public init(data: Data) {
self.data = data;
}
public func chunk(size chunkSize: Int) -> Cipher.DataProviderResult {
guard offset < data.count else {
return .ended;
}
let size = min(chunkSize, data.count - offset);
defer {
offset = offset + size;
}
return .data(data.subdata(in: offset..<(offset + size)));
}
}
public class FileDataProvider: CipherDataProviderWithAuth {
let inputStream: InputStream;
var count: Int = 0;
var limit: Int = 0;
public convenience init(inputStream: InputStream, fileSize: Int, hasAuthTag: Bool) {
if hasAuthTag {
self.init(inputStream: inputStream, limit: fileSize - 16);
} else {
self.init(inputStream: inputStream);
}
}
public init(inputStream: InputStream, limit: Int = Int.max) {
self.limit = limit;
self.inputStream = inputStream;
self.inputStream.open();
}
deinit {
self.inputStream.close();
}
public func chunk(size: Int) -> Cipher.DataProviderResult {
guard inputStream.hasBytesAvailable else {
inputStream.close();
return .ended;
}
let limit = min(size, self.limit - self.count);
guard limit > 0 else {
return .ended;
}
var buf = Array(repeating: UInt8(0), count: limit);
let read = inputStream.read(&buf, maxLength: limit);
guard read > 0 else {
return .ended;
}
count = count + read;
return .data(Data(bytes: &buf, count: read));
}
public func authTag() -> Data? {
guard inputStream.hasBytesAvailable else {
return nil;
}
var buf = Array(repeating: UInt8(0), count: 16);
let read = inputStream.read(&buf, maxLength: 16);
guard read > 0 else {
return nil;
}
return Data(bytes: &buf, count: read);
}
}
public class TempFileConsumer: CipherDataConsumer {
public let url: URL;
private var outputStream: OutputStream?;
public private(set) var size: Int = 0;
public init?() {
self.url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString);
guard let outputStream = OutputStream(url: url, append: true) else {
return nil;
}
self.outputStream = outputStream;
self.outputStream?.open();
guard self.outputStream != nil, self.outputStream!.hasSpaceAvailable else {
return nil;
}
}
public func consume(data: Data) -> Int {
let wrote = data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Int in
return outputStream!.write(ptr.bindMemory(to: UInt8.self).baseAddress!, maxLength: data.count);
}
size = size + wrote;
return wrote;
}
public func close() {
self.outputStream!.close();
}
deinit {
if outputStream != nil {
outputStream?.close();
}
try? FileManager.default.removeItem(at: url);
}
}
public enum DataProviderResult {
case data(Data)
case ended
}
}
public protocol CipherDataProvider {
func chunk(size: Int) -> Cipher.DataProviderResult;
}
public protocol CipherDataProviderWithAuth: CipherDataProvider {
func authTag() -> Data?;
}
public protocol CipherDataConsumer {
func consume(data: Data) -> Int;
}
================================================
FILE: Shared/util/crypto/SSLCertificate.swift
================================================
//
// SSLCertificate.swift
//
// Siskin IM
// Copyright (C) 2021 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import OpenSSL
open class SSLCertificate {
private let ref: OpaquePointer;
init(withOwnedReference ref: OpaquePointer) {
self.ref = ref;
}
deinit {
X509_free(ref);
}
open func derCertificateData() -> Data? {
var buf: UnsafeMutablePointer<UInt8>? = nil;
let len = i2d_X509(self.ref, &buf);
guard len >= 0 else {
return nil;
}
defer {
X509_free(OpaquePointer.init(buf));
}
return Data(bytes: UnsafeRawPointer(buf!), count: Int(len));
}
open func secCertificate() -> SecCertificate? {
guard let data = derCertificateData() else {
return nil;
}
return SecCertificateCreateWithData(nil, data as CFData);
}
open func secTrust() -> SecTrust? {
guard let cert = secCertificate() else {
return nil;
}
var commonName: CFString?;
SecCertificateCopyCommonName(cert, &commonName);
var trust: SecTrust?;
guard SecTrustCreateWithCertificates([cert] as CFArray, SecPolicyCreateBasicX509(), &trust) == errSecSuccess else {
return nil;
}
return trust;
}
}
================================================
FILE: Shared/util/crypto/SSLContext.swift
================================================
//
// SSLContext.swift
//
// Siskin IM
// Copyright (C) 2021 "Tigase, Inc." <office@tigase.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. Look for COPYING file in the top folder.
// If not, see https://www.gnu.org/licenses/.
//
import Foundation
import OpenSSL
open class SSLContext {
private let sslContext: OpaquePointer;
public init?(alpnProtocols: [String] = [], supportedTlsVersions: ClosedRange<SSLProtocol> = SSLProtocol.TLSv1_2...SSLProtocol.TLSv1_3) {
guard let context = SSL_CTX_new(TLS_client_method()) else {
return nil;
}
sslContext = context;
if !alpnProtocols.isEmpty {
var bytes = alpnProtocols.map { SSLContext.protocolToBytes($0) }.reduce(into: [UInt8](repeating: 0, count: 0), { result, value in result.append(contentsOf: value) });
SSL_CTX_set_alpn_protos(context, &bytes, UInt32(bytes.count));
}
var options = UInt(0) | UInt(SSL_OP_NO_SSLv2) | UInt(SSL_OP_NO_SSLv3) | UInt(SSL_OP_NO_COMPRESSION);
for version in SSLProtocol.allCases {
if !supportedTlsVersions.contains(version) {
options = options | UInt(version.ssl_op_no);
}
}
SSL_CTX_set_options(context, options)
gitextract_t2pcpgpq/ ├── .bartycrouch.toml ├── .github/ │ ├── FUNDING.yml │ └── ISSUE_TEMPLATE/ │ ├── bug_report_developer.md │ ├── bug_report_user.md │ ├── feature_request.md │ └── question.md ├── .gitignore ├── COPYING ├── Documentation/ │ ├── css/ │ │ └── docbook-xsl.css │ ├── index.asciidoc │ ├── restructured/ │ │ ├── .readthedocs.yaml │ │ ├── Advanced_Options.rst │ │ ├── Makefile │ │ ├── Tigase_Messenger_iOS.rst │ │ ├── Welcome.rst │ │ ├── conf.py │ │ ├── index.rst │ │ ├── locale/ │ │ │ ├── pl/ │ │ │ │ └── LC_MESSAGES/ │ │ │ │ └── siskin_im_translation.po │ │ │ └── zh_CN/ │ │ │ └── LC_MESSAGES/ │ │ │ └── siskin_im_translation.po │ │ └── make.bat │ └── text/ │ ├── advanced.asciidoc │ ├── interface.asciidoc │ └── welcome.asciidoc ├── NotificationService/ │ ├── Info.plist │ ├── NotificationService.entitlements │ └── NotificationService.swift ├── README.md ├── Shared/ │ ├── Info.plist │ ├── NotificationCategory.swift │ ├── Shared.h │ ├── database/ │ │ ├── ConversationType.swift │ │ └── Database.swift │ ├── notifications/ │ │ ├── ConversationNotifications.swift │ │ ├── NotificationEncryptionKeys.swift │ │ └── NotificationsManagerHelper.swift │ ├── ui/ │ │ └── UIImage.swift │ └── util/ │ ├── HTTPFileUploadHelper.swift │ ├── ImageQuality.swift │ ├── MediaHelper.swift │ ├── VideoQuality.swift │ └── crypto/ │ ├── Cipher+AES.swift │ ├── SSLCertificate.swift │ ├── SSLContext.swift │ └── SSLProcessor.swift ├── SiskinIM/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon-Simple.appiconset/ │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── appLogo.imageset/ │ │ │ └── Contents.json │ │ ├── appearance/ │ │ │ ├── Contents.json │ │ │ ├── chatMessageText.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── chatslistBackground.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── chatslistItemSecondaryLabel.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── chatslistSemiBackground.colorset/ │ │ │ │ └── Contents.json │ │ │ └── tintColor.colorset/ │ │ │ └── Contents.json │ │ ├── audioCall.imageset/ │ │ │ └── Contents.json │ │ ├── defaultAvatar.imageset/ │ │ │ └── Contents.json │ │ ├── defaultGroupchatAvatar.imageset/ │ │ │ └── Contents.json │ │ ├── endCall.imageset/ │ │ │ └── Contents.json │ │ ├── first.imageset/ │ │ │ └── Contents.json │ │ ├── message.fill.imageset/ │ │ │ └── Contents.json │ │ ├── messageArchiving.imageset/ │ │ │ └── Contents.json │ │ ├── mute.imageset/ │ │ │ └── Contents.json │ │ ├── participants.imageset/ │ │ │ └── Contents.json │ │ ├── person.crop.circle.fill.imageset/ │ │ │ └── Contents.json │ │ ├── pushNotifications.imageset/ │ │ │ └── Contents.json │ │ ├── qrCodeBackground.colorset/ │ │ │ └── Contents.json │ │ ├── qrCodeForeground.colorset/ │ │ │ └── Contents.json │ │ ├── second.imageset/ │ │ │ └── Contents.json │ │ ├── switchCamera.imageset/ │ │ │ └── Contents.json │ │ ├── tigaseLogo.imageset/ │ │ │ └── Contents.json │ │ └── videoCall.imageset/ │ │ └── Contents.json │ ├── Info.plist │ ├── SiskinIM-Bridging-Header.h │ ├── bookmarks/ │ │ ├── BookmarkItem.swift │ │ ├── BookmarkViewCell.swift │ │ └── BookmarksController.swift │ ├── channel/ │ │ ├── ChannelBlockedUsersController.swift │ │ ├── ChannelCreateViewController.swift │ │ ├── ChannelEditInfoController.swift │ │ ├── ChannelInviteController.swift │ │ ├── ChannelJoinViewController.swift │ │ ├── ChannelParticipantsController.swift │ │ ├── ChannelSelectAccountAndComponentController.swift │ │ ├── ChannelSelectNewOwnerViewController.swift │ │ ├── ChannelSelectToJoinViewController.swift │ │ ├── ChannelSettingsViewController.swift │ │ ├── ChannelViewController.swift │ │ └── ChannelsHelper.swift │ ├── chat/ │ │ ├── BaseChatViewController+Share.swift │ │ ├── BaseChatViewController+ShareFile.swift │ │ ├── BaseChatViewController+ShareMedia.swift │ │ ├── BaseChatViewController.swift │ │ ├── BaseChatViewControllerWithDataSource.swift │ │ ├── BaseChatViewControllerWithDataSourceContextMenuAndToolbar.swift │ │ ├── ChatAttachementsCellView.swift │ │ ├── ChatAttachementsController.swift │ │ ├── ChatViewController.swift │ │ ├── ChatViewInputBar.swift │ │ ├── ShareLocationController.swift │ │ └── ShareLocationSearchResultsController.swift │ ├── chats/ │ │ ├── ChatsListTableViewCell.swift │ │ └── ChatsListViewController.swift │ ├── contacts/ │ │ ├── ContactBasicTableViewCell.swift │ │ ├── ContactFormTableViewCell.swift │ │ ├── ContactViewController.swift │ │ └── OMEMOIdentityTableViewCell.swift │ ├── conversation/ │ │ ├── AttachmentChatTableViewCell.swift │ │ ├── BaseChatTableViewCell.swift │ │ ├── ChatTableViewCell.swift │ │ ├── ChatTableViewMarkerCell.swift │ │ ├── ChatTableViewSystemCell.swift │ │ ├── ConversationDataSource.swift │ │ ├── ConversationLogController.swift │ │ ├── InvitationChatTableViewCell.swift │ │ ├── LinkPreviewChatTableViewCell.swift │ │ └── LocationChatTableViewCell.swift │ ├── database/ │ │ ├── DBCapabilitiesCache.swift │ │ ├── DBChatHistoryStore.swift │ │ ├── DBChatHistorySyncStore.swift │ │ ├── DBChatMarkersStore.swift │ │ ├── DBChatStore+ChannelStore.swift │ │ ├── DBChatStore+ChatStore.swift │ │ ├── DBChatStore+RoomStore.swift │ │ ├── DBChatStore.swift │ │ ├── DBOMEMOStore.swift │ │ ├── DBRosterStore.swift │ │ ├── DBVCardStore.swift │ │ ├── Database.swift │ │ ├── DatabaseMigrator.swift │ │ ├── MessageState.swift │ │ └── model/ │ │ ├── DisplayableIdProtocol.swift │ │ ├── conversations/ │ │ │ ├── AccountConversations.swift │ │ │ ├── Channel.swift │ │ │ ├── Chat.swift │ │ │ ├── Conversation.swift │ │ │ ├── ConversationBase.swift │ │ │ └── Room.swift │ │ └── history/ │ │ ├── AppendixProtocol.swift │ │ ├── ConversationAttachment.swift │ │ ├── ConversationEntry.swift │ │ ├── ConversationEntryEncryption.swift │ │ ├── ConversationEntryRecipient.swift │ │ ├── ConversationEntrySender.swift │ │ ├── ConversationEntryState.swift │ │ ├── ConversationInvitation.swift │ │ └── ConversationKey.swift │ ├── db-schema-1.sql │ ├── db-schema-10.sql │ ├── db-schema-11.sql │ ├── db-schema-12.sql │ ├── db-schema-13.sql │ ├── db-schema-14.sql │ ├── db-schema-2.sql │ ├── db-schema-3.sql │ ├── db-schema-4.sql │ ├── db-schema-5.sql │ ├── db-schema-6.sql │ ├── db-schema-7.sql │ ├── db-schema-8.sql │ ├── db-schema-9.sql │ ├── groupchat/ │ │ ├── InviteViewController.swift │ │ ├── MucChatOccupantsTableViewCell.swift │ │ ├── MucChatOccupantsTableViewController.swift │ │ ├── MucChatSettingsViewController.swift │ │ └── MucChatViewController.swift │ ├── localization/ │ │ ├── Base.lproj/ │ │ │ ├── Account.storyboard │ │ │ ├── Conversation.storyboard │ │ │ ├── Groupchat.storyboard │ │ │ ├── Info.storyboard │ │ │ ├── LaunchScreen.storyboard │ │ │ ├── MIX.storyboard │ │ │ ├── Main.storyboard │ │ │ ├── Settings.storyboard │ │ │ └── VoIP.storyboard │ │ ├── de.lproj/ │ │ │ ├── Account.strings │ │ │ ├── Conversation.strings │ │ │ ├── Groupchat.strings │ │ │ ├── Info.strings │ │ │ ├── LaunchScreen.strings │ │ │ ├── Localizable.strings │ │ │ ├── MIX.strings │ │ │ ├── Main.strings │ │ │ ├── Settings.strings │ │ │ └── VoIP.strings │ │ ├── en.lproj/ │ │ │ ├── Account.strings │ │ │ ├── Conversation.strings │ │ │ ├── Groupchat.strings │ │ │ ├── Info.strings │ │ │ ├── Localizable.strings │ │ │ ├── MIX.strings │ │ │ ├── Main.strings │ │ │ ├── Settings.strings │ │ │ └── VoIP.strings │ │ ├── es.lproj/ │ │ │ ├── Account.strings │ │ │ ├── Conversation.strings │ │ │ ├── Groupchat.strings │ │ │ ├── Info.strings │ │ │ ├── LaunchScreen.strings │ │ │ ├── Localizable.strings │ │ │ ├── MIX.strings │ │ │ ├── Main.strings │ │ │ ├── Settings.strings │ │ │ └── VoIP.strings │ │ └── pl.lproj/ │ │ ├── Account.strings │ │ ├── Conversation.strings │ │ ├── Groupchat.strings │ │ ├── Info.strings │ │ ├── LaunchScreen.strings │ │ ├── Localizable.strings │ │ ├── MIX.strings │ │ ├── Main.strings │ │ ├── Settings.strings │ │ └── VoIP.strings │ ├── notifications/ │ │ ├── NotificationCenterDelegate.swift │ │ └── NotificationManager.swift │ ├── roster/ │ │ ├── AbstractRosterViewController.swift │ │ ├── RosterItemEditViewController.swift │ │ ├── RosterItemTableViewCell.swift │ │ ├── RosterProvider.swift │ │ ├── RosterProviderFlat.swift │ │ ├── RosterProviderGrouped.swift │ │ └── RosterViewController.swift │ ├── service/ │ │ ├── AvatarEventHandler.swift │ │ ├── BlockedEventHandler.swift │ │ ├── DNSSrvDiskCache.swift │ │ ├── MeetEventHandler.swift │ │ ├── MessageEventHandler.swift │ │ ├── MixEventHandler.swift │ │ ├── MucEventHandler.swift │ │ ├── NewFeaturesDetector.swift │ │ ├── PresenceRosterEventHandler.swift │ │ ├── PushEventHandler.swift │ │ ├── StreamFeaturesCache.swift │ │ ├── XMPPClient_extension.swift │ │ ├── XmppService.swift │ │ └── XmppServiceEventHandler.swift │ ├── settings/ │ │ ├── AccountConnectivitySettingsViewController.swift │ │ ├── AccountDomainTableViewCell.swift │ │ ├── AccountQRCodeController.swift │ │ ├── AccountSettingsViewController.swift │ │ ├── AccountTableViewCell.swift │ │ ├── AddAccountController.swift │ │ ├── BlockedContactsController.swift │ │ ├── ChatSettingsViewController.swift │ │ ├── ContactsSettingsViewController.swift │ │ ├── DeviceMemoryUsageTableViewCell.swift │ │ ├── ExperimentalSettingsViewController.swift │ │ ├── MediaSettingsVIewController.swift │ │ ├── NotificationSettingsViewController.swift │ │ ├── OMEMOFingerprintsController.swift │ │ ├── RegisterAccountController.swift │ │ ├── ServerFeaturesViewController.swift │ │ ├── ServerSelectorTableViewCell.swift │ │ ├── SetAccountSettingsController.swift │ │ ├── SettingsViewController.swift │ │ ├── SetupViewController.swift │ │ └── server_features_list.xml │ ├── ui/ │ │ ├── AboutController.swift │ │ ├── AvatarStatusView.swift │ │ ├── AvatarView.swift │ │ ├── CertificateErrorAlert.swift │ │ ├── ChartView.swift │ │ ├── ChatBottomView.swift │ │ ├── CustomTabBarController.swift │ │ ├── DataFormController.swift │ │ ├── EmptyViewController.swift │ │ ├── EnumTableViewCell.swift │ │ ├── GetInTouchViewController.swift │ │ ├── GlobalSplitViewController.swift │ │ ├── MainTabBarController.swift │ │ ├── Markdown.swift │ │ ├── MessageTextView.swift │ │ ├── NavigationControllerWrappingSegue.swift │ │ ├── RoundButton.swift │ │ ├── StepperTableViewCell.swift │ │ ├── SwitchTableViewCell.swift │ │ ├── TablePicketViewController.swift │ │ └── suggestions/ │ │ └── MultiContactSelectionView.swift │ ├── util/ │ │ ├── AccountManager.swift │ │ ├── AccountManagerScramSaltedPasswordCache.swift │ │ ├── AppStoryboard.swift │ │ ├── Array+IndexChanges.swift │ │ ├── AudioSession.swift │ │ ├── AvatarManager.swift │ │ ├── AvatarStore.swift │ │ ├── ContactManager.swift │ │ ├── CurrentDatePublisher.swift │ │ ├── DownloadManager.swift │ │ ├── DownloadStore.swift │ │ ├── InvitationsManager.swift │ │ ├── MainNotificationManagerProvider.swift │ │ ├── MediaHelper.swift │ │ ├── MessageEncryption.swift │ │ ├── MetadataCache.swift │ │ ├── OSLog.swift │ │ ├── OpenSSL_AES_GCM_Engine.swift │ │ ├── PresenceStore.swift │ │ ├── ServerCertificateInfo.swift │ │ ├── Settings.swift │ │ ├── SiskinPushNotificationsModuleProvider.swift │ │ ├── TasksQueue.swift │ │ ├── UIColor_mix.swift │ │ ├── VCardManager.swift │ │ └── combine/ │ │ ├── Publisher+OnlyGetter.swift │ │ ├── Publisher+ThrottleFixed.swift │ │ └── Publisher+ThrottledSink.swift │ ├── vcard/ │ │ ├── VCardAvatarEditCell.swift │ │ ├── VCardEditAddressTableViewCell.swift │ │ ├── VCardEditEmailTableViewCell.swift │ │ ├── VCardEditPhoneTableViewCell.swift │ │ ├── VCardEditViewController.swift │ │ ├── VCardEntryTypeAwareTableViewCell.swift │ │ └── VCardTextEditCell.swift │ ├── voip/ │ │ ├── CallManager.swift │ │ ├── CameraPreviewView.swift │ │ ├── CreateMeetingViewController.swift │ │ ├── ExternalServiceDiscovery_Service_extension.swift │ │ ├── InviteToMeetingViewController.swift │ │ ├── JingleManager.swift │ │ ├── JingleManager_Session.swift │ │ ├── MeetController.swift │ │ ├── MeetManager.swift │ │ ├── RTCCameraVideoCapturer_Format.swift │ │ └── VideoCallController.swift │ └── xmpp/ │ ├── HttpFileUploadModule.swift │ └── SiskinPushNotificationsModule.swift ├── SiskinIM - Share/ │ ├── Assets.xcassets/ │ │ ├── AppIcon-Simple.appiconset/ │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Info.plist │ ├── ShareViewController.swift │ ├── SiskinIM - Share.entitlements │ └── localization/ │ ├── Base.lproj/ │ │ └── MainInterface.storyboard │ ├── de.lproj/ │ │ └── MainInterface.strings │ ├── en.lproj/ │ │ └── MainInterface.strings │ ├── es.lproj/ │ │ └── MainInterface.strings │ └── pl.lproj/ │ └── MainInterface.strings ├── SiskinIM.entitlements ├── SiskinIM.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata/ │ └── xcschemes/ │ └── NotificationService.xcscheme ├── pom.xml ├── siskin-im.doap ├── swiftScript.swift ├── trim.sh └── update-frameworks.sh
SYMBOL INDEX (38 symbols across 7 files) FILE: SiskinIM/db-schema-1.sql type chats (line 1) | CREATE TABLE IF NOT EXISTS chats ( type chat_history (line 14) | CREATE TABLE IF NOT EXISTS chat_history ( type chat_history_jid_idx (line 28) | CREATE INDEX IF NOT EXISTS chat_history_jid_idx on chats ( type roster_items (line 32) | CREATE TABLE IF NOT EXISTS roster_items ( type roster_groups (line 42) | CREATE TABLE IF NOT EXISTS roster_groups ( type roster_items_groups (line 47) | CREATE TABLE IF NOT EXISTS roster_items_groups ( type vcards_cache (line 54) | CREATE TABLE IF NOT EXISTS vcards_cache ( type vcards_cache_jid_idx (line 61) | CREATE INDEX IF NOT EXISTS vcards_cache_jid_idx on vcards_cache ( type caps_features (line 65) | CREATE TABLE IF NOT EXISTS caps_features ( type caps_features_node_idx (line 70) | CREATE INDEX IF NOT EXISTS caps_features_node_idx on caps_features ( type caps_features_feature_idx (line 74) | CREATE INDEX IF NOT EXISTS caps_features_feature_idx on caps_features ( type caps_identities (line 78) | CREATE TABLE IF NOT EXISTS caps_identities ( type caps_indentities_node_idx (line 85) | CREATE INDEX IF NOT EXISTS caps_indentities_node_idx on caps_identities ( type avatars_cache (line 89) | CREATE TABLE IF NOT EXISTS avatars_cache ( type avatars_cache_jid_idx (line 97) | CREATE INDEX IF NOT EXISTS avatars_cache_jid_idx on avatars_cache ( FILE: SiskinIM/db-schema-11.sql type chat_history_account_jid_server_msg_id_idx (line 7) | CREATE INDEX IF NOT EXISTS chat_history_account_jid_server_msg_id_idx on... FILE: SiskinIM/db-schema-13.sql type chat_history_sync (line 1) | CREATE TABLE IF NOT EXISTS chat_history_sync ( FILE: SiskinIM/db-schema-14.sql type chat_markers (line 1) | CREATE TABLE IF NOT EXISTS chat_markers ( type chat_markers_key (line 11) | CREATE UNIQUE INDEX IF NOT EXISTS chat_markers_key on chat_markers ( FILE: SiskinIM/db-schema-2.sql type chats (line 3) | CREATE TABLE IF NOT EXISTS chats ( type chat_jid_idx (line 24) | CREATE INDEX IF NOT EXISTS chat_jid_idx on chats ( type chat_history (line 30) | CREATE TABLE IF NOT EXISTS chat_history ( type chat_history_account_jid_timestamp_idx (line 53) | CREATE INDEX IF NOT EXISTS chat_history_account_jid_timestamp_idx on cha... type chat_history_account_jid_state_idx (line 57) | CREATE INDEX IF NOT EXISTS chat_history_account_jid_state_idx on chat_hi... type roster_items (line 64) | CREATE TABLE IF NOT EXISTS roster_items ( type roster_items_groups (line 80) | CREATE TABLE IF NOT EXISTS roster_items_groups ( type roster_item_jid_idx (line 99) | CREATE INDEX IF NOT EXISTS roster_item_jid_idx on roster_items ( type roster_item_groups_item_id_idx (line 103) | CREATE INDEX IF NOT EXISTS roster_item_groups_item_id_idx ON roster_item... type roster_item_groups_group_id_idx (line 104) | CREATE INDEX IF NOT EXISTS roster_item_groups_group_id_idx ON roster_ite... type vcards_cache (line 108) | CREATE TABLE IF NOT EXISTS vcards_cache ( type vcards_cache_jid_idx (line 123) | CREATE INDEX IF NOT EXISTS vcards_cache_jid_idx on vcards_cache ( type avatars_cache (line 129) | CREATE TABLE IF NOT EXISTS avatars_cache ( type avatars_cache_jid_idx (line 145) | CREATE INDEX IF NOT EXISTS avatars_cache_jid_idx on avatars_cache ( FILE: SiskinIM/db-schema-5.sql type omemo_sessions (line 1) | CREATE TABLE IF NOT EXISTS omemo_sessions ( type omemo_identities (line 10) | CREATE TABLE IF NOT EXISTS omemo_identities ( type omemo_pre_keys (line 22) | CREATE TABLE IF NOT EXISTS omemo_pre_keys ( type omemo_signed_pre_keys (line 30) | CREATE TABLE IF NOT EXISTS omemo_signed_pre_keys ( FILE: SiskinIM/db-schema-8.sql type chats_read (line 1) | CREATE TABLE IF NOT EXISTS chats_read (
Condensed preview — 348 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,261K chars).
[
{
"path": ".bartycrouch.toml",
"chars": 471,
"preview": "[update]\ntasks = [\"interfaces\", \"code\", \"normalize\"]\n\n[update.interfaces]\npaths = [\".\"]\ndefaultToBase = true\nignoreEmpty"
},
{
"path": ".github/FUNDING.yml",
"chars": 64,
"preview": "# These are supported funding model platforms\n\ngithub: [tigase]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report_developer.md",
"chars": 730,
"preview": "---\nname: Bug report (Developer)\nabout: Reports from app developers\ntitle: ''\nlabels: 'bug'\nassignees: ''\n\n---\n\n**Descri"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report_user.md",
"chars": 694,
"preview": "---\nname: Bug report (App user)\nabout: Reports for app download from App Store\ntitle: ''\nlabels: 'bug'\nassignees: ''\n\n--"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 609,
"preview": "---\nname: Feature request\nabout: Suggest an idea to improve SiskinIM\ntitle: ''\nlabels: 'enhancement'\nassignees: ''\n\n---\n"
},
{
"path": ".github/ISSUE_TEMPLATE/question.md",
"chars": 387,
"preview": "---\nname: General question\nabout: Just a question\ntitle: ''\nlabels: 'question'\nassignees: ''\n\n---\n\n**I have a problem wi"
},
{
"path": ".gitignore",
"chars": 2765,
"preview": "docs\nFrameworks\n\n#########################\n# **.gitignore** file for Xcode4 / OS X Source projects\n#\n# NB: if you are st"
},
{
"path": "COPYING",
"chars": 35148,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "Documentation/css/docbook-xsl.css",
"chars": 5738,
"preview": "/*\n CSS stylesheet for XHTML produced by DocBook XSL stylesheets.\n*/\n\nbody {\n font-family: Georgia,serif;\n}\n\ncode, pre"
},
{
"path": "Documentation/index.asciidoc",
"chars": 277,
"preview": "= Tigase Messenger for iOS\nTigase Team <team@tigase.net>\n:toc:\n:numbered:\n:website: http://tigase.net\n:Date: 2017-04-10\n"
},
{
"path": "Documentation/restructured/.readthedocs.yaml",
"chars": 226,
"preview": "# Required\nversion: 2\n\n# Set the version of Python and other tools you might need\nbuild:\n os: ubuntu-22.04\n tools:\n "
},
{
"path": "Documentation/restructured/Advanced_Options.rst",
"chars": 2345,
"preview": "Advanced Options\r\n=================\r\n\r\nThis section contains information about advanced settings and options that are av"
},
{
"path": "Documentation/restructured/Makefile",
"chars": 632,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the "
},
{
"path": "Documentation/restructured/Tigase_Messenger_iOS.rst",
"chars": 7289,
"preview": "Tigase Messenger for iOS Interface\r\n======================================\r\n\r\nThe menu interface for Tigase Messenger fo"
},
{
"path": "Documentation/restructured/Welcome.rst",
"chars": 3694,
"preview": "Welcome\r\n========\r\n\r\nWelcome to the documentation for Siskin IM for iOS.\r\n\r\nSiskin IM has some nice feature:\r\n\r\nencrypte"
},
{
"path": "Documentation/restructured/conf.py",
"chars": 2544,
"preview": "# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common op"
},
{
"path": "Documentation/restructured/index.rst",
"chars": 233,
"preview": "\n==========================================\nTigase Messenger for iOS - Version 1.0\n====================================="
},
{
"path": "Documentation/restructured/locale/pl/LC_MESSAGES/siskin_im_translation.po",
"chars": 19270,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as th"
},
{
"path": "Documentation/restructured/locale/zh_CN/LC_MESSAGES/siskin_im_translation.po",
"chars": 23897,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as th"
},
{
"path": "Documentation/restructured/make.bat",
"chars": 799,
"preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sp"
},
{
"path": "Documentation/text/advanced.asciidoc",
"chars": 1982,
"preview": "[[iOS_Advanced]]\n= Advanced Options\n:author: Tigase Team <team@tigase.net>\n\n:toc:\n:numbered:\n:website: http://tigase.net"
},
{
"path": "Documentation/text/interface.asciidoc",
"chars": 11146,
"preview": "[[Interface]]\n= Tigase Messenger for iOS Interface\n:author: Tigase Team <team@tigase.net>\n\n:toc:\n:numbered:\n:website: ht"
},
{
"path": "Documentation/text/welcome.asciidoc",
"chars": 3314,
"preview": "[[Welcome]]\n= Welcome\n:author: Tigase Team <team@tigase.net>\n\n:toc:\n:numbered:\n:website: http://tigase.net\n\nWelcome to t"
},
{
"path": "NotificationService/Info.plist",
"chars": 1076,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "NotificationService/NotificationService.entitlements",
"chars": 346,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "NotificationService/NotificationService.swift",
"chars": 12817,
"preview": "//\n// NotificationService.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This prog"
},
{
"path": "README.md",
"chars": 2050,
"preview": "<p align=\"center\">\n <a href=\"https://siskin.im/\">\n <img\n alt=\"SiskinIM\"\n src=\"https://siskin.im/img/servic"
},
{
"path": "Shared/Info.plist",
"chars": 752,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Shared/NotificationCategory.swift",
"chars": 1239,
"preview": "//\n// NotificationCategory.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pro"
},
{
"path": "Shared/Shared.h",
"chars": 1143,
"preview": "//\n// Shared.h\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free softw"
},
{
"path": "Shared/database/ConversationType.swift",
"chars": 911,
"preview": "//\n// ConversationType.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program"
},
{
"path": "Shared/database/Database.swift",
"chars": 2364,
"preview": "//\n// Database.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free"
},
{
"path": "Shared/notifications/ConversationNotifications.swift",
"chars": 921,
"preview": "//\n// ConversationNotifications.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "Shared/notifications/NotificationEncryptionKeys.swift",
"chars": 1238,
"preview": "//\n// NotificationEncryptionKeys.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Th"
},
{
"path": "Shared/notifications/NotificationsManagerHelper.swift",
"chars": 9290,
"preview": "//\n// NotificationsManagerHelper.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Th"
},
{
"path": "Shared/ui/UIImage.swift",
"chars": 2073,
"preview": "//\n// UIImage.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free "
},
{
"path": "Shared/util/HTTPFileUploadHelper.swift",
"chars": 4055,
"preview": "//\n// HTTPFileUploadHelper.swift\n//\n// Siskin IM\n// Copyright (C) 2017 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pro"
},
{
"path": "Shared/util/ImageQuality.swift",
"chars": 2125,
"preview": "//\n// ImageQuality.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "Shared/util/MediaHelper.swift",
"chars": 7048,
"preview": "//\n// MediaHelper.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is f"
},
{
"path": "Shared/util/VideoQuality.swift",
"chars": 1739,
"preview": "//\n// VideoQuality.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "Shared/util/crypto/Cipher+AES.swift",
"chars": 14521,
"preview": "//\n// Cipher+AES.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is fr"
},
{
"path": "Shared/util/crypto/SSLCertificate.swift",
"chars": 2073,
"preview": "//\n// SSLCertificate.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program i"
},
{
"path": "Shared/util/crypto/SSLContext.swift",
"chars": 2518,
"preview": "//\n// SSLContext.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is fr"
},
{
"path": "Shared/util/crypto/SSLProcessor.swift",
"chars": 14501,
"preview": "//\n// SSLProcessor.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "SiskinIM/AppDelegate.swift",
"chars": 35358,
"preview": "//\n// AppDelegate.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is f"
},
{
"path": "SiskinIM/Assets.xcassets/AppIcon-Simple.appiconset/Contents.json",
"chars": 2411,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"scale\" : \"2x\",\n \"size\" : \"20x20\"\n },\n {\n \"idiom\""
},
{
"path": "SiskinIM/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 2411,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"scale\" : \"2x\",\n \"size\" : \"20x20\"\n },\n {\n \"idiom\""
},
{
"path": "SiskinIM/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "SiskinIM/Assets.xcassets/appLogo.imageset/Contents.json",
"chars": 302,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"1024.png\",\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n "
},
{
"path": "SiskinIM/Assets.xcassets/appearance/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "SiskinIM/Assets.xcassets/appearance/chatMessageText.colorset/Contents.json",
"chars": 602,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n },\n \"colors\" : [\n {\n \"idiom\" : \"universal\",\n \"c"
},
{
"path": "SiskinIM/Assets.xcassets/appearance/chatslistBackground.colorset/Contents.json",
"chars": 700,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n },\n \"colors\" : [\n {\n \"idiom\" : \"universal\",\n \"c"
},
{
"path": "SiskinIM/Assets.xcassets/appearance/chatslistItemSecondaryLabel.colorset/Contents.json",
"chars": 603,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"color-space\" : \"gray-gamma-22\",\n \"components\" : {\n \"al"
},
{
"path": "SiskinIM/Assets.xcassets/appearance/chatslistSemiBackground.colorset/Contents.json",
"chars": 707,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"color-space\" : \"display-p3\",\n \"components\" : {\n \"alpha"
},
{
"path": "SiskinIM/Assets.xcassets/appearance/tintColor.colorset/Contents.json",
"chars": 704,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"color-space\" : \"display-p3\",\n \"components\" : {\n \"alpha"
},
{
"path": "SiskinIM/Assets.xcassets/audioCall.imageset/Contents.json",
"chars": 380,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/defaultAvatar.imageset/Contents.json",
"chars": 893,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"avatar-light.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "SiskinIM/Assets.xcassets/defaultGroupchatAvatar.imageset/Contents.json",
"chars": 913,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"groupchat-avatar-light.png\",\n \"scale\" : \"1x"
},
{
"path": "SiskinIM/Assets.xcassets/endCall.imageset/Contents.json",
"chars": 413,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/first.imageset/Contents.json",
"chars": 154,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"first.pdf\"\n }\n ],\n \"info\" : {\n \"version\""
},
{
"path": "SiskinIM/Assets.xcassets/message.fill.imageset/Contents.json",
"chars": 397,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/messageArchiving.imageset/Contents.json",
"chars": 284,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"icons8-communication.pdf\"\n }\n ],\n \"info\" : "
},
{
"path": "SiskinIM/Assets.xcassets/mute.imageset/Contents.json",
"chars": 392,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/participants.imageset/Contents.json",
"chars": 387,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/person.crop.circle.fill.imageset/Contents.json",
"chars": 400,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/pushNotifications.imageset/Contents.json",
"chars": 289,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"icons8-push-notifications.pdf\"\n }\n ],\n \"inf"
},
{
"path": "SiskinIM/Assets.xcassets/qrCodeBackground.colorset/Contents.json",
"chars": 534,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"platform\" : \"ios\",\n \"reference\" : \"systemBackgroundColor\"\n "
},
{
"path": "SiskinIM/Assets.xcassets/qrCodeForeground.colorset/Contents.json",
"chars": 689,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"color-space\" : \"srgb\",\n \"components\" : {\n \"alpha\" : \"1"
},
{
"path": "SiskinIM/Assets.xcassets/second.imageset/Contents.json",
"chars": 155,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"second.pdf\"\n }\n ],\n \"info\" : {\n \"version"
},
{
"path": "SiskinIM/Assets.xcassets/switchCamera.imageset/Contents.json",
"chars": 396,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Assets.xcassets/tigaseLogo.imageset/Contents.json",
"chars": 319,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"filename\" : \"tigase_logo_10"
},
{
"path": "SiskinIM/Assets.xcassets/videoCall.imageset/Contents.json",
"chars": 386,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"scale\" : \"1x\"\n },\n {\n \"idiom\" : \"universal\",\n "
},
{
"path": "SiskinIM/Info.plist",
"chars": 3128,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "SiskinIM/SiskinIM-Bridging-Header.h",
"chars": 1075,
"preview": "//\n// SiskinIM-Bridging-Header.h\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pro"
},
{
"path": "SiskinIM/bookmarks/BookmarkItem.swift",
"chars": 1314,
"preview": "//\n// BookmarkItem.swift\n//\n// Siskin IM\n// Copyright (C) 2022 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "SiskinIM/bookmarks/BookmarkViewCell.swift",
"chars": 1837,
"preview": "//\n// BookmarkViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2022 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program"
},
{
"path": "SiskinIM/bookmarks/BookmarksController.swift",
"chars": 8456,
"preview": "//\n// BookmarksController.swift\n//\n// Siskin IM\n// Copyright (C) 2022 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This prog"
},
{
"path": "SiskinIM/channel/ChannelBlockedUsersController.swift",
"chars": 4354,
"preview": "//\n// ChannelBlockedUsersController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n//"
},
{
"path": "SiskinIM/channel/ChannelCreateViewController.swift",
"chars": 8793,
"preview": "//\n// ChannelCreateViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// T"
},
{
"path": "SiskinIM/channel/ChannelEditInfoController.swift",
"chars": 8171,
"preview": "//\n// ChannelEditInfoController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "SiskinIM/channel/ChannelInviteController.swift",
"chars": 5231,
"preview": "//\n// ChannelInviteController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This "
},
{
"path": "SiskinIM/channel/ChannelJoinViewController.swift",
"chars": 23521,
"preview": "//\n// ChannelJoinViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "SiskinIM/channel/ChannelParticipantsController.swift",
"chars": 8670,
"preview": "//\n// ChannelParticipantsController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n//"
},
{
"path": "SiskinIM/channel/ChannelSelectAccountAndComponentController.swift",
"chars": 2847,
"preview": "//\n// ChannelSelectAccountAndComponentController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tiga"
},
{
"path": "SiskinIM/channel/ChannelSelectNewOwnerViewController.swift",
"chars": 2299,
"preview": "//\n// ChannelSelectNewOwnerViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2022 \"Tigase, Inc.\" <office@tigase.com>"
},
{
"path": "SiskinIM/channel/ChannelSelectToJoinViewController.swift",
"chars": 14906,
"preview": "//\n// ChannelJoinViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "SiskinIM/channel/ChannelSettingsViewController.swift",
"chars": 9829,
"preview": "//\n// ChannelSettingsViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n//"
},
{
"path": "SiskinIM/channel/ChannelViewController.swift",
"chars": 7046,
"preview": "//\n// ChannelViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/channel/ChannelsHelper.swift",
"chars": 6864,
"preview": "//\n// ChannelsHelper.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program i"
},
{
"path": "SiskinIM/chat/BaseChatViewController+Share.swift",
"chars": 11096,
"preview": "//\n// BaseChatViewController+Share.swift\n//\n// Siskin IM\n// Copyright (C) 2017 \"Tigase, Inc.\" <office@tigase.com>\n//\n// "
},
{
"path": "SiskinIM/chat/BaseChatViewController+ShareFile.swift",
"chars": 3233,
"preview": "//\n// BaseChatViewController+ShareFile.swift\n//\n// Siskin IM\n// Copyright (C) 2017 \"Tigase, Inc.\" <office@tigase.com>\n//"
},
{
"path": "SiskinIM/chat/BaseChatViewController+ShareMedia.swift",
"chars": 11237,
"preview": "//\n// BaseChatViewController+ShareMedia.swift\n//\n// Siskin IM\n// Copyright (C) 2017 \"Tigase, Inc.\" <office@tigase.com>\n/"
},
{
"path": "SiskinIM/chat/BaseChatViewController.swift",
"chars": 11757,
"preview": "//\n// BaseChatViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/chat/BaseChatViewControllerWithDataSource.swift",
"chars": 1307,
"preview": "//\n// BaseChatViewControllerWithDataSource.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com"
},
{
"path": "SiskinIM/chat/BaseChatViewControllerWithDataSourceContextMenuAndToolbar.swift",
"chars": 11011,
"preview": "//\n// BaseChatViewControllerWithDataSourceAndContextMenuAndToolbar.swift\n//\n// Siskin IM\n// Copyright (C) 2017 \"Tigase, "
},
{
"path": "SiskinIM/chat/ChatAttachementsCellView.swift",
"chars": 5459,
"preview": "//\n// ChatAttachementsCellView.swift\n// Siskin IM\n//\n// Created by Andrzej Wójcik on 03/01/2020.\n// Copyright © 2020"
},
{
"path": "SiskinIM/chat/ChatAttachementsController.swift",
"chars": 3739,
"preview": "//\n// ChatAttachementsController.swift\n// Siskin IM\n//\n// Created by Andrzej Wójcik on 03/01/2020.\n// Copyright © 20"
},
{
"path": "SiskinIM/chat/ChatViewController.swift",
"chars": 12917,
"preview": "//\n// ChatViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This progr"
},
{
"path": "SiskinIM/chat/ChatViewInputBar.swift",
"chars": 20141,
"preview": "//\n// ChatViewInputBar.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program"
},
{
"path": "SiskinIM/chat/ShareLocationController.swift",
"chars": 12615,
"preview": "//\n// ShareLocationController.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This "
},
{
"path": "SiskinIM/chat/ShareLocationSearchResultsController.swift",
"chars": 2842,
"preview": "//\n// ShareLocationSearchResultsController.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com"
},
{
"path": "SiskinIM/chats/ChatsListTableViewCell.swift",
"chars": 9635,
"preview": "//\n// ChatsListTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/chats/ChatsListViewController.swift",
"chars": 40394,
"preview": "//\n// ChatListViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/contacts/ContactBasicTableViewCell.swift",
"chars": 2505,
"preview": "//\n// ContactBasicTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "SiskinIM/contacts/ContactFormTableViewCell.swift",
"chars": 1241,
"preview": "//\n// ContactFormTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This"
},
{
"path": "SiskinIM/contacts/ContactViewController.swift",
"chars": 19979,
"preview": "//\n// ContactViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/contacts/OMEMOIdentityTableViewCell.swift",
"chars": 1176,
"preview": "//\n// OMEMOIdentityTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Th"
},
{
"path": "SiskinIM/conversation/AttachmentChatTableViewCell.swift",
"chars": 34363,
"preview": "//\n// AttachmentChatTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// T"
},
{
"path": "SiskinIM/conversation/BaseChatTableViewCell.swift",
"chars": 10158,
"preview": "//\n// BaseChatTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/conversation/ChatTableViewCell.swift",
"chars": 7047,
"preview": "//\n// ChatTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This progra"
},
{
"path": "SiskinIM/conversation/ChatTableViewMarkerCell.swift",
"chars": 2391,
"preview": "//\n// ChatTableViewMarkerCell.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This "
},
{
"path": "SiskinIM/conversation/ChatTableViewSystemCell.swift",
"chars": 1860,
"preview": "//\n// ChatTableViewSystemCell.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This "
},
{
"path": "SiskinIM/conversation/ConversationDataSource.swift",
"chars": 13743,
"preview": "//\n// ConversationDataSource.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/conversation/ConversationLogController.swift",
"chars": 19703,
"preview": "//\n// ConversationLogController.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "SiskinIM/conversation/InvitationChatTableViewCell.swift",
"chars": 3906,
"preview": "//\n// InvitationChatTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// T"
},
{
"path": "SiskinIM/conversation/LinkPreviewChatTableViewCell.swift",
"chars": 3610,
"preview": "//\n// LinkPreviewChatTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// "
},
{
"path": "SiskinIM/conversation/LocationChatTableViewCell.swift",
"chars": 2223,
"preview": "//\n// LocationChatTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Thi"
},
{
"path": "SiskinIM/database/DBCapabilitiesCache.swift",
"chars": 5292,
"preview": "//\n// DBCapabilitiesCache.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This prog"
},
{
"path": "SiskinIM/database/DBChatHistoryStore.swift",
"chars": 66904,
"preview": "//\n// DBChatHistoryStore.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This progr"
},
{
"path": "SiskinIM/database/DBChatHistorySyncStore.swift",
"chars": 7412,
"preview": "//\n// DBChatHistorySyncStore.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/database/DBChatMarkersStore.swift",
"chars": 8978,
"preview": "//\n// DBChatMarkersStore.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This progr"
},
{
"path": "SiskinIM/database/DBChatStore+ChannelStore.swift",
"chars": 2657,
"preview": "//\n// DBChatStore+ChannelStore.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This"
},
{
"path": "SiskinIM/database/DBChatStore+ChatStore.swift",
"chars": 2326,
"preview": "//\n// DBChatStore+ChatStore.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/database/DBChatStore+RoomStore.swift",
"chars": 2456,
"preview": "//\n// DBChatStore+RoomStore.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/database/DBChatStore.swift",
"chars": 20073,
"preview": "//\n// DBChatStore.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is f"
},
{
"path": "SiskinIM/database/DBOMEMOStore.swift",
"chars": 23819,
"preview": "//\n// DBOMEMOStore.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "SiskinIM/database/DBRosterStore.swift",
"chars": 9648,
"preview": "//\n// DBRosterStore.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is"
},
{
"path": "SiskinIM/database/DBVCardStore.swift",
"chars": 3088,
"preview": "//\n// DBVCardStore.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "SiskinIM/database/Database.swift",
"chars": 1453,
"preview": "//\n// Database.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free"
},
{
"path": "SiskinIM/database/DatabaseMigrator.swift",
"chars": 9257,
"preview": "//\n// DatabaseMigrator.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program"
},
{
"path": "SiskinIM/database/MessageState.swift",
"chars": 2058,
"preview": "//\n// MessageState.swift\n//\n// Siskin IM\n// Copyright (C) 2020 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "SiskinIM/database/model/DisplayableIdProtocol.swift",
"chars": 1445,
"preview": "//\n// DisplayableIdProtocol.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/database/model/conversations/AccountConversations.swift",
"chars": 2927,
"preview": "//\n// AccountConversations.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pro"
},
{
"path": "SiskinIM/database/model/conversations/Channel.swift",
"chars": 16944,
"preview": "//\n// Channel.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free "
},
{
"path": "SiskinIM/database/model/conversations/Chat.swift",
"chars": 16168,
"preview": "//\n// Chat.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free sof"
},
{
"path": "SiskinIM/database/model/conversations/Conversation.swift",
"chars": 7159,
"preview": "//\n// Conversation.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is "
},
{
"path": "SiskinIM/database/model/conversations/ConversationBase.swift",
"chars": 7156,
"preview": "//\n// ConversationBase.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program"
},
{
"path": "SiskinIM/database/model/conversations/Room.swift",
"chars": 26460,
"preview": "//\n// Room.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program is free sof"
},
{
"path": "SiskinIM/database/model/history/AppendixProtocol.swift",
"chars": 919,
"preview": "//\n// AppendixProtocol.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program"
},
{
"path": "SiskinIM/database/model/history/ConversationAttachment.swift",
"chars": 2390,
"preview": "//\n// ConversationAttachment.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/database/model/history/ConversationEntry.swift",
"chars": 6692,
"preview": "//\n// ConversationEntry.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This progra"
},
{
"path": "SiskinIM/database/model/history/ConversationEntryEncryption.swift",
"chars": 2356,
"preview": "//\n// ConversationEntryEncryption.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// T"
},
{
"path": "SiskinIM/database/model/history/ConversationEntryRecipient.swift",
"chars": 1104,
"preview": "//\n// ConversationEntryRecipient.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// Th"
},
{
"path": "SiskinIM/database/model/history/ConversationEntrySender.swift",
"chars": 3478,
"preview": "//\n// ConversationEntrySender.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This "
},
{
"path": "SiskinIM/database/model/history/ConversationEntryState.swift",
"chars": 4750,
"preview": "//\n// ConversationEntryState.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/database/model/history/ConversationInvitation.swift",
"chars": 2723,
"preview": "//\n// ConversationInvitation.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This p"
},
{
"path": "SiskinIM/database/model/history/ConversationKey.swift",
"chars": 1328,
"preview": "//\n// ConversationKey.swift\n//\n// Siskin IM\n// Copyright (C) 2021 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This program "
},
{
"path": "SiskinIM/db-schema-1.sql",
"chars": 2170,
"preview": "CREATE TABLE IF NOT EXISTS chats (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n account TEXT NOT NULL,\n jid TEXT NOT"
},
{
"path": "SiskinIM/db-schema-10.sql",
"chars": 61,
"preview": "ALTER TABLE chat_history ADD COLUMN recipient_nickname TEXT;\n"
},
{
"path": "SiskinIM/db-schema-11.sql",
"chars": 343,
"preview": "ALTER TABLE roster_items ADD COLUMN annotations TEXT;\n\nALTER TABLE chat_history ADD COLUMN server_msg_id TEXT;\nALTER TAB"
},
{
"path": "SiskinIM/db-schema-12.sql",
"chars": 180,
"preview": "ALTER TABLE chat_history ADD COLUMN master_id INT;\nALTER TABLE chat_history ADD COLUMN correction_stanza_id TEXT;\nALTER "
},
{
"path": "SiskinIM/db-schema-13.sql",
"chars": 253,
"preview": "CREATE TABLE IF NOT EXISTS chat_history_sync (\n id TEXT NOT NULL COLLATE NOCASE,\n account TEXT NOT NULL COLLATE NO"
},
{
"path": "SiskinIM/db-schema-14.sql",
"chars": 399,
"preview": "CREATE TABLE IF NOT EXISTS chat_markers (\n account TEXT NOT NULL COLLATE NOCASE,\n jid TEXT NOT NULL COLLATE NOCASE"
},
{
"path": "SiskinIM/db-schema-2.sql",
"chars": 3836,
"preview": "ALTER TABLE chats RENAME TO chats_old;\n\nCREATE TABLE IF NOT EXISTS chats (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n "
},
{
"path": "SiskinIM/db-schema-3.sql",
"chars": 49,
"preview": "ALTER TABLE chats ADD COLUMN message_draft TEXT;\n"
},
{
"path": "SiskinIM/db-schema-4.sql",
"chars": 40,
"preview": "ALTER TABLE chats ADD COLUMN name TEXT;\n"
},
{
"path": "SiskinIM/db-schema-5.sql",
"chars": 1073,
"preview": "CREATE TABLE IF NOT EXISTS omemo_sessions (\n account TEXT NOT NULL COLLATE NOCASE,\n name TEXT NOT NULL,\n device"
},
{
"path": "SiskinIM/db-schema-6.sql",
"chars": 43,
"preview": "ALTER TABLE chats ADD COLUMN options TEXT;\n"
},
{
"path": "SiskinIM/db-schema-7.sql",
"chars": 307,
"preview": "UPDATE chat_history SET state = 11 WHERE state = 5;\nUPDATE chat_history SET state = 5 WHERE state = 9;\nUPDATE chat_histo"
},
{
"path": "SiskinIM/db-schema-8.sql",
"chars": 173,
"preview": "CREATE TABLE IF NOT EXISTS chats_read (\n account TEXT NOT NULL COLLATE NOCASE,\n jid TEXT NOT NULL COLLATE NOCASE,\n"
},
{
"path": "SiskinIM/db-schema-9.sql",
"chars": 51,
"preview": "ALTER TABLE chat_history ADD COLUMN appendix TEXT;\n"
},
{
"path": "SiskinIM/groupchat/InviteViewController.swift",
"chars": 3055,
"preview": "//\n// InviteViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pro"
},
{
"path": "SiskinIM/groupchat/MucChatOccupantsTableViewCell.swift",
"chars": 3691,
"preview": "//\n// MucChatOccupantsTableViewCell.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n//"
},
{
"path": "SiskinIM/groupchat/MucChatOccupantsTableViewController.swift",
"chars": 13036,
"preview": "//\n// MucChatOccupantsTableViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>"
},
{
"path": "SiskinIM/groupchat/MucChatSettingsViewController.swift",
"chars": 24383,
"preview": "//\n// MucChatSettingsViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2019 \"Tigase, Inc.\" <office@tigase.com>\n//\n//"
},
{
"path": "SiskinIM/groupchat/MucChatViewController.swift",
"chars": 10982,
"preview": "//\n// MucChatViewController.swift\n//\n// Siskin IM\n// Copyright (C) 2016 \"Tigase, Inc.\" <office@tigase.com>\n//\n// This pr"
},
{
"path": "SiskinIM/localization/Base.lproj/Account.storyboard",
"chars": 157215,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/Conversation.storyboard",
"chars": 63752,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/Groupchat.storyboard",
"chars": 55622,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/Info.storyboard",
"chars": 27346,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/LaunchScreen.storyboard",
"chars": 7376,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/MIX.storyboard",
"chars": 125471,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/Main.storyboard",
"chars": 144929,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/Settings.storyboard",
"chars": 157387,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/Base.lproj/VoIP.storyboard",
"chars": 19490,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "SiskinIM/localization/de.lproj/Account.strings",
"chars": 6588,
"preview": "/* Class = \"UILabel\"; text = \"add email\"; ObjectID = \"06Y-Cg-0Q3\"; */\n\"06Y-Cg-0Q3.text\" = \"E-Mail hinzufügen\";\n\n/* Class"
},
{
"path": "SiskinIM/localization/de.lproj/Conversation.strings",
"chars": 213,
"preview": "/* Class = \"UIButton\"; normalTitle = \"Accept\"; ObjectID = \"7TV-Kq-bdP\"; */\n\"7TV-Kq-bdP.normalTitle\" = \"Akzeptieren\";\n\n/*"
},
{
"path": "SiskinIM/localization/de.lproj/Groupchat.strings",
"chars": 1566,
"preview": "/* Class = \"UILabel\"; text = \"Notifications\"; ObjectID = \"02f-aw-3Zd\"; */\n\"02f-aw-3Zd.text\" = \"Benachrichtigungen\";\n\n/* "
},
{
"path": "SiskinIM/localization/de.lproj/Info.strings",
"chars": 806,
"preview": "/* Class = \"UITableViewSection\"; headerTitle = \"Application\"; ObjectID = \"1Kk-Xc-9Ce\"; */\n\"1Kk-Xc-9Ce.headerTitle\" = \"An"
},
{
"path": "SiskinIM/localization/de.lproj/LaunchScreen.strings",
"chars": 1,
"preview": "\n"
},
{
"path": "SiskinIM/localization/de.lproj/Localizable.strings",
"chars": 35471,
"preview": "/* section label, device memory */\n\"%@ memory\" = \"%@ Speicher\";\n\n/* no. of lines of messages preview label */\n\"%d lines "
},
{
"path": "SiskinIM/localization/de.lproj/MIX.strings",
"chars": 5690,
"preview": "/* Class = \"UITextField\"; placeholder = \"Automatic\"; ObjectID = \"19A-3H-7QN\"; */\n\"19A-3H-7QN.placeholder\" = \"Automatisch"
},
{
"path": "SiskinIM/localization/de.lproj/Main.strings",
"chars": 3195,
"preview": "/* Class = \"UILabel\"; text = \"by Tigase, Inc.\"; ObjectID = \"8ba-yZ-XRA\"; */\n\"8ba-yZ-XRA.text\" = \"von Tigase, Inc.\";\n\n/* "
},
{
"path": "SiskinIM/localization/de.lproj/Settings.strings",
"chars": 5129,
"preview": "/* Class = \"UINavigationController\"; title = \"Settings\"; ObjectID = \"15C-WY-qrp\"; */\n\"15C-WY-qrp.title\" = \"Einstellungen"
},
{
"path": "SiskinIM/localization/de.lproj/VoIP.strings",
"chars": 481,
"preview": "/* Class = \"UITableViewSection\"; footerTitle = \"Select which account should be used to join channel\"; ObjectID = \"0d6-UW"
},
{
"path": "SiskinIM/localization/en.lproj/Account.strings",
"chars": 6409,
"preview": "/* Class = \"UILabel\"; text = \"add email\"; ObjectID = \"06Y-Cg-0Q3\"; */\n\"06Y-Cg-0Q3.text\" = \"add email\";\n\n/* Class = \"UILa"
},
{
"path": "SiskinIM/localization/en.lproj/Conversation.strings",
"chars": 208,
"preview": "/* Class = \"UIButton\"; normalTitle = \"Accept\"; ObjectID = \"7TV-Kq-bdP\"; */\n\"7TV-Kq-bdP.normalTitle\" = \"Accept\";\n\n/* Clas"
},
{
"path": "SiskinIM/localization/en.lproj/Groupchat.strings",
"chars": 1530,
"preview": "/* Class = \"UILabel\"; text = \"Notifications\"; ObjectID = \"02f-aw-3Zd\"; */\n\"02f-aw-3Zd.text\" = \"Notifications\";\n\n/* Class"
},
{
"path": "SiskinIM/localization/en.lproj/Info.strings",
"chars": 803,
"preview": "/* Class = \"UITableViewSection\"; headerTitle = \"Application\"; ObjectID = \"1Kk-Xc-9Ce\"; */\n\"1Kk-Xc-9Ce.headerTitle\" = \"Ap"
},
{
"path": "SiskinIM/localization/en.lproj/Localizable.strings",
"chars": 33447,
"preview": "/* section label, device memory */\n\"%@ memory\" = \"%@ memory\";\n\n/* no. of lines of messages preview label */\n\"%d lines of"
},
{
"path": "SiskinIM/localization/en.lproj/MIX.strings",
"chars": 5529,
"preview": "/* Class = \"UITextField\"; placeholder = \"Automatic\"; ObjectID = \"19A-3H-7QN\"; */\n\"19A-3H-7QN.placeholder\" = \"Automatic\";"
},
{
"path": "SiskinIM/localization/en.lproj/Main.strings",
"chars": 3132,
"preview": "/* Class = \"UILabel\"; text = \"by Tigase, Inc.\"; ObjectID = \"8ba-yZ-XRA\"; */\n\"8ba-yZ-XRA.text\" = \"by Tigase, Inc.\";\n\n/* C"
},
{
"path": "SiskinIM/localization/en.lproj/Settings.strings",
"chars": 4967,
"preview": "/* Class = \"UINavigationController\"; title = \"Settings\"; ObjectID = \"15C-WY-qrp\"; */\n\"15C-WY-qrp.title\" = \"Settings\";\n\n/"
},
{
"path": "SiskinIM/localization/en.lproj/VoIP.strings",
"chars": 459,
"preview": "/* Class = \"UITableViewSection\"; footerTitle = \"Select which account should be used to join channel\"; ObjectID = \"0d6-UW"
},
{
"path": "SiskinIM/localization/es.lproj/Account.strings",
"chars": 6580,
"preview": "/* Class = \"UILabel\"; text = \"add email\"; ObjectID = \"06Y-Cg-0Q3\"; */\n\"06Y-Cg-0Q3.text\" = \"añadir correo electrónico\";\n\n"
},
{
"path": "SiskinIM/localization/es.lproj/Conversation.strings",
"chars": 212,
"preview": "/* Class = \"UIButton\"; normalTitle = \"Accept\"; ObjectID = \"7TV-Kq-bdP\"; */\n\"7TV-Kq-bdP.normalTitle\" = \"Aceptar\";\n\n/* Cla"
},
{
"path": "SiskinIM/localization/es.lproj/Groupchat.strings",
"chars": 1572,
"preview": "/* Class = \"UILabel\"; text = \"Notifications\"; ObjectID = \"02f-aw-3Zd\"; */\n\"02f-aw-3Zd.text\" = \"Notificaciones\";\n\n/* Clas"
},
{
"path": "SiskinIM/localization/es.lproj/Info.strings",
"chars": 802,
"preview": "/* Class = \"UITableViewSection\"; headerTitle = \"Application\"; ObjectID = \"1Kk-Xc-9Ce\"; */\n\"1Kk-Xc-9Ce.headerTitle\" = \"Ap"
},
{
"path": "SiskinIM/localization/es.lproj/LaunchScreen.strings",
"chars": 1,
"preview": "\n"
},
{
"path": "SiskinIM/localization/es.lproj/Localizable.strings",
"chars": 34892,
"preview": "/* section label, device memory */\n\"%@ memory\" = \"%@ memoria\";\n\n/* no. of lines of messages preview label */\n\"%d lines o"
}
]
// ... and 148 more files (download for full content)
About this extraction
This page contains the full source code of the tigase/siskin-im GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 348 files (2.9 MB), approximately 790.7k tokens, and a symbol index with 38 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.