Compare commits

..
37 Commits
Author SHA1 Message Date
smizrahi be3630d009 fix(mail): Keep content of Outlook downlevel-revealed conditional comments. Fixes #6167. 2026-10-02 17:25:02 +02:00
Hivert Quentin 6c93d6b66c fix(mail): fix the sort value 2026-09-21 10:21:26 +02:00
Hivert Quentin aa7f82c09d fix(doc): typos 2026-09-14 17:28:06 +02:00
Hivert Quentin c0a83f5c35 chore(release): 5.12.11 2026-09-14 11:22:21 +02:00
Hivert Quentin 1bf1031ab2 i18n(hu): Update Hungarian translations 2026-09-14 11:12:34 +02:00
Hivert Quentin 012e6eff47 fix(junk): extend list of junk icon available 2026-09-14 10:56:28 +02:00
Hivert Quentin 04a3e98238 fix(pwd): use the proper url for reset password mail part2 2026-09-10 08:11:23 +02:00
Hivert Quentin 3d9853e95b chore(js/css): update generated files 2026-09-10 01:01:19 -04:00
Hivert Quentin 382118a93b fix(pwd): use the proper url for reset password mail 2026-09-07 16:26:09 +02:00
Hivert Quentin 3f4f949664 feat(junk): add junk action on mail view 2026-09-04 18:13:36 +02:00
Hivert Quentin 089b50c0de fix(db): release db channel after using them part 2 2026-09-04 11:30:42 +02:00
Hivert Quentin 152f44b90e fix(db): release db channel after using them 2026-09-03 17:09:44 +02:00
Hivert Quentin d089c4046f fix(UI): use legitimate sorting value 2026-09-03 14:42:51 +02:00
Hivert Quentin 10dc173349 fix(sanitization): update regex for html sanitization 2026-09-03 10:51:45 +02:00
WoodySlum 7ce69e3ea7 Merge pull request #395 from kah-ja/fix/imip-card-template-injection
fix(mail): stop interpreting mail data in compiled part viewers
2026-08-27 11:04:41 +02:00
Jan Kahmen 045a0b9c0a fix(mail): escape mail data placed in attributes of a compiled part
the generic attribute writer used for these two spots does not escape what it
writes, so a value taken from the message ends the attribute and starts a new
one. both are inside a part that is compiled, which is where an injected
handler runs.

* the organizer link took inEvent.organizer.email verbatim. a value such as
  mailto:x@y" onpointerover="... produced a live handler on the anchor, and an
  entity encoded scheme such as javascript: reached the href, where the
  browser decodes it. the href is now built as mailto: plus the parsed address
  and escaped as an attribute value.

* the attachment name paragraph of the image and the link viewer took
  filenameForDisplay verbatim. a quote inside an RFC 2231 encoded filename
  ended the title attribute. the writer escapes & < > there but not the quote,
  so the accessor drops the quote instead of escaping it, which keeps a plain
  filename such as A&B.pdf unchanged in the tooltip.

the img title of the image viewer is left alone: attributes of that element are
escaped by the framework already.
2026-08-17 23:48:41 +02:00
Jan Kahmen 8813677ecb fix(core): keep the @import cleanup working in stringWithoutHTMLInjection
the trailing while loop reuses the regex variable, which by then points at the
angular brace pattern whenever stripAngular is YES. that pattern has two
capture groups while the loop substitutes $1@im****$3, so NSRegularExpression
raises NSInvalidArgumentException and the NS_HANDLER swallows it.

give the @import pattern its own variable and use it in both the replacement
and the loop. the replacement also used the template of the block above, which
dropped the whole style element instead of masking the @import in place, so
test_stringWithoutHTMLInjection failed. it passes again, and a case with
stripAngular YES covers the loop.
2026-08-17 23:03:41 +02:00
Jan Kahmen 0d66d7503d fix(mail): stop interpreting iMIP card text fields as markup
the whole server built html of an iMIP card is rendered through sg-compile
(Message.service.js sets part.compile for UIxMailPartICalViewer), and the user
comment and the description are emitted with escapeHTML="NO". event data from
the invitation is therefore both an angular template and raw html.

47133fdf3 closed the description against interpolation with ng-non-bindable.
this closes both paths for every remaining field:

* ng-non-bindable on the user comment, the location, the organizer common name
  and the raw body shown when the calendar cannot be parsed
* the user comment and the description are read through accessors that escape
  them, so the url detection and insertBR produce the only markup left in the
  output

the marker sits on the leaf containers, not on md-card-content, so the buttons,
the delegation autocomplete and the attendee chips keep being compiled. url
detection still turns a bare url in a comment or a description into a link.
2026-08-17 23:03:41 +02:00
Hivert Quentin f7fce46371 chore(release): 5.12.10 2026-08-13 10:17:59 +02:00
Hivert Quentin 2a8f20e080 i18n(hu): Update Hungarian translations 2026-08-13 10:03:55 +02:00
Hivert Quentin 41e2b7b1ab fix(bug): escape angularJS only if needed to keep legitimate directive 2026-08-12 10:53:51 +02:00
Hivert Quentin f0c23407bf chore(sendmail): disable sendmail for now 2026-08-11 17:34:50 +02:00
Hivert Quentin 1caa25711f fix(bug): incorrect method called 2026-08-11 17:06:27 +02:00
Hivert Quentin a156f4ce9f fix(passwordRecoveyr): get the domain from the user mail 2026-08-10 19:35:31 +02:00
Hivert Quentin 47133fdf3b fix(mail): escape angularJS directive for description 2026-08-10 19:34:25 +02:00
Hivert Quentin 78807d01ae fix(dav): properly parse MKCOL request 2026-08-10 19:31:32 +02:00
smizrahi aefc14b265 feat(sogo-tool): add init-user command to create a user's personal folders and preferences without a login 2026-07-15 15:03:37 +02:00
Vincent Bonamy a99a848064 Revert cleanup of usesCASAuthentication on c3234882eb - useful for CAS-specific frontend recovery logic 2026-06-15 08:56:23 +02:00
Hivert Quentin 3a78a3bbb4 fix(calendar): do not transmit og http status to external calendar 2026-06-09 10:26:58 +02:00
smizrahi f11f34cdef fix(mail): escape References/Organization/Newsgroups headers in HTML forward so message-id chevrons aren't parsed as HTML tags hiding the quoted body. Closes #6046. 2026-06-04 11:53:58 +02:00
smizrahi 39a35830e9 fix(ckeditor): Sync resized image width/height into style and HTML attributes so Outlook and other mail clients ignoring CSS aspect-ratio render the correct dimensions. Closes #6169. 2026-06-04 11:53:58 +02:00
smizrahi 6904911ac1 fix(mail): Some links broken in HTML rendering due to libxml2 (SOPE) only supporting HTML4 — when an anchor has no content between its opening and closing tags, store it and re-open it around the next element, closing </a> after that element's end tag. Closes #5961. Closes #6172. 2026-06-04 11:53:58 +02:00
Hivert Quentin fbd5bd19c6 Merge branch 'master' of github.com:Alinto/sogo 2026-05-29 01:00:09 -04:00
Hivert Quentin 97bbd7ed7f fix(tool): wrong method called 2026-05-28 14:57:59 +02:00
Hivert Quentin b5c83f5839 fix(event): clean import of event 2026-05-28 13:27:08 +02:00
Hivert Quentin b978ceb020 Merge branch 'master' of github.com:Alinto/sogo 2026-05-28 01:00:08 -04:00
Hivert Quentin b18f1a09f5 fix(event): clean import of event 2026-05-26 10:30:02 +02:00
72 changed files with 1282 additions and 428 deletions

No files matched your search

+1 -1
View File
@@ -15,9 +15,9 @@
* *
* **************************************************************************/
/* Database configuration (mysql://, postgresql:// or oracle://) */
WOPort = "0.0.0.0:50000";
/* Database configuration (mysql://, postgresql:// or oracle://) */
// MySQL
SOGoProfileURL = "mysql://sogobuild:sogo123@mariadb:3306/sogo/sogo_user_profile";
+54
View File
@@ -2,6 +2,60 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [5.12.11](https://github.com/Alinto/sogo/compare/SOGo-5.12.10...SOGo-5.12.11) (2026-09-14)
### Features
* **junk:** add junk action on mail view ([3f4f949](https://github.com/Alinto/sogo/commit/3f4f9496644953010a4a429f90eae733a50d61c4))
### Bug Fixes
* **core:** keep the [@import](https://github.com/import) cleanup working in stringWithoutHTMLInjection ([8813677](https://github.com/Alinto/sogo/commit/8813677ecb171e5d27cd582989c1d87b308c6df3))
* **db:** release db channel after using them ([152f44b](https://github.com/Alinto/sogo/commit/152f44b90e08013b5c132473a2de249682d997df))
* **db:** release db channel after using them part 2 ([089b50c](https://github.com/Alinto/sogo/commit/089b50c0defdb48e8c0bc0a13334591f3ee2dd11))
* **junk:** extend list of junk icon available ([012e6ef](https://github.com/Alinto/sogo/commit/012e6eff471893efaac033c69ddf7838ca51b791))
* **mail:** escape mail data placed in attributes of a compiled part ([045a0b9](https://github.com/Alinto/sogo/commit/045a0b9c0ac57aeea1b619f281acff0427cd81c7)), closes [#115](https://bugs.sogo.nu/view.php?id=115)
* **mail:** stop interpreting iMIP card text fields as markup ([0d66d75](https://github.com/Alinto/sogo/commit/0d66d7503d8fd147842e87b9b4f1b921986ef44b))
* **pwd:** use the proper url for reset password mail ([382118a](https://github.com/Alinto/sogo/commit/382118a93b6925de2ce7f774abc1865ebea2dbba))
* **pwd:** use the proper url for reset password mail part2 ([04a3e98](https://github.com/Alinto/sogo/commit/04a3e9823889acaf6c247b224f5f7a0108f8f829))
* **sanitization:** update regex for html sanitization ([10dc173](https://github.com/Alinto/sogo/commit/10dc1733496fb2f393537d864d83408e357f2215))
* **UI:** use legitimate sorting value ([d089c40](https://github.com/Alinto/sogo/commit/d089c4046ffac8124874c504491db9d1f15ae650))
### Localization
* **hu:** Update Hungarian translations ([1bf1031](https://github.com/Alinto/sogo/commit/1bf1031ab2932f196f1adcc8fdfc9dea7dfb1f65))
## [5.12.10](https://github.com/Alinto/sogo/compare/SOGo-5.12.9...SOGo-5.12.10) (2026-08-13)
### Features
* **sogo-tool:** add init-user command to create a user's personal folders and preferences without a login ([aefc14b](https://github.com/Alinto/sogo/commit/aefc14b265172e5ff94fa0fc1e4799f05d8e29f8))
### Bug Fixes
* **bug:** escape angularJS only if needed to keep legitimate directive ([41e2b7b](https://github.com/Alinto/sogo/commit/41e2b7b1ab59fefe48cedf49ba82f4e03b646d23))
* **bug:** incorrect method called ([1caa257](https://github.com/Alinto/sogo/commit/1caa25711f5891135c7ebca63f33b8758c00c8e8))
* **calendar:** do not transmit og http status to external calendar ([3a78a3b](https://github.com/Alinto/sogo/commit/3a78a3bbb44484c95c141b0f3c0c8fabdc8bcf6a))
* **ckeditor:** Sync resized image width/height into style and HTML attributes so Outlook and other mail clients ignoring CSS aspect-ratio render the correct dimensions. Closes [#6169](https://bugs.sogo.nu/view.php?id=6169). ([39a3583](https://github.com/Alinto/sogo/commit/39a35830e9d01a26b533515a20c1b4f527064519))
* **dav:** properly parse MKCOL request ([78807d0](https://github.com/Alinto/sogo/commit/78807d01aee9195b95e2ed21344f260aadb2faef))
* **event:** clean import of event ([b5c83f5](https://github.com/Alinto/sogo/commit/b5c83f58393e31151939d0d6fc227c0d0f127f33))
* **event:** clean import of event ([b18f1a0](https://github.com/Alinto/sogo/commit/b18f1a09f59424a36f6de5aa7c30e6f27405c15d))
* **mail:** escape angularJS directive for description ([47133fd](https://github.com/Alinto/sogo/commit/47133fdf3bc3148d374bbf413f058d714776e832))
* **mail:** escape References/Organization/Newsgroups headers in HTML forward so message-id chevrons aren't parsed as HTML tags hiding the quoted body. Closes [#6046](https://bugs.sogo.nu/view.php?id=6046). ([f11f34c](https://github.com/Alinto/sogo/commit/f11f34cdefdd4175ae6c8b0814c2ccaecfd7c373))
* **mail:** Some links broken in HTML rendering due to libxml2 (SOPE) only supporting HTML4 — when an anchor has no content between its opening and closing tags, store it and re-open it around the next element, closing </a> after that element's end tag. Closes [#5961](https://bugs.sogo.nu/view.php?id=5961). Closes [#6172](https://bugs.sogo.nu/view.php?id=6172). ([6904911](https://github.com/Alinto/sogo/commit/6904911ac17651de754246aff26fbb85aa8e01c7))
* **passwordRecoveyr:** get the domain from the user mail ([a156f4c](https://github.com/Alinto/sogo/commit/a156f4ce9f0eda3ff54633d4bcb7051388d2f39b))
* **tool:** wrong method called ([97bbd7e](https://github.com/Alinto/sogo/commit/97bbd7ed7f8173894f01b259f2f0f539ad6e95ed))
### Localization
* **hu:** Update Hungarian translations ([2a8f20e](https://github.com/Alinto/sogo/commit/2a8f20e080cdb39544622874a12fef807cc66612))
### [5.12.9](https://github.com/Alinto/sogo/compare/SOGo-5.12.8...SOGo-5.12.9) (2026-05-27)
+52 -6
View File
@@ -72,7 +72,7 @@ following:
* Database server (MariaDB, MySQL, PostgreSQL or Oracle)
* LDAP server (OpenLDAP, Novell eDirectory, Microsoft Active Directory
and others) 
* SMTP server (Postfix, Sendmail and others)
* SMTP server (Postfix)
* IMAP server (Courier, Cyrus IMAP Server, Dovecot and others)
If you plan to use ActiveSync, an IMAP server supporting the ACL,
@@ -840,6 +840,16 @@ Obiously, if your users can connect without specifying a domain, let this parame
|S |SOGoPasswordRecoveryEnabled
|Boolean enable password recovery with secret question or secondary e-mail. Default value is `NO`.
|S |SOGoPasswordRecoveryBaseURLs
| *Since 5.12.11*. List of allowed base URL for password reset link.
`SOGoPasswordRecoveryBaseURLs = ("https://demo.sogo.nu");`
`SOGoPasswordRecoveryBaseURLs = ("https://platform1.sogo.nu", "https://platform2.sogo.nu);`
Do not end the URLs by '/'.
No defaults, will throw an error if empty or unset.
|S |SOGoPasswordRecoveryDomains
|List of domains where password recovery is enabled, for example `(example.org, example.net)`. If empty array `()`, disabled for all domains. If not set, enabled for all domains.
@@ -2174,7 +2184,6 @@ The following table describes the related parameters.
are:
[options="compact"]
* `sendmail` - to use the sendmail binary
* `smtp` - to use the SMTP protocol
|D |SOGoSMTPServer
@@ -2202,10 +2211,6 @@ Current, Are supported `PLAIN` and 'xoauth2' for openid.
|D |SOGoSMTPMasterUserPassword
|SMTP account password for master account (`SOGoSMTPMasterUserEnabled` enabled).
|S |WOSendMail
|The path of the sendmail binary.
Defaults to `/usr/lib/sendmail`.
|D |SOGoForceExternalLoginWithEmail
|Parameter used to specify if, when logging in to the SMTP server, the
@@ -2891,6 +2896,20 @@ SOGoMailJunkSettings = {
};
----
|D |mailJunkIcon
|Choose the Junk icon. Values are :
https://materialui.co/icon/thumb-down[thumb_down],
https://materialui.co/icon/gpp-bad[gpp_bad],
https://materialui.co/icon/local-fire-department[local_fire_department],
https://materialui.co/icon/bug-report[bug_report],
https://materialui.co/icon/error[error],
https://materialui.co/icon/warning[warning],
https://materialui.co/icon/dangerous[dangerous] and
https://materialui.co/icon/report-gmailerrorred[report_gmailerrorred].
Defaults to `thumb_down`.
|D |SOGoMailKeepDraftsAfterSend
|Parameter used to keep mails in the drafts folder once they have been
sent by SOGo. Defaults to `NO` when unset.
@@ -3765,6 +3784,30 @@ sogo-tool create-folder user2 Calendar Pro_Calendar
----
=== sogo-tool init-user
Create the personal calendar and the personal address book of one or more users, and initialize
their preferences. Those folders and preferences are otherwise only created upon the user's first
login, which prevents any operation on them (setting ACLs or reading the user's preferences from a
provisioning tool, for instance) beforehand.
The resulting preferences are the same as the ones a login through the web interface would produce.
init-user [-l language] user1 [user2] ...
* *user* is the name of the user
* *language* is the language of the user's preferences, as it would be chosen at login. When
omitted, the language of the domain or of the system is used. The categories and labels stored in
the preferences are localized accordingly.
Example:
----
sogo-tool init-user user1
sogo-tool init-user user1 user2
sogo-tool init-user -l French user1
----
=== sogo-tool dump-defaults
Output all current defaults value of GNUstep and SOGo (sogo.conf)
@@ -4265,6 +4308,9 @@ current version of SOGo from the previous release.
[cols="100a"]
|=======================================================================
h|5.12.11
|If you enable Password Recovery, new mandatory settings `SOGoPasswordRecoveryBaseURLs`
h|5.11
|Parameters `SOGoGlobalAddressBookFirstEntries` and `SOGoGlobalAddressBookFirstEntriesCount` has been removed. Please use `listRequiresDot` and `globalAddressBookFirstEntriesCount` instead. Signature are now between a div tag to avoid CKEditor changing the content - set `SOGoForceRawHtmlSignature` to `NO` to leave signature (https://bugs.sogo.nu/view.php?id=5920).
+128 -75
View File
@@ -32,10 +32,12 @@
#import <NGExtensions/NSObject+Logs.h>
#import <GDLAccess/EOAdaptorContext.h>
#import <GDLAccess/EOAdaptor.h>
#import <NGExtensions/NGResourceLocator.h>
#import "GCSChannelManager.h"
#import "EOAdaptorChannel+GCS.h"
#import "EOQualifier+GCS.h"
#import "GCSAlarmsFolder.h"
#import "GCSAdminFolder.h"
#import "GCSOpenIdFolder.h"
@@ -383,21 +385,12 @@ static BOOL _singleStoreMode = NO;
return [[self channelManager] canConnect:[self folderInfoLocation]];
}
- (NSArray *)performSQL:(NSString *)_sql {
EOAdaptorChannel *channel;
- (NSArray *)performSQL:(NSString *)_sql withChannel: (EOAdaptorChannel*) channel{
NSException *ex;
NSMutableArray *rows;
NSDictionary *row;
NSArray *attrs;
/* acquire channel */
if ((channel = [self acquireOpenChannel]) == nil) {
if (debugOn) [self debugWithFormat:@"could not acquire channel!"];
return nil;
}
if (debugOn) [self debugWithFormat:@"acquired channel: %@", channel];
/* run SQL */
if ((ex = [channel evaluateExpressionX:_sql]) != nil) {
@@ -513,9 +506,12 @@ static BOOL _singleStoreMode = NO;
- (NSString *)generateSQLWhereForInternalNames:(NSArray *)_names
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
withChannel: (EOAdaptorChannel*) channel
{
/* generates a WHERE qualifier for matching the "quick" entries */
NSMutableString *sql;
EOAdaptorContext *adaptorContext;
EOAdaptor *adaptor;
unsigned i, count;
if ((count = [_names count]) == 0) {
@@ -523,10 +519,13 @@ static BOOL _singleStoreMode = NO;
__PRETTY_FUNCTION__];
return @"1 = 2";
}
sql = [NSMutableString stringWithCapacity:(count * 8)];
adaptorContext = [channel adaptorContext];
adaptor = [adaptorContext adaptor];
sql = [NSMutableString stringWithString:@""];
for (i = 0; i < quickPathCount; i++) {
NSString *pathColumn;
EOQualifier *qualifier;
char buf[32];
sprintf(buf, GCSPathColumnPattern, (i + 1));
@@ -537,8 +536,11 @@ static BOOL _singleStoreMode = NO;
if (i < count) {
/* exact match, regular column */
if ([sql length] > 0) [sql appendString:@" AND "];
[sql appendString:pathColumn];
[sql appendFormat:@" = '%@'", [_names objectAtIndex:i]];
qualifier = [[EOKeyValueQualifier alloc] initWithKey: pathColumn
operatorSelector: EOQualifierOperatorEqual
value: [_names objectAtIndex:i]];
[qualifier appendSQLToString: sql withAdaptor: adaptor];
}
else if (_beExact) {
/* exact match, ensure that all additional quick-cols are NULL */
@@ -569,9 +571,12 @@ static BOOL _singleStoreMode = NO;
}
if (_beExact && (count > quickPathCount)) {
[sql appendString:@" AND c_foldername = '"];
[sql appendString:[_names lastObject]];
[sql appendString:@"'"];
EOQualifier *qualifier;
[sql appendString:@" AND "];
qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"c_foldername"
operatorSelector: EOQualifierOperatorEqual
value: [_names lastObject]];
[qualifier appendSQLToString: sql withAdaptor: adaptor];
}
return sql;
@@ -579,13 +584,15 @@ static BOOL _singleStoreMode = NO;
- (NSString *)generateSQLPathFetchForInternalNames:(NSArray *)_names
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
withChannel: (EOAdaptorChannel*) channel
{
/* fetches the 'path' subset for a given quick-names */
NSMutableString *sql;
NSString *ws;
ws = [self generateSQLWhereForInternalNames:_names
exactMatch:_beExact orDirectSubfolderMatch:_directSubs];
exactMatch:_beExact orDirectSubfolderMatch:_directSubs
withChannel: channel];
if ([ws length] == 0)
return nil;
@@ -600,13 +607,15 @@ static BOOL _singleStoreMode = NO;
- (NSString *)generateSQLPathAndNameFetchForInternalNames:(NSArray *)_names
exactMatch:(BOOL)_beExact orDirectSubfolderMatch:(BOOL)_directSubs
withChannel: (EOAdaptorChannel*) channel
{
/* fetches the 'path' subset for a given quick-names */
NSMutableString *sql;
NSString *ws;
ws = [self generateSQLWhereForInternalNames:_names
exactMatch:_beExact orDirectSubfolderMatch:_directSubs];
exactMatch:_beExact orDirectSubfolderMatch:_directSubs
withChannel: channel];
if ([ws length] == 0)
return nil;
@@ -726,6 +735,7 @@ static BOOL _singleStoreMode = NO;
NSString *fname, *sname, *sql;
NSArray *fnames, *records;
unsigned int count;
EOAdaptorChannel* channel;
NSDictionary *record;
BOOL result;
@@ -733,36 +743,41 @@ static BOOL _singleStoreMode = NO;
fnames = [self internalNamesFromPath: _path];
if (fnames)
{
sql = [self generateSQLPathFetchForInternalNames: fnames
exactMatch: YES
orDirectSubfolderMatch: NO];
if ([sql length])
{
records = [self performSQL: sql];
if (records)
{
count = [records count];
if (count)
{
fname = [self internalNameFromPath: _path];
if (count == 1)
{
record = [records objectAtIndex: 0];
sname = [record objectForKey: GCSPathRecordName];
result = [fname isEqualToString: sname];
}
else
[self logWithFormat: @"records: %@", records];
}
}
else
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
__PRETTY_FUNCTION__, sql];
}
else
[self debugWithFormat:@"got no SQL for names: %@", fnames];
{
if ((channel = [self acquireOpenChannel]) == nil) {
return [NSException exceptionWithName:@"GCSNoChannel"
reason:@"could not "
userInfo:nil];
}
sql = [self generateSQLPathFetchForInternalNames: fnames
exactMatch: YES
orDirectSubfolderMatch: NO
withChannel: channel];
if ([sql length])
{
records = [self performSQL: sql withChannel: channel];
if (records)
{
count = [records count];
if (count)
{
fname = [self internalNameFromPath: _path];
if (count == 1)
{
record = [records objectAtIndex: 0];
sname = [record objectForKey: GCSPathRecordName];
result = [fname isEqualToString: sname];
}
else
[self logWithFormat: @"records: %@", records];
}
}
else
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'", __PRETTY_FUNCTION__, sql];
}
else
[self debugWithFormat:@"got no SQL for names: %@", fnames];
}
else
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
@@ -774,21 +789,29 @@ static BOOL _singleStoreMode = NO;
NSString *fname;
NSArray *fnames, *records;
NSString *sql;
EOAdaptorChannel *channel;
unsigned i, count;
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
return nil;
}
if ((channel = [self acquireOpenChannel]) == nil) {
return [NSException exceptionWithName:@"GCSNoChannel"
reason:@"could not "
userInfo:nil];
}
sql = [self generateSQLPathFetchForInternalNames:fnames
exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES)];
exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES) withChannel: channel];
if ([sql length] == 0) {
[self debugWithFormat:@"got no SQL for names: %@", fnames];
[self releaseChannel: channel];
return nil;
}
if ((records = [self performSQL:sql]) == nil) {
if ((records = [self performSQL:sql withChannel: channel]) == nil) {
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
__PRETTY_FUNCTION__, sql];
return nil;
@@ -833,27 +856,37 @@ static BOOL _singleStoreMode = NO;
NSArray *fnames, *records;
NSString *sql;
unsigned i, count;
EOAdaptorChannel *channel;
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
return nil;
}
if ((channel = [self acquireOpenChannel]) == nil) {
return [NSException exceptionWithName:@"GCSNoChannel"
reason:@"could not "
userInfo:nil];
}
sql = [self generateSQLPathAndNameFetchForInternalNames:fnames
exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES)];
exactMatch:NO orDirectSubfolderMatch:(_recursive ? NO : YES) withChannel: channel];
if ([sql length] == 0) {
[self debugWithFormat:@"got no SQL for names: %@", fnames];
[self releaseChannel: channel];
return nil;
}
if ((records = [self performSQL:sql]) == nil) {
if ((records = [self performSQL:sql withChannel: channel]) == nil) {
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
__PRETTY_FUNCTION__, sql];
return nil;
}
if ((count = [records count]) == 0)
if ((count = [records count]) == 0) {
return emptyArray;
}
result = [NSMutableArray arrayWithCapacity:(count > 128 ? 128 : count)];
@@ -899,16 +932,23 @@ static BOOL _singleStoreMode = NO;
NSArray *fnames, *records;
NSString *ws;
NSDictionary *record;
EOAdaptorChannel *channel;
if ((fnames = [self internalNamesFromPath:_path]) == nil) {
[self debugWithFormat:@"got no internal names for path: '%@'", _path];
return nil;
}
/* generate SQL to fetch folder attributes */
if ((channel = [self acquireOpenChannel]) == nil) {
return [NSException exceptionWithName:@"GCSNoChannel"
reason:@"could not "
userInfo:nil];
}
ws = [self generateSQLWhereForInternalNames:fnames
exactMatch:YES orDirectSubfolderMatch:NO];
exactMatch:YES orDirectSubfolderMatch:NO withChannel: channel];
sql = [NSMutableString stringWithCapacity:256];
[sql appendString:@"SELECT "];
@@ -925,7 +965,7 @@ static BOOL _singleStoreMode = NO;
/* fetching */
if ((records = [self performSQL:sql]) == nil) {
if ((records = [self performSQL:sql withChannel: channel]) == nil) {
[self logWithFormat:@"ERROR(%s): executing SQL failed: '%@'",
__PRETTY_FUNCTION__, sql];
return nil;
@@ -992,44 +1032,55 @@ static BOOL _singleStoreMode = NO;
andChannel: (EOAdaptorChannel *) channel
atPath: (NSString *) path
{
NSString *baseURL, *tableName, *quickTableName, *aclTableName, *createQuery, *sql;
NSString *baseURL, *tableName, *quickTableName, *aclTableName, *createQuery, *sql, *safeFolderName, *safePath;
GCSSpecialQueries *specialQuery;
EOAdaptorContext *aContext;
EOAdaptor *adaptor;
EOAttribute *attribute;
NSMutableArray *paths;
NSException *error;
NSRange range;
paths = [NSMutableArray arrayWithArray: [path componentsSeparatedByString: @"/"]];
aContext = [channel adaptorContext];
[aContext beginTransaction];
//escape foldername to avoid sql injection
adaptor = [aContext adaptor];
attribute = [EOAttribute new];
[attribute autorelease];
[attribute setExternalType: @"varchar"];
safeFolderName = [adaptor formatValue: folderName forAttribute: attribute];
safePath = [adaptor formatValue: path forAttribute: attribute];
paths = [NSMutableArray arrayWithArray: [safePath componentsSeparatedByString: @"/"]];
while ([paths count] < 5)
[paths addObject: @"NULL"];
aContext = [channel adaptorContext];
[aContext beginTransaction];
tableName = [self baseTableNameWithUID: [paths objectAtIndex: 2]];
quickTableName = [tableName stringByAppendingString: @"_quick"];
aclTableName = [tableName stringByAppendingString: @"_acl"];
// TBD: fix SQL injection issues.
// We no longer call stringByDeletingLastPathComponent since, since GNUstep 1.22,
// it'll replace // characters in the URL with /, so mysql:// becomes mysql:/
// This is to conform with recent Apple changes.
baseURL = [folderInfoLocation absoluteString];
range = [baseURL rangeOfString: @"/" options: NSBackwardsSearch];
if (range.location != NSNotFound)
baseURL = [baseURL substringToIndex: range.location];
//Mind the quote, safeFolderName and safePath already have them as they have been escaped, c_path4 already has one at the end
sql = [NSString stringWithFormat: @"INSERT INTO %@"
@" (c_path, c_path1, c_path2, c_path3, c_path4,"
@" c_foldername, c_location, c_quick_location,"
@" c_acl_location, c_folder_type)"
@" VALUES ('%@', '%@', '%@', '%@', '%@', '%@', '%@/%@',"
@" VALUES (%@, '%@', '%@', '%@', '%@, %@, '%@/%@',"
@" '%@/%@', '%@/%@', '%@')",
[self folderInfoTableName], path,
[self folderInfoTableName], safePath,
[paths objectAtIndex: 1], [paths objectAtIndex: 2],
[paths objectAtIndex: 3], [paths objectAtIndex: 4],
[folderName stringByReplacingString: @"'" withString: @"''"],
safeFolderName,
baseURL, tableName,
baseURL, quickTableName,
baseURL, aclTableName,
@@ -1114,6 +1165,12 @@ static BOOL _singleStoreMode = NO;
EOAdaptorChannel *channel;
NSException *ex;
if ((channel = [self acquireOpenChannel]) == nil) {
return [NSException exceptionWithName:@"GCSNoChannel"
reason:@"could not "
userInfo:nil];
}
if ((folder = [self folderAtPath:_path]) == nil) {
return [NSException exceptionWithName:@"GCSMissingFolder"
reason:@"missing folder"
@@ -1126,15 +1183,11 @@ static BOOL _singleStoreMode = NO;
}
ws = [self generateSQLWhereForInternalNames:fnames
exactMatch:YES orDirectSubfolderMatch:NO];
exactMatch:YES orDirectSubfolderMatch:NO withChannel: channel];
sql = [NSString stringWithFormat: @"DELETE FROM %@ WHERE %@",
[self folderInfoTableName], ws];
if ((channel = [self acquireOpenChannel]) == nil) {
return [NSException exceptionWithName:@"GCSNoChannel"
reason:@"could not "
userInfo:nil];
}
[[channel adaptorContext] beginTransaction];
@@ -26,7 +26,7 @@ vtodo_class2 = "(Bizalmas feladat)";
"time_label" = "Idő";
"to_label" = "-";
"due_label" = "Lejárat napja:";
"location_label" = "Hely";
"location_label" = "Helyszín";
"summary_label" = "Összegzés:";
"comment_label" = "Megjegyzés:";
"organizer_label" = "Szervező";
@@ -3521,6 +3521,15 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
timezone = nil;
element = [components objectAtIndex: i];
//remove all attenddees, change organisator and change uid
//If we do not clean up, any user could impersonate someone by importing a malicious .ics
//and send notificaitons to attenddes or remove their event (as SOGo think the user has the rights to do so)
[element removeAllAttendees];
[element setOrganizer: nil];
[element setUid: [self globallyUniqueObjectId]];
if ([element isKindOfClass: iCalEventK])
{
event = (iCalEvent *)element;
@@ -184,6 +184,10 @@ size_t curl_body_function(void *ptr, size_t size, size_t nmemb, void *buffer)
else
[result setObject: @"invalid-calendar-content" forKey: @"error"];
}
else if (status == 401)
{
[result setObject: @"need-auth" forKey: @"error"];
}
else
[result setObject: @"http-error" forKey: @"error"];
}
+1 -1
View File
@@ -322,7 +322,7 @@ static NSString *userAgent = nil;
- (void) setText: (NSString *) newText
{
ASSIGN (text, [newText stringWithoutHTMLInjection: NO]);
ASSIGN (text, [newText stringWithoutHTMLInjection: NO stripAngular: NO]);
}
- (NSString *) text
+3 -3
View File
@@ -153,7 +153,7 @@
NSString *rc;
if (htmlComposition)
rc = [NSString stringWithFormat: @"%@<br/>", [self _headerField: @"organization"]];
rc = [NSString stringWithFormat: @"%@<br/>", [[self _headerField: @"organization"] stringByEscapingHTMLString]];
else
rc = [NSString stringWithFormat: @"%@\n", [self _headerField: @"organization"]];
@@ -205,7 +205,7 @@
NSString *rc;
if (htmlComposition)
rc = [NSString stringWithFormat: @"%@<br/>", [self _headerField: @"newsgroups"]];
rc = [NSString stringWithFormat: @"%@<br/>", [[self _headerField: @"newsgroups"] stringByEscapingHTMLString]];
else
rc = [NSString stringWithFormat: @"%@\n", [self _headerField: @"newsgroups"]];
@@ -222,7 +222,7 @@
NSString *rc;
if (htmlComposition)
rc = [NSString stringWithFormat: @"%@<br/>", [self _headerField: @"references"]];
rc = [NSString stringWithFormat: @"%@<br/>", [[self _headerField: @"references"] stringByEscapingHTMLString]];
else
rc = [NSString stringWithFormat: @"%@\n", [self _headerField: @"references"]];
+2 -1
View File
@@ -78,7 +78,8 @@
/* XSS protection */
- (NSString *) removeHTMLTagsExceptAnchorTags;
- (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode;
- (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode
stripAngular: (BOOL)stripAngular;
#ifndef GNUSTEP_BASE_LIBRARY
- (BOOL) boolValue;
+114 -39
View File
@@ -936,18 +936,19 @@ static int cssEscapingCount;
* @param stripHTMLCode Remove all HTML code from content
* @return A safe string
*/
- (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode
- (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode stripAngular: (BOOL)stripAngular
{
NSString *result, *text, *newResult;
NSScanner *theScanner;
NSError *error;
NSUInteger numberOfMatches;
NSRegularExpression *regex;
NSRegularExpression *regex, *importRegex;
text = nil;
error = nil;
result = [NSString stringWithString: self];
regex = nil;
importRegex = nil;
NS_DURING
{
@@ -986,20 +987,28 @@ static int cssEscapingCount;
// Remove javascript:
regex = [NSRegularExpression regularExpressionWithPattern:@"j[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*a[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*v[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*a[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*c[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*:"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@""];
result = [NSString stringWithString: newResult];
// loop until stable: a single pass lets nested tokens reconstruct
// the scheme, e.g. "javajavascript:script:" -> "javascript:"
while ([regex numberOfMatchesInString:result options:0 range:NSMakeRange(0, [result length])] > 0) {
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@""];
result = [NSString stringWithString: newResult];
}
// Remove vbscript:
regex = [NSRegularExpression regularExpressionWithPattern:@"v[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*b[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*c[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*:"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@""];
result = [NSString stringWithString: newResult];
while ([regex numberOfMatchesInString:result options:0 range:NSMakeRange(0, [result length])] > 0) {
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@""];
result = [NSString stringWithString: newResult];
}
// Remove livescript:
regex = [NSRegularExpression regularExpressionWithPattern:@"l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*v[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*c[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*:"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@""];
result = [NSString stringWithString: newResult];
while ([regex numberOfMatchesInString:result options:0 range:NSMakeRange(0, [result length])] > 0) {
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@""];
result = [NSString stringWithString: newResult];
}
// Remove <script
regex = [NSRegularExpression regularExpressionWithPattern:@"<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*c[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t"
@@ -1031,36 +1040,41 @@ static int cssEscapingCount;
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"</for*"];
result = [NSString stringWithString: newResult];
// Remove onload
regex = [NSRegularExpression regularExpressionWithPattern:@"onload="
// Remove ANY inline event handler (on...=) in one rule instead of an
// incomplete allow list. This also covers obfuscation by whitespace
// before the '=' ("onerror =") and handler names that were not listed
// before (onfocus, onbegin, onanimationstart, ...). With onfocus gone,
// a lone "autofocus" has no handler left to trigger.
regex = [NSRegularExpression regularExpressionWithPattern:@"\\bon(click|error|focus|load|mouseover|animationstart)[\\s\\u200B&#x09;&#x0A;&#x0D;\\r\\n\\t]*="
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"onl***="];
result = [NSString stringWithString: newResult];
// Remove onmouseover
regex = [NSRegularExpression regularExpressionWithPattern:@"onmouseover="
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"onmouseo***="];
result = [NSString stringWithString: newResult];
// Remove onrepeat
regex = [NSRegularExpression regularExpressionWithPattern:@"onrepeat="
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"onrep***="];
result = [NSString stringWithString: newResult];
// Remove onerror
regex = [NSRegularExpression regularExpressionWithPattern:@"onerror="
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"onerr***="];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"data-blocked="];
result = [NSString stringWithString: newResult];
// Remove @import css (in style tags)
regex = [NSRegularExpression regularExpressionWithPattern:@"(<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e.*)([\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*@[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*m[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*o[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t)(.*<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*\\/[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*>)"
importRegex = [NSRegularExpression regularExpressionWithPattern:@"(<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e.*)([\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*@[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*m[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*o[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t)(.*<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*\\/[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*>)"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [importRegex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"$1@im****$3"];
result = [NSString stringWithString: newResult];
if(stripAngular) {
// Remove {{ and }} as they are interprated by angularJS with no way of escaping.
// Cover the literal form, the hex entity (&#x7b;) AND the decimal entity
// (&#123;), both with optional leading zeros, since the browser decodes
// any of them back to a brace before AngularJS parses the node.
regex = [NSRegularExpression regularExpressionWithPattern:@"(\\{\\{)|(&#x0*7b;&#x0*7b;)|(&#0*123;&#0*123;)"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"{\\\\{"];
result = [NSString stringWithString: newResult];
regex = [NSRegularExpression regularExpressionWithPattern:@"(\\}\\})|(&#x0*7d;&#x0*7d;)|(&#0*125;&#0*125;)"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"}/}"];
result = [NSString stringWithString: newResult];
}
newResult = result;
while([regex numberOfMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length])] > 0) {
newResult = [regex stringByReplacingMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length]) withTemplate:@"$1@im****$3"];
while([importRegex numberOfMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length])] > 0) {
newResult = [importRegex stringByReplacingMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length]) withTemplate:@"$1@im****$3"];
}
result = [NSString stringWithString: newResult];
}
@@ -1075,14 +1089,75 @@ static int cssEscapingCount;
}
- (NSString *) cleanInvalidHTMLTags {
// Clean HTML invalid tags as reported in https://bugs.sogo.nu/view.php?id=5755
NSString *s;
s = [NSString stringWithString: self];
s = [s stringByReplacingOccurrencesOfString:@"<!-->" withString:@""];
s = [s stringByReplacingOccurrencesOfString:@"<!--<!" withString:@"<!--"];
// Clean HTML invalid tags as reported in https://bugs.sogo.nu/view.php?id=5755 and https://bugs.sogo.nu/view.php?id=6167
// libxml2 (HTML4) sees a standalone empty comment (<!--> or <!--->) as an
// unterminated comment and drops the rest of the document, so remove it.
// Only do it outside of comments: the Outlook "downlevel-revealed" pattern
// <!--[if !mso]><!--> ... <!--<![endif]--> ends its first comment with
// "<!-->", and removing it would hide the content in between.
NSString *result;
unichar *in, *out;
NSUInteger len, i, j, o;
return s;
len = [self length];
if (len == 0)
return [NSString string];
// Work on raw buffers: linear time, bounded memory (no per-comment objects)
in = NSZoneMalloc (NULL, len * sizeof (unichar));
out = NSZoneMalloc (NULL, len * sizeof (unichar));
[self getCharacters: in];
i = o = 0;
while (i < len)
{
if (i + 3 < len
&& in[i] == '<' && in[i+1] == '!' && in[i+2] == '-' && in[i+3] == '-')
{
j = i + 4;
if (j < len && in[j] == '>')
{
// <!-->
i = j + 1;
continue;
}
if (j + 1 < len && in[j] == '-' && in[j+1] == '>')
{
// <!--->
i = j + 2;
continue;
}
// Regular comment: keep it untouched up to its end ("-->" or "--!>",
// as libxml2 accepts both), or up to the end if unterminated
while (j < len)
{
if (j + 2 < len && in[j] == '-' && in[j+1] == '-' && in[j+2] == '>')
{
j += 3;
break;
}
if (j + 3 < len && in[j] == '-' && in[j+1] == '-'
&& in[j+2] == '!' && in[j+3] == '>')
{
j += 4;
break;
}
j++;
}
memcpy (out + o, in + i, (j - i) * sizeof (unichar));
o += j - i;
i = j;
}
else
out[o++] = in[i++];
}
result = [NSString stringWithCharacters: out length: o];
NSZoneFree (NULL, in);
NSZoneFree (NULL, out);
return result;
}
@end
+1 -1
View File
@@ -93,7 +93,7 @@ static const NSString *kCacheMotdKey = @"admin-motd";
NSException *error;
NSString *safeMotd;
safeMotd = [motd stringWithoutHTMLInjection: NO];
safeMotd = [motd stringWithoutHTMLInjection: NO stripAngular: NO];
error = [[[GCSFolderManager defaultFolderManager] adminFolder] writeMotd: safeMotd];
if (!error) {
[[SOGoCache sharedCache] setValue:safeMotd forKey:kCacheMotdKey];
+1 -1
View File
@@ -30,7 +30,7 @@
SOGoLDAPContactInfoAttribute = "description";
SOGoMailingMechanism = "sendmail";
SOGoMailingMechanism = "smtp";
SOGoSMTPServer = "smtp://localhost";
SOGoMailSpoolPath = "/var/spool/sogo";
+1
View File
@@ -100,6 +100,7 @@
- (BOOL) notifyOnExternalModifications;
- (NSDictionary *) mailJunkSettings;
- (NSString *) mailJunkIcon;
- (BOOL) createIdentitiesDisabled;
+25 -3
View File
@@ -304,11 +304,10 @@
NSString *mailingMechanism;
mailingMechanism = [self stringForKey: @"SOGoMailingMechanism"];
if (!([mailingMechanism isEqualToString: @"sendmail"]
|| [mailingMechanism isEqualToString: @"smtp"]))
if (![mailingMechanism isEqualToString: @"smtp"])
{
[self logWithFormat: @"mechanism '%@' is invalid and"
@" should be set to 'sendmail' or 'smtp' instead",
@" should be set to 'smtp' instead",
mailingMechanism];
mailingMechanism = nil;
}
@@ -482,6 +481,29 @@
return [self objectForKey: @"SOGoMailJunkSettings"];
}
- (NSString *) mailJunkIcon
{
NSArray *iconsList;
NSString *icon;
iconsList = [NSArray arrayWithObjects: @"thumb_down",
@"gpp_bad",
@"local_fire_department",
@"bug_report",
@"error",
@"warning",
@"dangerous",
@"report_gmailerrorred",
nil];
icon = [self objectForKey: @"mailJunkIcon"];
if (([icon length] && ![iconsList containsObject:icon]) || ![icon length])
{
icon = @"thumb_down";
}
return icon;
}
- (BOOL) createIdentitiesDisabled
{
return [self boolForKey: @"SOGoCreateIdentitiesDisabled"];
+1
View File
@@ -149,6 +149,7 @@ NSComparisonResult languageSort(id el1, id el2, void *context);
- (BOOL) isEasUIDisabled;
- (BOOL)isPasswordRecoveryEnabled;
- (NSArray *) passwordRecoveryBaseURLs;
- (NSArray *) passwordRecoveryDomains;
- (NSString *) JWTSecret;
+5
View File
@@ -1082,6 +1082,11 @@ NSComparisonResult languageSort(id el1, id el2, void *context)
return [self boolForKey: @"SOGoPasswordRecoveryEnabled"];
}
- (NSArray *) passwordRecoveryBaseURLs
{
return [self stringArrayForKey: @"SOGoPasswordRecoveryBaseURLs"];
}
- (NSArray *) passwordRecoveryDomains
{
static NSArray *passwordRecoveryDomains = nil;
+2 -2
View File
@@ -152,7 +152,7 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
rc = NO;
if ([fullName length])
[identity setObject: [fullName stringWithoutHTMLInjection: YES] forKey: @"fullName"];
[identity setObject: [fullName stringWithoutHTMLInjection: YES stripAngular:NO] forKey: @"fullName"];
if ([email length])
[identity setObject: email forKey: @"email"];
if ([replyTo length])
@@ -864,7 +864,7 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
if (mailIdentity && [mailIdentity objectForKey: @"fullName"]) {
fullName = [NSString stringWithString: [mailIdentity objectForKey: @"fullName"]];
if (fullName) {
[mailIdentity setObject: [fullName stringWithoutHTMLInjection: YES] forKey: @"fullName"];
[mailIdentity setObject: [fullName stringWithoutHTMLInjection: YES stripAngular:NO] forKey: @"fullName"];
[mailIdentities setObject: mailIdentity atIndexedSubscript: i];
}
}
+32 -12
View File
@@ -99,23 +99,43 @@
- (void) test_stringWithoutHTMLInjection
{
testEquals([[NSString stringWithString:@"<a href=\"\">foo</a>bar"] stringWithoutHTMLInjection: YES], @" foo bar");
testEquals([[NSString stringWithString:@"fb <foo@bar.com>"] stringWithoutHTMLInjection: YES], @"fb <foo@bar.com>");
testEquals([[NSString stringWithString:@"Test\n<script>alert(\"foobar\");"] stringWithoutHTMLInjection: NO], @"Test\n<scr***>alert(\"foobar\");");
testEquals([[NSString stringWithString:@"<img vbscript:test"] stringWithoutHTMLInjection: NO], @"<img test");
testEquals([[NSString stringWithString:@"<img javascript:test"] stringWithoutHTMLInjection: NO], @"<img test");
testEquals([[NSString stringWithString:@"<img livescript:test"] stringWithoutHTMLInjection: NO], @"<img test");
testEquals([[NSString stringWithString:@"foobar <form action=\"\">bar</form>"] stringWithoutHTMLInjection: NO], @"foobar <for* action=\"\">bar</for*>");
testEquals([[NSString stringWithString:@"foobar <iframe src=\"\">bar</iframe>"] stringWithoutHTMLInjection: NO], @"foobar <ifr*** src=\"\">bar</iframe>");
testEquals([[NSString stringWithString:@"foobar <img onload=foo bar"] stringWithoutHTMLInjection: NO], @"foobar <img onl***=foo bar");
testEquals([[NSString stringWithString:@"foobar <img onmouseover=foo bar"] stringWithoutHTMLInjection: NO], @"foobar <img onmouseo***=foo bar");
testEquals([[NSString stringWithString:@"<!DOCTYPE html><html><head><style>@import url(https://foo.bar/malicious.css);.foo{background-color: red; @import url(https://bar.foo/malicious2.css);</style></head><body><table><tr><td>A</td><td>B</td><td>C</td></tr></table></body></html>"] stringWithoutHTMLInjection: NO], @"<!DOCTYPE html><html><head><style>@im**** url(https://foo.bar/malicious.css);.foo{background-color: red; @im**** url(https://bar.foo/malicious2.css);</style></head><body><table><tr><td>A</td><td>B</td><td>C</td></tr></table></body></html>");
testEquals([[NSString stringWithString:@"<a href=\"\">foo</a>bar"] stringWithoutHTMLInjection: YES stripAngular: NO], @" foo bar");
testEquals([[NSString stringWithString:@"fb <foo@bar.com>"] stringWithoutHTMLInjection: YES stripAngular: NO], @"fb <foo@bar.com>");
testEquals([[NSString stringWithString:@"Test\n<script>alert(\"foobar\");"] stringWithoutHTMLInjection: NO stripAngular: NO], @"Test\n<scr***>alert(\"foobar\");");
testEquals([[NSString stringWithString:@"<img vbscript:test"] stringWithoutHTMLInjection: NO stripAngular: NO], @"<img test");
testEquals([[NSString stringWithString:@"<img javascript:test"] stringWithoutHTMLInjection: NO stripAngular: NO], @"<img test");
testEquals([[NSString stringWithString:@"<img livescript:test"] stringWithoutHTMLInjection: NO stripAngular: NO], @"<img test");
testEquals([[NSString stringWithString:@"foobar <form action=\"\">bar</form>"] stringWithoutHTMLInjection: NO stripAngular: NO], @"foobar <for* action=\"\">bar</for*>");
testEquals([[NSString stringWithString:@"foobar <iframe src=\"\">bar</iframe>"] stringWithoutHTMLInjection: NO stripAngular: NO], @"foobar <ifr*** src=\"\">bar</iframe>");
testEquals([[NSString stringWithString:@"foobar <img onload=foo bar"] stringWithoutHTMLInjection: NO stripAngular: NO], @"foobar <img data-blocked=foo bar");
testEquals([[NSString stringWithString:@"foobar <img onmouseover=foo bar"] stringWithoutHTMLInjection: NO stripAngular: NO], @"foobar <img data-blocked=foo bar");
// any on...= handler is neutralised, including whitespace before '=' and names
// that were never in the old list
testEquals([[NSString stringWithString:@"<img src=x onerror =alert(1)>"] stringWithoutHTMLInjection: NO stripAngular: NO], @"<img src=x data-blocked=alert(1)>");
testEquals([[NSString stringWithString:@"<input autofocus onfocus=alert(1)>"] stringWithoutHTMLInjection: NO stripAngular: NO], @"<input autofocus data-blocked=alert(1)>");
// deletion filters loop until stable, so nesting cannot rebuild the scheme
testEquals([[NSString stringWithString:@"javajavascript:script:"] stringWithoutHTMLInjection: NO stripAngular: NO], @"");
testEquals([[NSString stringWithString:@"<!DOCTYPE html><html><head><style>@import url(https://foo.bar/malicious.css);.foo{background-color: red; @import url(https://bar.foo/malicious2.css);</style></head><body><table><tr><td>A</td><td>B</td><td>C</td></tr></table></body></html>"] stringWithoutHTMLInjection: NO stripAngular: NO], @"<!DOCTYPE html><html><head><style>@im**** url(https://foo.bar/malicious.css);.foo{background-color: red; @im**** url(https://bar.foo/malicious2.css);</style></head><body><table><tr><td>A</td><td>B</td><td>C</td></tr></table></body></html>");
// the @import cleanup must still run when angular interpolation is stripped as well
testEquals([[NSString stringWithString:@"<style>@import url(https://foo.bar/malicious.css);</style>"] stringWithoutHTMLInjection: NO stripAngular: YES], @"<style>@im**** url(https://foo.bar/malicious.css);</style>");
// literal braces are rewritten so AngularJS cannot interpolate them
testEquals([[NSString stringWithString:@"{{1337*1337}}"] stringWithoutHTMLInjection: NO stripAngular: YES], @"{\\{1337*1337}/}");
// ... and so are the decimal HTML entities the browser would decode back to braces
testEquals([[NSString stringWithString:@"&#123;&#123;1337*1337&#125;&#125;"] stringWithoutHTMLInjection: NO stripAngular: YES], @"{\\{1337*1337}/}");
}
- (void) test_stringCleanInvalidHTMLTags
{
testEquals([[NSString stringWithString:@"<div>Test<!--></div>"] cleanInvalidHTMLTags], @"<div>Test</div>");
testEquals([[NSString stringWithString:@"<div><!--[if !mso]><span>Test</span><!--<![endif]--></div>"] cleanInvalidHTMLTags], @"<div><!--[if !mso]><span>Test</span><!--[endif]--></div>");
testEquals([[NSString stringWithString:@"<div>Test<!---></div>"] cleanInvalidHTMLTags], @"<div>Test</div>");
testEquals([[NSString stringWithString:@"<div>A<!-->B<!-- c -->C<!-->D</div>"] cleanInvalidHTMLTags], @"<div>AB<!-- c -->CD</div>");
testEquals([[NSString stringWithString:@"<div><!--[if !mso]><span>Test</span><!--<![endif]--></div>"] cleanInvalidHTMLTags], @"<div><!--[if !mso]><span>Test</span><!--<![endif]--></div>");
// Outlook "downlevel-revealed" conditional comment: content must stay visible
testEquals([[NSString stringWithString:@"<a><!--[if !mso]><!--> <div><img src=\"x\"/></div> <!--<![endif]--><!--[if mso]><v:fill/><![endif]--></a>"] cleanInvalidHTMLTags], @"<a><!--[if !mso]><!--> <div><img src=\"x\"/></div> <!--<![endif]--><!--[if mso]><v:fill/><![endif]--></a>");
// libxml2 also ends a comment with "--!>"
testEquals([[NSString stringWithString:@"A<!-- x --!>B<!-->C"] cleanInvalidHTMLTags], @"A<!-- x --!>BC");
// unterminated comment is left as is
testEquals([[NSString stringWithString:@"<div>A<!-- B</div>"] cleanInvalidHTMLTags], @"<div>A<!-- B</div>");
}
- (void) test_stringRemoveHTMLTagsExceptAnchorTags
+1
View File
@@ -25,6 +25,7 @@ $(SOGO_TOOL)_OBJC_FILES += \
SOGoToolCleanupUser.m \
SOGoToolRestore.m \
SOGoToolCreateFolder.m \
SOGoToolInitUser.m \
SOGoToolUserPreferences.m \
SOGoToolManageACL.m \
SOGoToolManageEAS.m \
+273
View File
@@ -0,0 +1,273 @@
/* SOGoToolInitUser.m - this file is part of SOGo
*
* Copyright (C) 2026 Alinto
*
* This file 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 2, or (at your option)
* any later version.
*
* This file 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSString.h>
#import <Foundation/NSUserDefaults.h>
#import <NGObjWeb/WOApplication.h>
#import <NGObjWeb/WOComponent.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <SOGo/SOGoGCSFolder.h>
#import <SOGo/SOGoParentFolder.h>
#import <SOGo/SOGoSystemDefaults.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserFolder.h>
#import "SOGoToolRestore.h"
@interface NSObject (UIxJSONPreferences)
- (NSString *) jsonDefaults;
@end
@interface SOGoToolInitUser : SOGoToolRestore
{
NSArray *usersToInit;
NSString *language;
}
@end
@implementation SOGoToolInitUser
+ (NSString *) command
{
return @"init-user";
}
+ (NSString *) description
{
return @"create the personal folders and preferences of user(s) without requiring a login";
}
- (id) init
{
if ((self = [super init]))
{
usersToInit = nil;
language = nil;
}
return self;
}
- (void) dealloc
{
[usersToInit release];
[language release];
[super dealloc];
}
- (void) usage
{
fprintf (stderr, "init-user [-l language] user1 [user2] ...\n\n"
" user the user whose personal folders must be created\n"
" language the language of the user's preferences, as chosen\n"
" at login (defaults to the domain or system one)\n\n"
"Creates the personal calendar and the personal address book of the\n"
"user(s) and initializes their preferences, as it would otherwise only\n"
"happen upon their first login.\n\n"
"Example: sogo-tool init-user jdoe\n"
" sogo-tool init-user -l French jdoe jsmith\n");
}
- (BOOL) parseArguments
{
NSArray *supportedLanguages;
NSString *requestedLanguage;
if ([sanitizedArguments count] < 1)
{
[self usage];
return NO;
}
requestedLanguage = [[NSUserDefaults standardUserDefaults] stringForKey: @"l"];
if ([requestedLanguage length])
{
supportedLanguages = [[SOGoSystemDefaults sharedSystemDefaults]
supportedLanguages];
if (![supportedLanguages containsObject: requestedLanguage])
{
fprintf (stderr, "Unsupported language: %s\n",
[requestedLanguage UTF8String]);
return NO;
}
ASSIGN (language, requestedLanguage);
}
ASSIGN (usersToInit, sanitizedArguments);
return YES;
}
/* The lookup of the "personal" folder triggers its creation when it does not
exist yet, provided the active user is the owner of the folder. */
- (BOOL) _initFolderOfType: (NSString *) folderType
withUserFolder: (SOGoUserFolder *) userFolder
inContext: (WOContext *) localContext
{
SOGoParentFolder *parentFolder;
id folder;
parentFolder = [userFolder lookupName: folderType
inContext: localContext
acquire: NO];
if (![parentFolder isKindOfClass: [SOGoParentFolder class]])
{
NSLog (@"module '%@' is not available for user '%@'", folderType, userID);
return NO;
}
[parentFolder setContext: localContext];
folder = [parentFolder lookupPersonalFolder: @"personal"
ignoringRights: YES];
if (![folder isKindOfClass: [SOGoGCSFolder class]])
{
NSLog (@"personal folder of module '%@' could not be created for user"
@" '%@'", folderType, userID);
return NO;
}
if (verbose)
NSLog (@"personal folder of module '%@' is ready for user '%@'",
folderType, userID);
return YES;
}
/* Upon the first login, the c_defaults of the user are materialized when the
web interface loads: -[UIxJSONPreferences jsonDefaults] resolves every
default through the domain and system defaults, writes them into the user's
profile and synchronizes it. We invoke that very method so that an
initialized user is indistinguishable from a user who has logged in. */
- (BOOL) _initDefaultsOfUser: (SOGoUser *) user
inContext: (WOContext *) localContext
{
WOComponent *jsonPreferences;
Class jsonPreferencesKlass;
/* The language must be set before the component is built: -jsonDefaults only
materializes the defaults that are missing, and the labels it localizes
(the calendar and contacts categories) are looked up in the languages of
the context, which are those of the user. */
if (language)
[[user userDefaults] setLanguage: language];
if (![WOApplication application])
[[WOApplication alloc] init];
jsonPreferencesKlass = NSClassFromString (@"UIxJSONPreferences");
if (!jsonPreferencesKlass)
{
NSLog (@"class 'UIxJSONPreferences' is unavailable, defaults of user"
@" '%@' cannot be initialized", userID);
return NO;
}
jsonPreferences = [[[jsonPreferencesKlass alloc]
initWithContext: localContext] autorelease];
if (![jsonPreferences jsonDefaults])
{
NSLog (@"defaults of user '%@' could not be initialized", userID);
return NO;
}
if (verbose)
NSLog (@"defaults of user '%@' are ready", userID);
return YES;
}
- (BOOL) _initUser
{
SOGoUserFolder *userFolder;
WOContext *localContext;
SOGoUser *user;
BOOL rc;
user = [SOGoUser userWithLogin: userID];
if (!user)
{
NSLog (@"user '%@' could not be instantiated", userID);
return NO;
}
localContext = [WOContext context];
[localContext setActiveUser: user];
rc = [self _initDefaultsOfUser: user inContext: localContext];
userFolder = [SOGoUserFolder objectWithName: userID inContainer: nil];
[userFolder setContext: localContext];
rc = [self _initFolderOfType: @"Calendar"
withUserFolder: userFolder
inContext: localContext] && rc;
rc = [self _initFolderOfType: @"Contacts"
withUserFolder: userFolder
inContext: localContext] && rc;
return rc;
}
- (BOOL) proceed
{
NSAutoreleasePool *pool;
NSString *identifier;
NSUInteger count, max;
BOOL rc;
rc = YES;
max = [usersToInit count];
for (count = 0; count < max; count++)
{
pool = [NSAutoreleasePool new];
identifier = [usersToInit objectAtIndex: count];
if ([self fetchUserID: identifier])
{
if (![self _initUser])
rc = NO;
}
else
{
fprintf (stderr, "Invalid user: %s\n", [identifier UTF8String]);
rc = NO;
}
[pool release];
}
return rc;
}
- (BOOL) run
{
return ([self parseArguments] && [self proceed]);
}
@end
-1
View File
@@ -584,7 +584,6 @@
[self _updateForeignSubscriptionsFromUser: oldUserID toUser: newUserID];
[self _updateLocalACLsFromUser: oldUserID toUser: newUserID];
[self _updateForeignACLsFromUser: oldUserID toUser: newUserID];
[self _updateOldUserIDDefaultAndSettings: oldUserID toUser: newUserID];
rc = YES;
}
@@ -25,7 +25,7 @@
"No resource" = "Nincs erőforrás";
"Any Authenticated User" = "Bármely azonosított felhasználó";
"Public Access" = "Nyilvános hozzáférés";
"Save" = "Save";
"Clear" = "Clear";
"Message of the day" = "Message of the day";
"Message of the day has been saved" = "Message of the day has been saved";
"Save" = "Mentés";
"Clear" = "Törlés";
"Message of the day" = "A nap üzenete";
"Message of the day has been saved" = "A nap üzenete mentésre került";
@@ -176,13 +176,13 @@
"Show or hide this help" = "Mutassa vagy elrejtse ezt a súgót";
/* Space key */
"key_space" = "space";
"key_space" = "szóköz";
/* Delete key */
"key_delete" = "törlés";
/* Shift and space key */
"key_shift+space" = "shift + space";
"key_shift+space" = "shift + szóköz";
/* Up arrow key */
"key_up" = "↑";
@@ -235,8 +235,8 @@
"passwordRecovery_SecondaryEmail" = "Másodlagos email cím";
/* Password */
"POLICY_MIN_LOWERCASE_LETTER" = "Minimum of %{0} lowercase letter";
"POLICY_MIN_UPPERCASE_LETTER" = "Minimum of %{0} uppercase letter";
"POLICY_MIN_DIGIT" = "Minimum of %{0} digit";
"POLICY_MIN_SPECIAL_SYMBOLS" = "Minimum of %{0} special symbols";
"POLICY_MIN_LENGTH" = "Minimum length of %{0} characters";
"POLICY_MIN_LOWERCASE_LETTER" = "Legalább %{0} kisbetű";
"POLICY_MIN_UPPERCASE_LETTER" = "Legalább %{0} nagybetű";
"POLICY_MIN_DIGIT" = "Legalább %{0} szám";
"POLICY_MIN_SPECIAL_SYMBOLS" = "Legalább %{0} különleges karakter";
"POLICY_MIN_LENGTH" = "Legalább %{0} karakter";
+8
View File
@@ -488,6 +488,14 @@
&& [user isSuperUser]);
}
- (BOOL) usesCASAuthentication
{
SOGoSystemDefaults *sd;
sd = [SOGoSystemDefaults sharedSystemDefaults];
return [[sd authenticationType] isEqualToString: @"cas"];
}
- (BOOL) usesOpenIdAuthentication
{
+1 -1
View File
@@ -61,7 +61,7 @@
request = [context request];
params = [[request contentAsString] objectFromJSONString];
name = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES];
name = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES stripAngular:NO];
nameInContainer = nil;
if ([name length] > 0)
@@ -12,10 +12,10 @@
"Common" = "Közös";
"Contact editor" = "Kapcsolatszerkesztő";
"Contact viewer" = "Kapcsolat betekintő";
"Email" = "Email";
"Email" = "Email cím";
"Screen Name" = "Megjelenített név";
"Extended" = "Kiterjesztett";
"Fax" = "Fax";
"Fax" = "Telefax";
"Firstname" = "Keresztnév";
"Home" = "Otthoni";
"HomePhone" = "Otthoni telefon";
@@ -61,10 +61,10 @@
"nsaimid" = "Megjelenített név";
/* Search scope: mail fields */
"mail" = "Mail";
"mail" = "Üzenetek";
/* Search scope: mail fields */
"c_mail" = "Mail";
"c_mail" = "Üzenetek";
/* Search scope: telephone field */
"telephonenumber" = "Telefon";
@@ -76,7 +76,7 @@
"mobile" = "Mobiltelefon";
/* Search scope: fax field */
"facsimiletelephonenumber" = "Fax";
"facsimiletelephonenumber" = "Telefax";
/* Search scope: pager field */
"pager" = "Személyhívó";
@@ -193,7 +193,7 @@
"Work" = "Munkahely";
"Mobile" = "Mobiltelefon";
"Pager" = "Személyhívó";
"Download iOS - MacOS configuration profile" = "Download iOS - MacOS configuration profile";
"Download iOS - MacOS configuration profile" = "iOS - MacOS konfigurációs profil letöltése";
/* categories */
"contacts_category_labels" = "Munkatárs, Versenytárs, Felhasználó, Barát, Család, Üzleti partner, Szolgáltató, Sajtó, VIP";
+1 -1
View File
@@ -485,7 +485,7 @@ static Class SOGoContactGCSEntryK = Nil;
co = [self clientObject];
card = [co vCard];
request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: YES] objectFromJSONString];
params = [[[request contentAsString] stringWithoutHTMLInjection: YES stripAngular:NO] objectFromJSONString];
forceSave = [[params objectForKey: @"ignoreDuplicate"] boolValue];
[self setAttributes: params];
+7 -1
View File
@@ -115,12 +115,18 @@
{
NSMutableDictionary *contactSettings;
NSString *ascending, *sort;
NSArray *sortValues;
SOGoUserSettings *us;
sort = [[self requestData] objectForKey: @"sort"];
ascending = [[self requestData] objectForKey: @"asc"];
if ([sort length])
//Check value of sorting
sortValues = [NSArray arrayWithObjects: @"c_cn", @"c_sn",
@"c_givenname", @"c_mail", @"c_screenname", @"c_o", @"c_telephonenumber", nil];
if ([sort length] && [sortValues containsObject:sort])
{
sort = [self sortKey];
us = [[context activeUser] userSettings];
+1 -1
View File
@@ -339,7 +339,7 @@
[list retain];
request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: YES] objectFromJSONString];
params = [[[request contentAsString] stringWithoutHTMLInjection: YES stripAngular:NO] objectFromJSONString];
o = [params objectForKey: @"refs"];
if (![o isKindOfClass: [NSArray class]])
@@ -44,7 +44,7 @@ reply_info = "Ez egy válasz az ön által kiküldött meghívásra.";
"Save Attachment" = "Melléklet mentése";
"CC" = "Másolat";
"Cancel" = "Mégsem";
"OK" = "OK";
"OK" = "Rendben";
"Comment" = "Megjegyzés:";
"Description" = "Leírás";
"Location" = "Location";
"Location" = "Helyszín";
+95 -1
View File
@@ -130,6 +130,13 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
BOOL hasEmbeddedCSS;
xmlCharEncoding contentEncoding;
BOOL rawContent;
// Wrap libxml2-orphaned <a> around the following block element
// (HTML5 pattern <a><table>...</table></a> auto-closed by libxml2 HTML4 DTD).
NSString *pendingAnchorTag;
NSString *anchorWrapTag;
NSUInteger anchorWrapDepth;
NSString *lastAnchorOpenString;
NSUInteger lastAnchorOpenEnd;
}
- (NSString *) result;
@@ -175,6 +182,9 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
[result release];
[css release];
[ignoreTag release];
[pendingAnchorTag release];
[anchorWrapTag release];
[lastAnchorOpenString release];
[super dealloc];
}
@@ -228,6 +238,15 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
inCSSDeclaration = NO;
hasEmbeddedCSS = NO;
embeddedCSSLevel = 0;
[pendingAnchorTag release];
[anchorWrapTag release];
[lastAnchorOpenString release];
pendingAnchorTag = nil;
anchorWrapTag = nil;
lastAnchorOpenString = nil;
anchorWrapDepth = 0;
lastAnchorOpenEnd = 0;
}
- (void) endDocument
@@ -494,6 +513,32 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
}
else
{
// If a libxml2-orphaned <a> is waiting, re-open it around the next
// block element (HTML5 pattern <a><table>...</table></a>).
// Skip if the next element is itself <a> (avoids invalid nesting).
if (pendingAnchorTag != nil)
{
if ([lowerName isEqualToString: @"a"])
{
[pendingAnchorTag release];
pendingAnchorTag = nil;
}
else
{
[result appendString: pendingAnchorTag];
[anchorWrapTag release];
anchorWrapTag = [lowerName copy];
anchorWrapDepth = 1;
[pendingAnchorTag release];
pendingAnchorTag = nil;
}
}
else if (anchorWrapTag != nil
&& [lowerName isEqualToString: anchorWrapTag])
{
anchorWrapDepth++;
}
resultPart = [NSMutableString string];
[resultPart appendFormat: @"<%@", _rawName];
@@ -599,6 +644,13 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
[resultPart appendString: @"/"];
[resultPart appendString: @">"];
[result appendString: resultPart];
if ([lowerName isEqualToString: @"a"])
{
[lastAnchorOpenString release];
lastAnchorOpenString = [resultPart copy];
lastAnchorOpenEnd = [result length];
}
}
}
}
@@ -655,8 +707,42 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
}
else
{
// Empty <a> auto-closed by libxml2 right after opening:
// strip it from result and remember it to wrap the next block.
if ([lowerName isEqualToString: @"a"]
&& lastAnchorOpenString != nil
&& [result length] == lastAnchorOpenEnd)
{
NSUInteger tagLen = [lastAnchorOpenString length];
[result deleteCharactersInRange:
NSMakeRange(lastAnchorOpenEnd - tagLen, tagLen)];
[pendingAnchorTag release];
pendingAnchorTag = lastAnchorOpenString;
lastAnchorOpenString = nil;
lastAnchorOpenEnd = 0;
return;
}
if ([lowerName isEqualToString: @"a"])
{
[lastAnchorOpenString release];
lastAnchorOpenString = nil;
lastAnchorOpenEnd = 0;
}
//NSLog (@"%@", _localName);
[result appendFormat: @"</%@>", _localName];
if (anchorWrapTag != nil
&& [lowerName isEqualToString: anchorWrapTag])
{
anchorWrapDepth--;
if (anchorWrapDepth == 0)
{
[result appendString: @"</a>"];
[anchorWrapTag release];
anchorWrapTag = nil;
}
}
}
}
}
@@ -673,9 +759,17 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content)
else if (inBody)
{
NSString *s;
s = [NSString stringWithCharacters: _chars length: _len];
if (pendingAnchorTag != nil
&& [[s stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] > 0)
{
[pendingAnchorTag release];
pendingAnchorTag = nil;
}
// HACK: This is to avoid appending the useless junk in the <html> tag
// that Outlook adds. It seems to confuse the XML parser for
// forwarded messages as we get this in the _body_ of the email
@@ -46,6 +46,9 @@
- (BOOL) isEndDateOnSameDay;
- (BOOL) hasLocation;
- (NSString *)location;
- (NSString *) organizerHref;
- (NSString *) userComment;
- (NSString *) eventDescription;
@end
+33 -1
View File
@@ -32,6 +32,7 @@
#import <NGExtensions/NSCalendarDate+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h>
#import <NGImap4/NGImap4EnvelopeAddress.h>
@@ -374,6 +375,7 @@
- (iCalEvent *) authorativeEvent
{
iCalEvent *authorativeEvent;
NSString *comment, *cleanComment;
[self storedEvent];
if (!storedEvent
@@ -382,6 +384,14 @@
else
authorativeEvent = [self storedEvent];
//Clean authorative event
if ((comment = [authorativeEvent comment])) {
cleanComment = [comment stringWithoutHTMLInjection: NO stripAngular:YES];
[authorativeEvent setComment: cleanComment];
}
return authorativeEvent;
}
@@ -408,6 +418,18 @@
return YES;
}
- (NSString *) organizerHref
{
NSString *address;
address = [[[self inEvent] organizer] rfc822Email];
if (![address length])
return nil;
return [[NSString stringWithFormat: @"mailto:%@", address]
stringByEscapingHTMLAttributeValue];
}
- (NSString *) organizerDisplayName
{
iCalPerson *organizer;
@@ -587,7 +609,7 @@
//Sanitise the html content
if([d objectForKey:@"content"]){
[d setObject: [[d objectForKey:@"content"] stringWithoutHTMLInjection: NO] forKey: @"content"];
[d setObject: [[d objectForKey:@"content"] stringWithoutHTMLInjection: NO stripAngular:NO] forKey: @"content"];
}
return d;
@@ -603,4 +625,14 @@
return [[self inEvent] location];
}
- (NSString *) userComment
{
return [[[self inEvent] userComment] stringByEscapingHTMLString];
}
- (NSString *) eventDescription
{
return [[[self authorativeEvent] comment] stringByEscapingHTMLString];
}
@end /* UIxMailPartICalViewer */
+1
View File
@@ -86,6 +86,7 @@
- (NSString *)preferredPathExtension;
- (NSString *)filename;
- (NSString *)filenameForDisplay;
- (NSString *)filenameForTitle;
- (NSFormatter *)sizeFormatter;
/* caches */
+14 -4
View File
@@ -201,7 +201,7 @@
content = [[[self generateResponse] contentAsString] stringWithoutHTMLInjection: NO];
content = [[[self generateResponse] contentAsString] stringWithoutHTMLInjection: NO stripAngular:NO];
if ([self respondsToSelector:@selector(getException)]) {
e = [self getException];
}
@@ -327,10 +327,12 @@
- (NSString *) filenameForDisplay
{
NSString *s;
NSString *s, *cleanFilename;
if ((s = [self filename]) != nil)
return s;
if ((s = [self filename]) != nil) {
cleanFilename = [s stringWithoutHTMLInjection: NO stripAngular:YES];
return cleanFilename;
}
s = [partPath componentsJoinedByString:@"-"];
return ([s length] > 0)
@@ -338,6 +340,14 @@
: (id)@"untitled";
}
- (NSString *) filenameForTitle
{
/* the generic attribute writer escapes & but not the quote, so a quote in the
filename would end the title attribute and start a new one */
return [[self filenameForDisplay] stringByReplacingOccurrencesOfString: @"\""
withString: @""];
}
- (NSFormatter *) sizeFormatter
{
return [UIxMailSizeFormatter sharedMailSizeFormatter];
+33 -33
View File
@@ -143,23 +143,23 @@
"This mail is being sent from an unsecure network!" = "A levelet nem biztonságos hálózatból készül elküldeni!";
"Address Book" = "Címjegyzék";
"Search For" = "Keresés";
"Contains" = "Contains";
"Does not contains" = "Does not contains";
"Advanced search" = "Advanced search";
"Anytime" = "Anytime";
"Last 7 days" = "Last 7 days";
"Last 30 days" = "Last 30 days";
"Last 6 month" = "Last 6 month";
"Before" = "Before";
"After" = "After";
"Between" = "Between";
"and" = "and";
"With attachments" = "With attachments";
"In favorites" = "In favorites";
"Unseen only" = "Unseen only";
"Show more" = "Show more";
"Reset" = "Reset";
"Message size" = "Message size";
"Contains" = "Tartalmazza";
"Does not contains" = "Nem tartalmazza";
"Advanced search" = "Bővített keresés";
"Anytime" = "Bármikor";
"Last 7 days" = "Előző 7 nap";
"Last 30 days" = "Előző 30 nap";
"Last 6 month" = "Előző 6 hónap";
"Before" = "Előtte";
"After" = "Utána";
"Between" = "Között";
"and" = "és";
"With attachments" = "Mellékletekkel";
"In favorites" = "Kedvencek között";
"Unseen only" = "Csak a még nem olvasott";
"Show more" = "Továbbiak megjelenítése";
"Reset" = "Alaphelyzet";
"Message size" = "Üzenet mérete";
/* Popup "show" */
"all" = "összes";
@@ -171,7 +171,7 @@
/* MailListView */
"Sender" = "Feladó";
"Subject or Sender" = "Tárgy vagy feladó";
"Subject or Recipient" = "Subject or Recipient";
"Subject or Recipient" = "Tárgy vagy címzett";
"To or Cc" = "Címzett vagy másolat";
"Entire Message" = "A teljes üzenet";
"Date" = "Dátum";
@@ -257,7 +257,7 @@
"Save As..." = "Mentés másként...";
"Print Preview" = "Nyomtatási kép";
"View Message Source" = "Üzenet forrása";
"View raw message" = "View raw message";
"View raw message" = "Nyers üzenet megtekintése";
/* Message view "more" menu: create an event from message */
"Convert To Event" = "Eseménnyé alakít";
@@ -434,7 +434,7 @@
"cannot sign message because the certificate doesn't include the specified sender address" = "Az üzenetet nem lehet aláírni, mivel a feladó címe nem található meg postafiókhoz hozzárendelt tanúsítványban.";
/* Contacts list in mail editor */
"Email" = "Email";
"Email" = "Email cím";
"More mail options" = "További levelezési tulajdonságok";
"Delegation" = "Jogok átadása";
"Add User" = "Felhasználó hozzáadása";
@@ -517,16 +517,16 @@
"hotkey_forward" = "f";
/* Email deletion */
"Clean folder" = "Clean folder";
"Clean mailbox" = "Clean mailbox";
"Delete permanently (do not use trash)" = "Delete permanently (do not use trash)";
"Apply to subfolders" = "Apply to subfolders";
"Delete e-mails older than" = "Delete e-mails older than";
"3 months" = "3 months";
"6 months" = "6 months";
"9 months" = "9 months";
"1 year" = "1 year";
"Custom" = "Custom";
"You are about to permanently delete some messages. Are you sure you want to proceed with this action?" = "You are about to permanently delete some messages. Are you sure you want to proceed with this action?";
"Apply" = "Apply";
"%{0} message(s) deleted" = "%{0} message(s) deleted";
"Clean folder" = "Mappa ürítése";
"Clean mailbox" = "Postafiók ürítése";
"Delete permanently (do not use trash)" = "Végleges törlés (ne használjon lomtárat)";
"Apply to subfolders" = "Alkalmazza az almappákra";
"Delete e-mails older than" = "Emailek törlése amelyek régebbiek mint";
"3 months" = "3 hónap";
"6 months" = "6 hónap";
"9 months" = "9 hónap";
"1 year" = "1 év";
"Custom" = "Egyéni";
"You are about to permanently delete some messages. Are you sure you want to proceed with this action?" = "Véglegesen törölni kíván néhány üzenetet. Biztosan végrehajtja a műveletet?";
"Apply" = "Alkalmaz";
"%{0} message(s) deleted" = "%{0} üzenet törölve";
+2 -2
View File
@@ -79,7 +79,7 @@
request = [context request];
params = [[request contentAsString] objectFromJSONString];
folderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES];
folderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES stripAngular:NO];
if ([folderName length] > 0)
{
encodedFolderName = [folderName stringByEncodingImap4FolderName];
@@ -145,7 +145,7 @@
// Retrieve new folder name from JSON payload
request = [context request];
params = [[request contentAsString] objectFromJSONString];
newFolderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES];
newFolderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES stripAngular:NO];
if (!newFolderName || [newFolderName length] == 0)
{
+27 -21
View File
@@ -408,6 +408,7 @@
NSString *sort, *module;
NSMutableDictionary *moduleSettings;
NSDictionary *urlParams, *sortingAttributes;
NSArray *sortValues;
SOGoUser *activeUser;
SOGoUserSettings *us;
BOOL asc, dry;
@@ -424,20 +425,24 @@
us = [activeUser userSettings];
moduleSettings = [us objectForKey: module];
if ([sort length])
{
if ([sort isEqualToString: [self defaultSortKey]] && !asc)
{
if (moduleSettings && !dry)
//Check sort value
sortValues = [NSArray arrayWithObjects: @"SUBJECT", @"FROM",
@"DATE", @"SIZE", @"ARRIVAL", nil];
if ([sort length] && [sortValues containsObject:sort])
{
if ([sort isEqualToString: [self defaultSortKey]] && !asc)
{
if (moduleSettings && !dry)
{
[moduleSettings removeObjectForKey: @"SortingState"];
[us synchronize];
}
}
else if (!dry)
{
// Save the sorting state in the user settings
if (!moduleSettings)
}
else if (!dry)
{
// Save the sorting state in the user settings
if (!moduleSettings)
{
moduleSettings = [NSMutableDictionary dictionary];
[us setObject: moduleSettings forKey: module];
@@ -445,17 +450,18 @@
[moduleSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", (asc ? 1 : 0)], nil]
forKey: @"SortingState"];
[us synchronize];
}
}
}
}
else if (moduleSettings)
{
NSArray *sortState = [moduleSettings objectForKey: @"SortingState"];
if ([sortState count])
{
sort = [[sortState objectAtIndex: 0] uppercaseString];
asc = [[sortState objectAtIndex: 1] boolValue];
}
}
{
NSArray *sortState = [moduleSettings objectForKey: @"SortingState"];
if ([sortState count])
{
sort = [[sortState objectAtIndex: 0] uppercaseString];
asc = [[sortState objectAtIndex: 1] boolValue];
}
}
if (![sort length])
sort = [self defaultSortKey];
@@ -1178,7 +1184,7 @@
[msg addObject: [NSNumber numberWithBool: [self isMessageFlagged]]];
// Subject
[msg addObject: [[[self messageSubject] stringWithoutHTMLInjection: YES] stringWithoutHTMLInjection: NO]];
[msg addObject: [[[self messageSubject] stringWithoutHTMLInjection: YES stripAngular:NO] stringWithoutHTMLInjection: NO stripAngular:NO]];
// From
from = [[message objectForKey: @"envelope"] from];
+1 -1
View File
@@ -363,7 +363,7 @@ static NSString *mailETag = nil;
if ([self formattedDate])
[data setObject: [self formattedDate] forKey: @"date"];
if ([self messageSubject])
[data setObject: [[[self messageSubject] stringWithoutHTMLInjection: YES] stringWithoutHTMLInjection: NO] forKey: @"subject"];
[data setObject: [[[self messageSubject] stringWithoutHTMLInjection: YES stripAngular:YES] stringWithoutHTMLInjection: NO stripAngular:YES] forKey: @"subject"];
if ((addresses = [addressFormatter dictionariesForArray: [co fromEnvelopeAddresses]]))
[data setObject: addresses forKey: @"from"];
if ((addresses = [addressFormatter dictionariesForArray: [co toEnvelopeAddresses]]))
+50 -50
View File
@@ -31,53 +31,53 @@
"Download" = "Letöltés";
"Language" = "Nyelv";
"choose" = "Válasszon ...";
"Arabic" = "العربية";
"Basque" = "Euskara";
"Bosnian" = "Bosanski";
"Bulgarian" = "Български";
"Catalan" = "Català";
"ChineseChina" = "简体中文";
"ChineseTaiwan" = "正體中文";
"Croatian" = "Hrvatski";
"Czech" = "Česky";
"Danish" = "Dansk (Danmark)";
"Dutch" = "Nederlands";
"English" = "English";
"Finnish" = "Suomi";
"French" = "Français";
"Galician" = "Galego";
"German" = "Deutsch";
"Greek" = "Ελληνικά";
"Hebrew" = "עברית";
"Arabic" = "Arab";
"Basque" = "Baszk";
"Bosnian" = "Bosnyák";
"Bulgarian" = "Bolgár";
"Catalan" = "Katalán";
"ChineseChina" = "Kínai (egyszerűsített)";
"ChineseTaiwan" = "Kínai (hagyományos)";
"Croatian" = "Horvát";
"Czech" = "Cseh";
"Danish" = "Dán (Dánia)";
"Dutch" = "Holland";
"English" = "Angol";
"Finnish" = "Finn";
"French" = "Francia";
"Galician" = "Galíciai";
"German" = "Német";
"Greek" = "Görög";
"Hebrew" = "Héber";
"Hungarian" = "Magyar";
"Icelandic" = "Íslenska";
"Indonesian" = "bahasa Indonesia";
"Italian" = "Italiano";
"Japanese" = "日本語";
"Korean" = "한국어";
"Kazakh" = "Қазақ";
"Latvian" = "Latviešu";
"Lithuanian" = "Lietuvių";
"Macedonian" = "Македонски";
"Montenegrin" = "Crnogorski";
"NorwegianBokmal" = "Norsk bokmål";
"NorwegianNynorsk" = "Norsk nynorsk";
"Polish" = "Polski";
"Portuguese" = "Português";
"BrazilianPortuguese" = "Português brasileiro";
"Romanian" = "Română";
"Russian" = "Русский";
"Serbian" = "Српски";
"SerbianLatin" = "Srpski";
"Slovak" = "Slovensky";
"Slovenian" = "Slovenščina";
"SpanishSpain" = "Español (España)";
"SpanishArgentina" = "Español (Argentina)";
"Swedish" = "Svenska";
"Thai" = "ภาษา";
"TurkishTurkey" = "Türkçe (Türkiye)";
"Ukrainian" = "Українська";
"Welsh" = "Cymraeg";
"Icelandic" = "Izlandi";
"Indonesian" = "Indonéz";
"Italian" = "Olasz";
"Japanese" = "Japán";
"Korean" = "Koreai";
"Kazakh" = "Kazah";
"Latvian" = "Lett";
"Lithuanian" = "Litván";
"Macedonian" = "Macedón";
"Montenegrin" = "Montenegrói";
"NorwegianBokmal" = " Norvég bokmål";
"NorwegianNynorsk" = "Norvég nynorsk";
"Polish" = "Lengyel";
"Portuguese" = "Portugál";
"BrazilianPortuguese" = "Portugál (Brazília)";
"Romanian" = "Román";
"Russian" = "Orosz";
"Serbian" = " Szerb (cirill írással)";
"SerbianLatin" = "Szerb (latin írással)";
"Slovak" = "Szlovák";
"Slovenian" = "Szlovén";
"SpanishSpain" = "Spanyol (Spanyolország)";
"SpanishArgentina" = "Spanyol (Argentína)";
"Swedish" = "Svéd";
"Thai" = "Thai";
"TurkishTurkey" = "Török (Törökország)";
"Ukrainian" = "Ukrán";
"Welsh" = "Walesi";
"About" = "Névjegy";
"AboutBox" = "Az Inverse és az Alinto által fejlesztett SOGo egy teljes funkcionalitású csoportmunka kiszolgáló, melynek fő célkitűzései az egyszerűség és az igény szerinti növekedés (skálázhatóság).<br/><br/>\nA SOGo egy AJAX alapú modern webes felhasználói felületet biztosít, továbbá szabványos protokollok alkalmazásával, mint pl. CalDAV, CardDAV támogatja telepített alkalmazások használatát is. <br/><br/>\nA SOGo a <a href=\"http://gnu.org/licenses/gpl.html\">GNU GPL</a> 2. ill. magasabb verziói, továbbá egyes részei a GNU LGPL 2. verziója alatt kerül kiadásra. Ez egy ingyenes szoftver, a módosítása és újra kiadása megengedett.NINCS GARANCIA, a törvény által megengedett mértékben.<br/><br/>\nLátogassa meg <a href=\"http://www.sogo.nu/en/support/community.html\">ezt az oldalt</a> a különböző támogatási lehetőségek megismeréséhez. ";
@@ -87,7 +87,7 @@
"Change your Password" = "Változtassa meg jelszavát";
"The password was changed successfully." = "A jelszó megváltoztatása sikeres.";
"Your password has expired, please enter a new one below" = "A jelszava lejárt, kérem adjon meg egy újat";
"Your password is too weak. Please choose a stronger password to enhance your security" = "Your password is too weak. Please choose a stronger password to enhance your security";
"Your password is too weak. Please choose a stronger password to enhance your security" = "A megadott jelszó túl gyenge. Kérjük, válasszon erősebb jelszót a nagyobb biztonság érdekében";
"Password must not be empty." = "A jelszó nem lehet üres.";
"The passwords do not match. Please try again." = "Jelszavak nem egyeznek meg, kérem próbálja meg újra.";
"Password Grace Period" = "Jelszóváltoztatás türelmi időszak";
@@ -108,11 +108,11 @@
"Unhandled error response" = "Nem kezelt hiba";
"Password change is not supported." = "A jelszó megváltoztatása nem támogatott.";
"Unhandled HTTP error code: %{0}" = "Nem kezelt HTTP hibakód: /{0}";
"Current password" = "Current password";
"Current password" = "Jelenlegi jelszó";
"New password" = "Új jelszó";
"Passwords don't match" = "Passwords don't match";
"Passwords don't match" = "Jelszavak nem egyeznek meg";
"Confirmation" = "Megerősítés";
"Change" = "Change";
"Change" = "Módosítás";
"Cancel" = "Törlés";
"Please wait..." = "Kérem várjon ...";
"Close" = "Bezárás";
+38 -12
View File
@@ -1091,7 +1091,7 @@ static const NSString *kJwtKey = @"jwt";
}
//Check common injection
loginClean = [login stringWithoutHTMLInjection: YES];
loginClean = [login stringWithoutHTMLInjection: YES stripAngular:NO];
if(![loginClean isEqualToString: login])
{
loginClean = @"";
@@ -1316,7 +1316,8 @@ static const NSString *kJwtKey = @"jwt";
*/
- (WOResponse *) passwordRecoveryEmailAction
{
NSString *username, *domain, *mode, *uid, *mailDomain, *fromEmail, *toEmail, *jwtToken, *url, *mailContent;
NSString *username, *domain, *mode, *uid, *mailDomain, *fromEmail, *toEmail, *jwtToken, *url, *mailContent, *email, *serverUrl;
NSArray *baseUrls;
NSDictionary *message, *info;
WORequest *request;
SOGoUserManager *um;
@@ -1333,7 +1334,6 @@ static const NSString *kJwtKey = @"jwt";
message = [[request contentAsString] objectFromJSONString];
username = [message objectForKey: @"userName"];
domain = [message objectForKey: @"domain"];
mailDomain = [message objectForKey: @"mailDomain"];
mode = [message objectForKey: @"mode"];
um = [SOGoUserManager sharedUserManager];
jwtToken = [request formValueForKey:@"token"];
@@ -1345,14 +1345,6 @@ static const NSString *kJwtKey = @"jwt";
ownerUser = [SOGoUser userWithLogin: username];
dd = [ownerUser domainDefaults];
// Email recovery
// Create email from
if (mailDomain) {
fromEmail = [NSString stringWithFormat:@"noreply@%@", mailDomain];
} else {
fromEmail = [dd passwordRecoveryFrom];
}
// Get password recovery email
info = [um contactInfosForUserWithUIDorEmail: username];
@@ -1361,14 +1353,48 @@ static const NSString *kJwtKey = @"jwt";
inDomain: domain];
toEmail = [userDefaults passwordRecoverySecondaryEmail];
//get fromEmail domain
email = [info objectForKey: @"c_email"];
// Create email from
mailDomain = [email mailDomain];
if (mailDomain) {
fromEmail = [NSString stringWithFormat:@"noreply@%@", mailDomain];
} else {
fromEmail = [dd passwordRecoveryFrom];
}
if (toEmail) {
// Generate token
jwtToken = [um generateAndSavePasswordRecoveryTokenWithUid: uid username: username domain: domain];
// Send mail
mailer = [SOGoMailer mailerWithDomainDefaults: dd];
//Get Allowed server URL and check if it's OK
baseUrls = [[SOGoSystemDefaults sharedSystemDefaults] passwordRecoveryBaseURLs];
serverUrl = [[request headers] objectForKey:@"origin"];
if(!(baseUrls && [baseUrls count] > 0))
{
[self logWithFormat: @"Password recovery exception for user %@: empty or unset SOGoPasswordRecoveryBaseURLs", uid];
response = [self responseWithStatus: 403
andString: @"Password recovery email in error"];
return response;
}
if(![baseUrls containsObject:serverUrl])
{
[self logWithFormat: @"Password recovery exception for user %@: invalid request header", uid];
response = [self responseWithStatus: 403
andString: @"Password recovery email in error"];
return response;
}
url = [NSString stringWithFormat:@"%@%@?token=%@"
, [[request headers] objectForKey:@"origin"]
, serverUrl
, [request uri]
, jwtToken];
@@ -152,7 +152,7 @@
"Contacts Names" = "Kapcsolatok nevei";
"Duplicate name" = "Név kettőzése";
"Calendar categories must have unique names." = "Naptár kategóriáknak egyedi névvel kell rendelkezniük.";
"Disable e-mail notifications on CalDAV" = "Disable e-mail notifications on CalDAV";
"Disable e-mail notifications on CalDAV" = "E-mail értesítések kikapcsolása a CalDAV esetén";
/* Default Calendar */
"Default calendar" = "Alapértelmezett naptár";
@@ -205,9 +205,9 @@
"Insert signature on new message" = "Aláírás beszúrása új levél esetén";
"Insert signature on reply" = "Aláírás beszúrása válasz esetén";
"Insert signature on forward" = "Aláírás beszúrása továbbítás esetén";
"Show recipients or sender full email in mailboxes" = "Show recipients or sender full email in mailboxes";
"Hide attachements for inline images" = "Hide attachments for inline images";
"Show attachments above the message" = "Show attachments above the message";
"Show recipients or sender full email in mailboxes" = "A címzettek vagy a feladó teljes e-mail-címének megjelenítése a postafiókokban";
"Hide attachements for inline images" = "Beágyazott képek mellékleteinek elrejtése";
"Show attachments above the message" = "Mellékletek megjelenítése az üzenet felett";
/* Base font size for messages composed in HTML */
"Default font size" = "Alapértelmezett betűméret";
@@ -233,11 +233,11 @@
"Mail Account" = "Levelezési fiók";
"New Mail Account" = "Új email fiók";
"Server Name" = "Kiszolgáló neve";
"Outgoing Server Name (SMTP)" = "Outgoing Server Name (SMTP)";
"Smtp Auth" = "Smtp Auth";
"Outgoing Server Name (SMTP)" = "Levéltovábbító kiszolgáló neve (SMTP)";
"Smtp Auth" = "SMTP hitelesítés";
"Port" = "Port";
"Encryption" = "Titkosítás";
"Outgoing Encryption" = "Outgoing Encryption";
"Outgoing Encryption" = "Kimenő levél titkosítása";
"None" = "Nincs";
"User Name" = "Felhasználónév";
"Full Name" = "Teljes név";
@@ -315,45 +315,45 @@
"Catalan" = "Català";
"ChineseChina" = "简体中文";
"ChineseTaiwan" = "正體中文";
"Croatian" = "Hrvatski";
"Croatian" = "Horvát";
"Czech" = "Česky";
"Danish" = "Dansk (Danmark)";
"Dutch" = "Nederlands";
"English" = "English";
"Finnish" = "Suomi";
"French" = "Français";
"Galician" = "Galego";
"German" = "Deutsch";
"Hebrew" = "עברית";
"Danish" = "Dán (Dánia)";
"Dutch" = "Holland";
"English" = "Angol";
"Finnish" = "Finn";
"French" = "Francia";
"Galician" = "Galíciai";
"German" = "Német";
"Hebrew" = "Héber";
"Hungarian" = "Magyar";
"Icelandic" = "Íslenska";
"Indonesian" = "bahasa Indonesia";
"Italian" = "Italiano";
"Japanese" = "日本語";
"Kazakh" = "Қазақ";
"Korean" = "한국어";
"Latvian" = "Latviešu";
"Lithuanian" = "Lietuvių";
"Macedonian" = "Македонски";
"Montenegrin" = "Crnogorski";
"NorwegianBokmal" = "Norsk bokmål";
"NorwegianNynorsk" = "Norsk nynorsk";
"Polish" = "Polski";
"Portuguese" = "Português";
"BrazilianPortuguese" = "Português brasileiro";
"Romanian" = "Română";
"Russian" = "Русский";
"Serbian" = "Српски";
"SerbianLatin" = "Srpski";
"Slovak" = "Slovensky";
"Slovenian" = "Slovenščina";
"SpanishSpain" = "Español (España)";
"SpanishArgentina" = "Español (Argentina)";
"Swedish" = "Svenska";
"Thai" = "ภาษา";
"TurkishTurkey" = "Türkçe (Türkiye)";
"Ukrainian" = "Українська";
"Welsh" = "Cymraeg";
"Icelandic" = "Izlandi";
"Indonesian" = "Indonéz";
"Italian" = "Olasz";
"Japanese" = "Japán";
"Kazakh" = "Kazah";
"Korean" = "Koreai";
"Latvian" = "Lett";
"Lithuanian" = "Litván";
"Macedonian" = "Macedón";
"Montenegrin" = "Montenegrói";
"NorwegianBokmal" = " Norvég bokmål";
"NorwegianNynorsk" = "Norvég nynorsk";
"Polish" = "Lengyel";
"Portuguese" = "Portugál";
"BrazilianPortuguese" = "Portugál (Brazília)";
"Romanian" = "Román";
"Russian" = "Orosz";
"Serbian" = " Szerb (cirill írással)";
"SerbianLatin" = "Szerb (latin írással)";
"Slovak" = "Szlovák";
"Slovenian" = "Szlovén";
"SpanishSpain" = "Spanyol (Spanyolország)";
"SpanishArgentina" = "Spanyol (Argentína)";
"Swedish" = "Svéd";
"Thai" = "Thai";
"TurkishTurkey" = "Török (Törökország)";
"Ukrainian" = "Ukrán";
"Welsh" = "Walesi";
"Refresh View" = "Nézet frissítése";
"refreshview_manually" = "Kézi";
@@ -435,7 +435,7 @@
"Stop processing filter rules" = "Szűrő szabályok feldolgozásának befejezése";
"is under" = "kisebb, mint";
"is over" = "nagyobb, mint";
"is" = "is";
"is" = "-";
"is not" = "nem";
"contains" = "tartalmaz";
"does not contain" = "nem tartalmaz";
@@ -487,8 +487,8 @@
"none" = "Nincs";
"identicon" = "Azonosító ikon";
"monsterid" = "Szörny";
"wavatar" = "Wavatar";
"retro" = "Retro";
"wavatar" = "Wavatár";
"retro" = "Retró";
/* Animation Level */
"Animation Level" = "Animáció szintje";
@@ -511,7 +511,7 @@
"Let SOGo handle everything" = "A SOGo kezeljen mindent";
/* Password Recovery */
"Password recovery mode" = "Password recovery mode";
"Password recovery mode" = "Jelszó helyreállítás módszere";
"Question" = "Kérdés";
"Secondary e-mail" = "Másodlagos email cím";
"Answer" = "Válasz";
+3
View File
@@ -344,6 +344,9 @@ static SoProduct *preferencesProduct = nil;
[[defaults source] setObject: [domainDefaults calendarJitsiRoomPrefix] forKey: @"SOGoCalendarJitsiRoomPrefix"];
}
//Add the Junk Icon pref
[[defaults source] setObject: [domainDefaults mailJunkIcon] forKey: @"mailJunkIcon"];
//
// Default Contacts preferences
//
+1 -1
View File
@@ -1750,7 +1750,7 @@ static NSArray *reminderValues = nil;
id o, v;
requestStr = [[context request] contentAsString];
requestStr = [requestStr stringWithoutHTMLInjection: NO];
requestStr = [requestStr stringWithoutHTMLInjection: NO stripAngular:NO];
o = [requestStr objectFromJSONString];
results = nil;
+1 -1
View File
@@ -403,7 +403,7 @@ static SoProduct *commonProduct = nil;
theme = [[context request] formValueForKey: @"theme"];
if ([theme length])
{
safeTheme = [theme stringWithoutHTMLInjection: YES];
safeTheme = [theme stringWithoutHTMLInjection: YES stripAngular:NO];
if([safeTheme isEqualToString: theme])
rel = [NSString stringWithFormat: @"%@?theme=%@", rel, theme];
}
@@ -65,7 +65,7 @@
"A total of %{0} events were imported in the calendar." = "Összesen %{0} esemény lett importálva a naptárba.";
"Compose E-Mail to All Attendees" = "Üzenet küldése az összes résztvevőnek";
"Compose E-Mail to Undecided Attendees" = "Üzenet küldése az bizonytalan résztvevőnek";
"Download iOS - MacOS configuration profile" = "Download iOS - MacOS configuration profile";
"Download iOS - MacOS configuration profile" = "iOS - MacOS konfigurációs profil letöltése";
/* Relative dates */
"Yesterday" = "Tegnap";
@@ -117,7 +117,7 @@
"Save and Close" = "Mentés és bezárás";
"Close" = "Bezárás";
"Invite Attendees" = "Résztvevők meghívása";
"Create Jitsi Meeting" = "Create Jitsi Meeting";
"Create Jitsi Meeting" = "Jitsi megbeszélés létrehozása";
"Attach" = "Csatolás";
"Update" = "Mentés";
"Cancel" = "Mégsem";
@@ -163,7 +163,7 @@
"End" = "Vége";
"Due Date" = "Lejárat";
"Name" = "Név";
"Email" = "Email";
"Email" = "Email cím";
"Status" = "Állapot";
"% complete" = "% kész";
"Location" = "Hely";
@@ -378,7 +378,7 @@
"Show an Alert" = "Riasztás";
"Send an E-mail" = "Email küldése";
"Email Organizer" = "Email szervező";
"Email Attendees (internal users)" = "Email Attendees (internal users)";
"Email Attendees (internal users)" = "Email résztvevők (belső felhasználók)";
"zoom_400" = "400%";
"zoom_200" = "200%";
"zoom_100" = "100%";
@@ -509,7 +509,7 @@ validate_untilbeforeend = "Az ismétlődésnek az első előfordulás után k
"Work days only" = "Csak munkanapok";
"The whole day" = "Az egész nap";
"Between" = "Között";
"and" = "and";
"and" = "és";
"A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?"
= "Egy vagy több résztvevő között időütközés van.\nMegtartja ennek ellenére a beállításokat?";
"Would you like to remove them and send the invitation to the remaining attendees?"
@@ -611,7 +611,7 @@ vtodo_class2 = "(Bizalmas feladat)";
"Select ending date" = "Válasszon befejezés dátumot";
"Delegated to" = "Jogok átruházása az alábiaknak";
"Keep sending me updates" = "Továbbiakban küldjön frissítéseket";
"OK" = "OK";
"OK" = "Rendben";
"Confidential" = "Bizalmas";
"Enable" = "Engedélyez";
"Filter" = "Szűrő";
+1 -1
View File
@@ -556,7 +556,7 @@
ex = nil;
request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: NO] objectFromJSONString];
params = [[[request contentAsString] stringWithoutHTMLInjection: NO stripAngular:NO] objectFromJSONString];
if (params == nil)
{
ex = [NSException exceptionWithName: @"JSONParsingException"
+14 -2
View File
@@ -148,14 +148,26 @@
*/
- (WOResponse *) reloadAction
{
NSDictionary *results;
NSMutableDictionary *results;
NSString *error;
unsigned int httpCode;
httpCode = 200;
results = [[self clientObject] loadWebCalendar];
if ([results objectForKey: @"status"])
httpCode = [[results objectForKey: @"status"] intValue];
{
if ([results objectForKey: @"error"])
{
error = [results objectForKey: @"error"];
if ([error isEqualToString: @"need-auth"])
httpCode = 401;
if ([error isEqualToString: @"invalid-calendar-content"] || [error isEqualToString: @"http-error"])
httpCode = 500;
}
[results removeObjectForKey:@"status"];
}
return [self responseWithStatus: httpCode andJSONRepresentation: results];
}
+8 -1
View File
@@ -786,12 +786,19 @@ static NSArray *tasksFields = nil;
- (void) saveSortValue: (NSString *) submodule
{
NSString *sort, *ascending;
NSArray *sortValues;
SOGoUserSettings *us;
NSMutableDictionary *calendarSettings;
sort = [[context request] formValueForKey: @"sort"];
ascending = [[context request] formValueForKey: @"asc"];
if ([sort length])
//Check value of sorting
sortValues = [NSArray arrayWithObjects: @"title", @"location",
@"calendarName", @"start", @"priority", @"category", @"status", @"end", nil];
if ([sort length] && [sortValues containsObject:sort])
{
us = [[context activeUser] userSettings];
calendarSettings = [us objectForKey: @"Calendar"];
+9
View File
@@ -254,6 +254,7 @@
{
WORequest *request;
NSDictionary *params;
NSArray *listValues;
NSString *selectedList;
[self _setupContext];
@@ -261,6 +262,14 @@
params = [[request contentAsString] objectFromJSONString];
selectedList = [params objectForKey: @"list"];
//Check value
listValues = [NSArray arrayWithObjects: @"eventsListView", @"tasksListView", nil];
if([selectedList length] && ![listValues containsObject:selectedList])
{
selectedList = @"eventsListView";
}
[moduleSettings setObject: selectedList
forKey: @"SelectedList"];
[us synchronize];
+1 -1
View File
@@ -182,7 +182,7 @@ _intValueFromHex (NSString *hexString)
fActiveTasks = [folder activeTasks];
[calendar setObject: folderName forKey: @"id"];
[calendar setObject: [fDisplayName stringWithoutHTMLInjection: YES] forKey: @"name"];
[calendar setObject: [fDisplayName stringWithoutHTMLInjection: YES stripAngular:NO] forKey: @"name"];
[calendar setObject: [folder calendarColor] forKey: @"color"];
isActive = [NSNumber numberWithBool: [folder isActive]];
[calendar setObject: isActive forKey: @"active"];
+1 -1
View File
@@ -335,7 +335,7 @@
ex = nil;
request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: NO] objectFromJSONString];
params = [[[request contentAsString] stringWithoutHTMLInjection: NO stripAngular:NO] objectFromJSONString];
if (params == nil)
{
ex = [NSException exceptionWithName: @"JSONParsingException"
@@ -17,7 +17,7 @@
</div>
</md-toolbar>
<md-whiteframe class="md-whiteframe-z1" layout="row" layout-align="space-between center">
<pre><var:string value="flatContentAsString" /></pre>
<pre ng-non-bindable=""><var:string value="flatContentAsString" /></pre>
</md-whiteframe>
</div>
</var:if> <!-- if condition="couldParseCalendar" const:negate="1" -->
@@ -137,7 +137,7 @@
<p>
<var:string label:value="Organizer" />
<a var:href="inEvent.organizer.email"
<a ng-non-bindable="" var:href="organizerHref"
><var:string value="organizerDisplayName" /></a>
<var:string label:value="request_info" />
</p>
@@ -147,7 +147,7 @@
<var:if condition="isLoggedInUserAnAttendee" const:negate="YES">
<p>
<var:string label:value="Organizer" />
<a var:href="inEvent.organizer.email">
<a ng-non-bindable="" var:href="organizerHref">
<var:string value="organizerDisplayName" />
</a>
<var:string label:value="request_info_no_attendee" />
@@ -233,14 +233,14 @@
</var:if>
<!-- the user comment is used in replies -->
<var:if condition="inEvent.userComment.isNotEmpty">
<var:if condition="userComment.isNotEmpty">
<div class="pseudo-input-container">
<label class="pseudo-input-label">
<var:string label:value="Comment"/>
</label>
<div>
<div ng-non-bindable="">
<md-content>
<var:string value="inEvent.userComment.stringByDetectingURLs" const:insertBR="1" const:escapeHTML="NO"/>
<var:string value="userComment.stringByDetectingURLs" const:insertBR="1" const:escapeHTML="NO"/>
</md-content>
</div>
</div>
@@ -251,7 +251,7 @@
<var:if condition="hasOrganizer" const:value="true">
<div class="pseudo-input-container">
<label class="pseudo-input-label"><var:string label:value="Organizer"/></label>
<div>
<div ng-non-bindable="">
<var:string value="organizerDisplayName"/>
</div>
</div>
@@ -268,7 +268,7 @@
<var:if condition="hasLocation">
<div class="pseudo-input-container">
<label class="pseudo-input-label"><var:string label:value="Location"/></label>
<div>
<div ng-non-bindable="">
<var:string value="location"/>
</div>
</div>
@@ -294,14 +294,14 @@
</div>
</md-list>
<var:if condition="authorativeEvent.comment.isNotEmpty">
<var:if condition="eventDescription.isNotEmpty">
<div class="pseudo-input-container">
<label class="pseudo-input-label">
<var:string label:value="Description"/>
</label>
<div>
<div ng-non-bindable="">
<md-content>
<var:string value="authorativeEvent.comment.stringByDetectingURLs" const:insertBR="1" const:escapeHTML="NO"/>
<var:string value="eventDescription.stringByDetectingURLs" const:insertBR="1" const:escapeHTML="NO"/>
</md-content>
</div>
</div>
@@ -10,7 +10,7 @@
<img var:src="pathToAttachment"
var:title="filenameForDisplay"><!-- image --></img>
<md-card-content>
<p class="md-caption" var:title="filenameForDisplay">
<p class="md-caption" var:title="filenameForTitle">
<var:string value="filenameForDisplay" />
</p>
</md-card-content>
@@ -6,7 +6,7 @@
xmlns:label="OGo:label">
<md-card>
<md-card-content>
<p class="md-caption sg-attachment-name" var:title="filenameForDisplay">
<p class="md-caption sg-attachment-name" var:title="filenameForTitle">
<var:if condition="preferredPathExtension.length"><span class="sg-label-outline"><var:string value="preferredPathExtension"/></span></var:if>
<var:string value="filenameForDisplay"/>
</p>
@@ -181,7 +181,7 @@
<md-icon>delete</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="mailbox.markOrUnMarkMessagesAsJunk()">
<md-icon ng-hide="::mailbox.service.selectedFolder.type == 'junk'">thumb_down</md-icon>
<md-icon ng-hide="::mailbox.service.selectedFolder.type == 'junk'">{{ mailbox.junkIcon}}</md-icon>
<md-tooltip ng-hide="::mailbox.service.selectedFolder.type == 'junk'" md-direction="bottom"><var:string label:value="Junk"/></md-tooltip>
<md-icon ng-hide="::mailbox.service.selectedFolder.type != 'junk'">thumb_up</md-icon>
<md-tooltip ng-hide="::mailbox.service.selectedFolder.type != 'junk'" md-direction="bottom"><var:string label:value="Not junk"/></md-tooltip>
@@ -45,6 +45,14 @@
<md-icon class="ng-animate-disabled" ng-switch-when="true" rsrc:md-svg-src="img/mark_email_unread.svg"><!-- mark as unread --></md-icon>
<md-icon class="ng-animate-disabled" ng-switch-default="true" rsrc:md-svg-src="img/mark_email_read.svg"><!-- mark as read --></md-icon>
</md-button>
<md-button class="sg-icon-button" label:aria-label="Junk"
ng-hide="::viewer.message.isDraft"
ng-click="viewer.markOrUnMarkAsJunk()">
<md-tooltip md-direction="bottom" ng-hide="::viewer.mailbox.type == 'junk'"><var:string label:value="Junk"/></md-tooltip>
<md-tooltip md-direction="bottom" ng-show="::viewer.mailbox.type == 'junk'"><var:string label:value="Not junk"/></md-tooltip>
<md-icon ng-hide="::viewer.mailbox.type == 'junk'">{{ viewer.junkIcon }}</md-icon>
<md-icon ng-show="::viewer.mailbox.type == 'junk'">thumb_up</md-icon>
</md-button>
<md-button class="sg-icon-button"
ng-hide="::viewer.message.isDraft"
ng-click="viewer.reply($event)"
+1 -1
View File
@@ -1,2 +1,2 @@
@charset "UTF-8";.ng-cloak,.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}.sg-hide{opacity:0}@-moz-document url-prefix(){[layout-fill]{margin:0;width:100%;min-height:100%;height:100%}}[flex-order]{-webkit-box-ordinal-group:1;-webkit-order:0;order:0}[flex-order="-20"]{-webkit-box-ordinal-group:-19;-webkit-order:-20;order:-20}[flex-order="-19"]{-webkit-box-ordinal-group:-18;-webkit-order:-19;order:-19}[flex-order="-18"]{-webkit-box-ordinal-group:-17;-webkit-order:-18;order:-18}[flex-order="-17"]{-webkit-box-ordinal-group:-16;-webkit-order:-17;order:-17}[flex-order="-16"]{-webkit-box-ordinal-group:-15;-webkit-order:-16;order:-16}[flex-order="-15"]{-webkit-box-ordinal-group:-14;-webkit-order:-15;order:-15}[flex-order="-14"]{-webkit-box-ordinal-group:-13;-webkit-order:-14;order:-14}[flex-order="-13"]{-webkit-box-ordinal-group:-12;-webkit-order:-13;order:-13}[flex-order="-12"]{-webkit-box-ordinal-group:-11;-webkit-order:-12;order:-12}[flex-order="-11"]{-webkit-box-ordinal-group:-10;-webkit-order:-11;order:-11}[flex-order="-10"]{-webkit-box-ordinal-group:-9;-webkit-order:-10;order:-10}[flex-order="-9"]{-webkit-box-ordinal-group:-8;-webkit-order:-9;order:-9}[flex-order="-8"]{-webkit-box-ordinal-group:-7;-webkit-order:-8;order:-8}[flex-order="-7"]{-webkit-box-ordinal-group:-6;-webkit-order:-7;order:-7}[flex-order="-6"]{-webkit-box-ordinal-group:-5;-webkit-order:-6;order:-6}[flex-order="-5"]{-webkit-box-ordinal-group:-4;-webkit-order:-5;order:-5}[flex-order="-4"]{-webkit-box-ordinal-group:-3;-webkit-order:-4;order:-4}[flex-order="-3"]{-webkit-box-ordinal-group:-2;-webkit-order:-3;order:-3}[flex-order="-2"]{-webkit-box-ordinal-group:-1;-webkit-order:-2;order:-2}[flex-order="-1"]{-webkit-box-ordinal-group:0;-webkit-order:-1;order:-1}[flex-order="0"]{-webkit-box-ordinal-group:1;-webkit-order:0;order:0}[flex-order="1"]{-webkit-box-ordinal-group:2;-webkit-order:1;order:1}[flex-order="2"]{-webkit-box-ordinal-group:3;-webkit-order:2;order:2}[flex-order="3"]{-webkit-box-ordinal-group:4;-webkit-order:3;order:3}[flex-order="4"]{-webkit-box-ordinal-group:5;-webkit-order:4;order:4}[flex-order="5"]{-webkit-box-ordinal-group:6;-webkit-order:5;order:5}[flex-order="6"]{-webkit-box-ordinal-group:7;-webkit-order:6;order:6}[flex-order="7"]{-webkit-box-ordinal-group:8;-webkit-order:7;order:7}[flex-order="8"]{-webkit-box-ordinal-group:9;-webkit-order:8;order:8}[flex-order="9"]{-webkit-box-ordinal-group:10;-webkit-order:9;order:9}[flex-order="10"]{-webkit-box-ordinal-group:11;-webkit-order:10;order:10}[flex-order="11"]{-webkit-box-ordinal-group:12;-webkit-order:11;order:11}[flex-order="12"]{-webkit-box-ordinal-group:13;-webkit-order:12;order:12}[flex-order="13"]{-webkit-box-ordinal-group:14;-webkit-order:13;order:13}[flex-order="14"]{-webkit-box-ordinal-group:15;-webkit-order:14;order:14}[flex-order="15"]{-webkit-box-ordinal-group:16;-webkit-order:15;order:15}[flex-order="16"]{-webkit-box-ordinal-group:17;-webkit-order:16;order:16}[flex-order="17"]{-webkit-box-ordinal-group:18;-webkit-order:17;order:17}[flex-order="18"]{-webkit-box-ordinal-group:19;-webkit-order:18;order:18}[flex-order="19"]{-webkit-box-ordinal-group:20;-webkit-order:19;order:19}[flex-order="20"]{-webkit-box-ordinal-group:21;-webkit-order:20;order:20}[flex-offset="0"]{margin-left:0}[flex-offset="5"]{margin-left:5%}[flex-offset="10"]{margin-left:10%}[flex-offset="15"]{margin-left:15%}[flex-offset="20"]{margin-left:20%}[flex-offset="25"]{margin-left:25%}[flex-offset="30"]{margin-left:30%}[flex-offset="35"]{margin-left:35%}[flex-offset="40"]{margin-left:40%}[flex-offset="45"]{margin-left:45%}[flex-offset="50"]{margin-left:50%}[flex-offset="55"]{margin-left:55%}[flex-offset="60"]{margin-left:60%}[flex-offset="65"]{margin-left:65%}[flex-offset="70"]{margin-left:70%}[flex-offset="75"]{margin-left:75%}[flex-offset="80"]{margin-left:80%}[flex-offset="85"]{margin-left:85%}[flex-offset="90"]{margin-left:90%}[flex-offset="95"]{margin-left:95%}[flex-offset="33"]{margin-left:33.3333333333%}[flex-offset="66"]{margin-left:66.6666666667%}[layout-align="start stretch"],[layout-align]{-webkit-box-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-box-align:stretch;-webkit-align-items:stretch;align-items:stretch}[layout-align="start center"],[layout-align="start end"],[layout-align="start start"],[layout-align="start stretch"],[layout-align=start]{-webkit-box-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start}[layout-align="center center"],[layout-align="center end"],[layout-align="center start"],[layout-align="center stretch"],[layout-align=center]{-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}[layout-align="end center"],[layout-align="end end"],[layout-align="end start"],[layout-align="end stretch"],[layout-align=end]{-webkit-box-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end}[layout-align="spacLine truncated
@charset "UTF-8";.ng-cloak,.x-ng-cloak,[data-ng-cloak],[ng-cloak],[ng\:cloak],[x-ng-cloak]{display:none!important}.sg-hide{opacity:0}@-moz-document url-prefix(){[layout-fill]{margin:0;width:100%;min-height:100%;height:100%}}[flex-order]{-webkit-box-ordinal-group:1;-webkit-order:0;order:0}[flex-order="-20"]{-webkit-box-ordinal-group:-19;-webkit-order:-20;order:-20}[flex-order="-19"]{-webkit-box-ordinal-group:-18;-webkit-order:-19;order:-19}[flex-order="-18"]{-webkit-box-ordinal-group:-17;-webkit-order:-18;order:-18}[flex-order="-17"]{-webkit-box-ordinal-group:-16;-webkit-order:-17;order:-17}[flex-order="-16"]{-webkit-box-ordinal-group:-15;-webkit-order:-16;order:-16}[flex-order="-15"]{-webkit-box-ordinal-group:-14;-webkit-order:-15;order:-15}[flex-order="-14"]{-webkit-box-ordinal-group:-13;-webkit-order:-14;order:-14}[flex-order="-13"]{-webkit-box-ordinal-group:-12;-webkit-order:-13;order:-13}[flex-order="-12"]{-webkit-box-ordinal-group:-11;-webkit-order:-12;order:-12}[flex-order="-11"]{-webkit-box-ordinal-group:-10;-webkit-order:-11;order:-11}[flex-order="-10"]{-webkit-box-ordinal-group:-9;-webkit-order:-10;order:-10}[flex-order="-9"]{-webkit-box-ordinal-group:-8;-webkit-order:-9;order:-9}[flex-order="-8"]{-webkit-box-ordinal-group:-7;-webkit-order:-8;order:-8}[flex-order="-7"]{-webkit-box-ordinal-group:-6;-webkit-order:-7;order:-7}[flex-order="-6"]{-webkit-box-ordinal-group:-5;-webkit-order:-6;order:-6}[flex-order="-5"]{-webkit-box-ordinal-group:-4;-webkit-order:-5;order:-5}[flex-order="-4"]{-webkit-box-ordinal-group:-3;-webkit-order:-4;order:-4}[flex-order="-3"]{-webkit-box-ordinal-group:-2;-webkit-order:-3;order:-3}[flex-order="-2"]{-webkit-box-ordinal-group:-1;-webkit-order:-2;order:-2}[flex-order="-1"]{-webkit-box-ordinal-group:0;-webkit-order:-1;order:-1}[flex-order="0"]{-webkit-box-ordinal-group:1;-webkit-order:0;order:0}[flex-order="1"]{-webkit-box-ordinal-group:2;-webkit-order:1;order:1}[flex-order="2"]{-webkit-box-ordinal-group:3;-webkit-order:2;order:2}[flex-order="3"]{-webkit-box-ordinal-group:4;-webkit-order:3;order:3}[flex-order="4"]{-webkit-box-ordinal-group:5;-webkit-order:4;order:4}[flex-order="5"]{-webkit-box-ordinal-group:6;-webkit-order:5;order:5}[flex-order="6"]{-webkit-box-ordinal-group:7;-webkit-order:6;order:6}[flex-order="7"]{-webkit-box-ordinal-group:8;-webkit-order:7;order:7}[flex-order="8"]{-webkit-box-ordinal-group:9;-webkit-order:8;order:8}[flex-order="9"]{-webkit-box-ordinal-group:10;-webkit-order:9;order:9}[flex-order="10"]{-webkit-box-ordinal-group:11;-webkit-order:10;order:10}[flex-order="11"]{-webkit-box-ordinal-group:12;-webkit-order:11;order:11}[flex-order="12"]{-webkit-box-ordinal-group:13;-webkit-order:12;order:12}[flex-order="13"]{-webkit-box-ordinal-group:14;-webkit-order:13;order:13}[flex-order="14"]{-webkit-box-ordinal-group:15;-webkit-order:14;order:14}[flex-order="15"]{-webkit-box-ordinal-group:16;-webkit-order:15;order:15}[flex-order="16"]{-webkit-box-ordinal-group:17;-webkit-order:16;order:16}[flex-order="17"]{-webkit-box-ordinal-group:18;-webkit-order:17;order:17}[flex-order="18"]{-webkit-box-ordinal-group:19;-webkit-order:18;order:18}[flex-order="19"]{-webkit-box-ordinal-group:20;-webkit-order:19;order:19}[flex-order="20"]{-webkit-box-ordinal-group:21;-webkit-order:20;order:20}[flex-offset="0"]{margin-left:0}[flex-offset="5"]{margin-left:5%}[flex-offset="10"]{margin-left:10%}[flex-offset="15"]{margin-left:15%}[flex-offset="20"]{margin-left:20%}[flex-offset="25"]{margin-left:25%}[flex-offset="30"]{margin-left:30%}[flex-offset="35"]{margin-left:35%}[flex-offset="40"]{margin-left:40%}[flex-offset="45"]{margin-left:45%}[flex-offset="50"]{margin-left:50%}[flex-offset="55"]{margin-left:55%}[flex-offset="60"]{margin-left:60%}[flex-offset="65"]{margin-left:65%}[flex-offset="70"]{margin-left:70%}[flex-offset="75"]{margin-left:75%}[flex-offset="80"]{margin-left:80%}[flex-offset="85"]{margin-left:85%}[flex-offset="90"]{margin-left:90%}[flex-offset="95"]{margin-left:95%}[flex-offset="33"]{margin-left:33.3333333333%}[flex-offset="66"]{margin-left:66.6666666667%}[layout-align="start stretch"],[layout-align]{-webkit-box-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start;-webkit-align-content:stretch;align-content:stretch;-webkit-box-align:stretch;-webkit-align-items:stretch;align-items:stretch}[layout-align="start center"],[layout-align="start end"],[layout-align="start start"],[layout-align="start stretch"],[layout-align=start]{-webkit-box-pack:start;-webkit-justify-content:flex-start;justify-content:flex-start}[layout-align="center center"],[layout-align="center end"],[layout-align="center start"],[layout-align="center stretch"],[layout-align=center]{-webkit-box-pack:center;-webkit-justify-content:center;justify-content:center}[layout-align="end center"],[layout-align="end end"],[layout-align="end start"],[layout-align="end stretch"],[layout-align=end]{-webkit-box-pack:end;-webkit-justify-content:flex-end;justify-content:flex-end}[layout-align="spacLine truncated
/*# sourceMappingURL=styles.css.map */
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["css/styles.css"],"names":[],"mappings":"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 truncated
{"version":3,"sources":["css/styles.css"],"names":[],"mappings":"iBAwBwD,UAAW,YAA1C,gBAAZ,WAAb,YAA0C,aACxC,QAAS,eAGX,SACE,QAAS,EAaX,4BACE,cACE,OAAQ,EACR,MAAO,KACP,WAAY,KACZ,OAAQ,MAOZ,aACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,IAC3B,cAAe,IACP,MAAO,IAGjB,mBACE,0BAA2B,GAC3B,cAAe,IACP,MAAO,IAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,EAC3B,cAAe,GACP,MAAO,GAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,EAC3B,cAAe,EACP,MAAO,EAGjB,iBACE,0BAA2B,GAC3B,cAAe,EACP,MAAO,EAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,0BAA2B,GAC3B,cAAe,GACP,MAAO,GAGjB,kBACE,YAAa,EAGf,kBACE,YAAa,GAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,IAGf,mBACE,YAAa,eAGf,mBACE,YAAa,eAIf,+BADA,eAEE,iBAAkB,MAClB,wBAAyB,WACjB,gBAAiB,WACzB,sBAAuB,QACf,cAAe,QACvB,kBAAmB,QACnB,oBAAqB,QACb,YAAa,QAKvB,8BACA,2BAFA,6BAGA,+BAJA,qBAKE,iBAAkB,MAClB,wBAAyB,WACjB,gBAAiB,WAK3B,+BACA,4BAFA,8BAGA,gCAJA,sBAKE,iBAAkB,OAClB,wBAAyB,OACjB,gBAAiB,OAI3B,4BAEA,yBADA,2BAEA,6BAJA,mBAKE,iBAAkB,IAClB,wBAAyB,SACjB,gBAAiB,SAI3B,qCAEA,kCADA,oCAEA,sCAJA,4BAKE,wBAAyB,aACjB,gBAAiB,aAI3B,sCAEA,mCADA,qCAEA,uCAJA,6BAKE,iBAAkB,QAClB,wBAAyB,cACjB,gBAAiB,cAI3B,8BACA,2BAEA,oCADA,qCAHA,6BAKE,kBAAmB,MACnB,oBAAqB,WACb,YAAa,WACrB,sBAAuB,WACf,cAAe,WAIzB,+BACA,4BAEA,qCADA,sCAHA,8BAKE,kBAAmB,OACnB,oBAAqB,OACb,YAAa,OACrB,sBAAuB,OACf,cAAe,OACvB,UAAW,KAIb,iCACA,8BAEA,uCADA,wCAHA,gCAKE,UAAW,KACX,mBAAoB,WACZ,WAAY,WAItB,4BACA,yBAEA,kCADA,mCAHA,2BAKE,kBAAmB,IACnB,oBAAqB,SACb,YAAa,SACrB,sBAAuB,SACf,cAAe,SAIzB,gCACA,6BAEA,sCADA,uCAHA,+BAKE,kBAAmB,QACnB,oBAAqB,QACb,YAAa,QACrB,sBAAuB,QACf,cAAe,QAGzB,OACE,iBAAkB,EAClB,aAAc,EACN,KAAM,EACd,mBAAoB,WACZ,WAAY,WAGtB,YACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,KACV,KAAM,EAAE,EAAE,KAClB,mBAAoB,WACZ,WAAY,WAGtB,eACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,KACV,KAAM,EAAE,EAAE,KAClB,mBAAoB,WACZ,WAAY,WAGtB,YACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,KACV,KAAM,EAAE,EAAE,KAClB,mBAAoB,WACZ,WAAY,WAGtB,YACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,KACV,KAAM,EAAE,EAAE,KAClB,mBAAoB,WACZ,WAAY,WAGtB,WACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,GACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,wBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,GACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,2BACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,KACX,WAAY,GACZ,mBAAoB,WACZ,WAAY,WAGtB,yBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,OACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAEtB,yBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,OACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,4BACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,KACX,WAAY,OACZ,mBAAoB,WACZ,WAAY,WAEtB,4BACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,KACX,WAAY,OACZ,mBAAoB,WACZ,WAAY,WAGtB,wBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,GACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,2BACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,KACX,WAAY,GACZ,mBAAoB,WACZ,WAAY,WAGtB,WACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,GACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,wBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,GACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,2BACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,GACV,KAAM,EAAE,EAAE,GAClB,UAAW,KACX,WAAY,GACZ,mBAAoB,WACZ,WAAY,WAGtB,yBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,OACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAEtB,yBACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,OACX,WAAY,KACZ,mBAAoB,WACZ,WAAY,WAGtB,4BACE,iBAAkB,EAClB,aAAc,EAAE,EAAE,OACV,KAAM,EAAE,EAAE,OAClB,UAAW,KACX,WAAY,OACZ,mBAAoBLine truncated
@@ -365,6 +365,7 @@
var refresh = function() {
var html = vm.editor.getData();
html = inlineImageDimensions(html);
var dom = document.createElement("DIV");
dom.innerHTML = html;
@@ -429,6 +430,29 @@
}
}
function inlineImageDimensions(html) {
return html.replace(/<img\b[^>]*>/gi, function (tag) {
var style = (tag.match(/style="([^"]*)"/i) || [])[1];
if (!style) return tag;
var r = style.match(/aspect-ratio\s*:\s*(\d+)\s*\/\s*(\d+)/i);
if (!r) return tag;
var w = (style.match(/(?:^|;)\s*width\s*:\s*(\d+)px/i) || [])[1]
|| (tag.match(/\swidth="(\d+)"/i) || [])[1];
if (!w) return tag;
var h = Math.round(w * r[2] / r[1]);
var newStyle = style;
if (!/(?:^|;)\s*width\s*:/i.test(newStyle)) newStyle = newStyle.replace(/;?\s*$/, '') + ';width:' + w + 'px';
if (!/(?:^|;)\s*height\s*:/i.test(newStyle)) newStyle = newStyle.replace(/;?\s*$/, '') + ';height:' + h + 'px';
return tag
.replace(/style="[^"]*"/i, 'style="' + newStyle + '"')
.replace(/\swidth="[^"]*"/gi, '')
.replace(/\sheight="[^"]*"/gi, '')
.replace(/\s*\/?>$/, ' width="' + w + '" height="' + h + '">');
});
}
function cleanDirtyHTMLElements(html, threshold) {
var regex = /(<[^>^/]+>(&nbsp;)*<\/[^>]+>)/gm;
+1 -1
View File
@@ -1,2 +1,2 @@
!function(){"use strict";function c(e){var t=this;"function"!=typeof e.then&&(angular.extend(this,e),_.forEach(this.identities,function(e){var t;e.fullName&&e.email?e.full=e.fullName+" <"+e.email+">":e.email?e.full="<"+e.email+">":e.full="",e.signature&&(t=angular.element("<div>"+e.signature+"</div>"),e.textSignature=_.map(t.contents(),"textContent").join(" ").trim())}),this.$mailboxes)&&c.$Mailbox.$unwrapCollection(this,c.$q.when({mailboxes:this.$mailboxes})).then(function(e){t.$mailboxes=e})}c.$factory=["$q","$timeout","$log","sgSettings","Resource","Preferences","Mailbox","Message",function(e,t,s,n,i,a,o,r){return angular.extend(c,{$q:e,$timeout:t,$log:s,$$resource:new i(n.activeUser("folderURL")+"Mail",n.activeUser()),$Preferences:a,$Mailbox:o,$Message:r}),c}];try{angular.module("SOGo.MailerUI")}catch(e){angular.module("SOGo.MailerUI",["SOGo.Common"])}angular.module("SOGo.MailerUI").factory("Account",c.$factory),c.$findAll=function(e){return e?c.$unwrapCollection(e):c.$accounts?c.$q.when(c.$accounts):c.$$resource.fetch("","mailAccounts").then(function(e){return c.$unwrapCollection(e)})},c.$unwrapCollection=function(e){var s=[];return angular.forEach(e,function(e,t){e.id=t,s[t]=new c(e)}),c.$accounts=s},c.refreshUnseenCount=function(e){var t,s=1===c.$Preferences.defaults.SOGoMailFetchAllUnseenCountFolders,n=c.$Preferences.defaults.SOGoRefreshViewCheck;if(s)t=[];else{if(!e)throw Error("SOGoMailFetchAllUnseenCountFolders is disabled and no folders list provided");t=e}_.forEach(c.$accounts,function(e){s?_.forEach(e.$$flattenMailboxes,function(e){t.push(e.id)}):(_.includes(t,e.id+"/folderINBOX")||t.push(e.id+"/folderINBOX"),_.forEach(e.$$flattenMailboxes,function(e){angular.isDefined(e.unseenCount)&&!_.includes(t,e.id)&&t.push(e.id)}))}),c.$$resource.post("","unseenCount",{mailboxes:t}).then(function(t){_.forEach(c.$accounts,function(e){_.forEach(e.$$flattenMailboxes,function(e){angular.isDefined(t[e.id])&&(e.unseenCount=t[e.id])})})}),n&&"manually"!=n&&(c.$refreshUnseenCount&&c.$timeout.cancel(c.$refreshUnseenCount),c.$refreshUnseenCount=c.$timeout(angular.bind(this,c.refreshUnseenCount,e),1e3*n.timeInterval()))},c.prototype.getLength=function(){return this.$expanded?this.$flattenMailboxes().length:0},c.prototype.getItemAtIndex=function(e){var t=this.$flattenMailboxes();return 0<=e&&e<t.length?t[e]:null},c.prototype.$getMailboxes=function(e){var a=this,t=e&&e.reload;return this.$mailboxes&&!t?c.$q.when(this.$mailboxes):(!t&&this.$futureMailboxesData||(this.$futureMailboxesData=c.$Mailbox.$find(this,e).then(function(e){function s(e){_.forEach(e,function(e){var t=_.find(n,["id",e.id]);t&&(e.unseenCount=t.unseenCount),e.children&&0<e.children.length&&s(e.children)})}var n=a.$flattenMailboxes({all:!0});a.$mailboxes=e,a.$expanded=!1;s(a.$mailboxes);function t(e){_.forEach(e,function(e){e.$expanded=0<=i.indexOf("/"+e.id),e.children&&0<e.children.length&&t(e.children)})}var i;if(c.$Preferences.settings.Mail.ExpandedFolders){if(angular.isString(c.$Preferences.settings.Mail.ExpandedFolders))try{i=angular.fromJson(c.$Preferences.settings.Mail.ExpandedFolders)}catch(e){c.$log.warn("Can't parse list of expanded folders. String was: "+c.$Preferences.settings.Mail.ExpandedFolders),i=[]}else i=c.$Preferences.settings.Mail.ExpandedFolders;a.$expanded=0<=i.indexOf("/"+a.id),0<i.length&&t(a.$mailboxes)}return c.$accounts&&(a.$expanded|=1==c.$accounts.length),a.$flattenMailboxes({reload:!0}),a.$mailboxes})),this.$futureMailboxesData)},c.prototype.$flattenMailboxes=function(t){function s(e){_.forEach(e,function(e){n.push(e),(t&&t.all||e.$expanded)&&e.children&&0<e.children.length&&s(e.children)})}var n=[],i=[];return!this.$$flattenMailboxes||t&&(t.reload||t.all)?(s(this.$mailboxes),t&&t.all||(this.$$flattenMailboxes=n,t&&t.saveState&&(_.forEach(c.$accounts,function(e){e.$expanded&&i.push("/"+e.id),_.reduce(e.$$flattenMailboxes,function(e,t){return t.$expanded&&e.push("/"+t.id),e},i)}),c.$$resource.post(null,"saveFoldersState",i)))):n=this.$$flattenMailboxes,n},c.prototype.$getMailboxByType=function(s){function n(e){var t=_.find(e,function(e){return e.type==s});return t||angular.forEach(e,function(e){!t&&e.children&&0<e.children.length&&(t=n(e.children))}),t}return n(this.$mailboxes)},c.prototype.$getMailboxByPath=function(s){function n(e){var t=_.find(e,function(e){return e.path==s});return t||angular.forEach(e,function(e){!t&&e.children&&0<e.children.length&&(t=n(e.children))}),t}var e;if(null==(e=n(this.$mailboxes)))throw Error("No mailbox found matching path "+s);return e},c.prototype.$newMailbox=function(e,t){var s=this;return c.$$resource.post(e.toString(),"createFolder",{name:t}).then(function(){s.$getMailboxes({reload:!0})})},c.prototype.getTextSignature=function(e){var t;return e.signature?(t=angular.element("<div>"+e.signature+"</div>"),e.textSignature=_.map(t.contents(),"textContent").join(" ").trim()):e.textSignature="",e.textSignature},c.prototype.$hasCertificate=function(){return this.security&&this.security.hasCLine truncated
!function(){"use strict";function c(e){var t=this;"function"!=typeof e.then&&(angular.extend(this,e),_.forEach(this.identities,function(e){var t;e.fullName&&e.email?e.full=e.fullName+" <"+e.email+">":e.email?e.full="<"+e.email+">":e.full="",e.signature&&(t=angular.element("<div>"+e.signature+"</div>"),e.textSignature=_.map(t.contents(),"textContent").join(" ").trim())}),this.$mailboxes)&&c.$Mailbox.$unwrapCollection(this,c.$q.when({mailboxes:this.$mailboxes})).then(function(e){t.$mailboxes=e})}c.$factory=["$q","$timeout","$log","sgSettings","Resource","Preferences","Mailbox","Message",function(e,t,s,n,i,a,o,r){return angular.extend(c,{$q:e,$timeout:t,$log:s,$$resource:new i(n.activeUser("folderURL")+"Mail",n.activeUser()),$Preferences:a,$Mailbox:o,$Message:r}),c}];try{angular.module("SOGo.MailerUI")}catch(e){angular.module("SOGo.MailerUI",["SOGo.Common"])}angular.module("SOGo.MailerUI").factory("Account",c.$factory),c.$findAll=function(e){return e?c.$unwrapCollection(e):c.$accounts?c.$q.when(c.$accounts):c.$$resource.fetch("","mailAccounts").then(function(e){return c.$unwrapCollection(e)})},c.$unwrapCollection=function(e){var s=[];return angular.forEach(e,function(e,t){e.id=t,s[t]=new c(e)}),c.$accounts=s},c.refreshUnseenCount=function(e){var t,s=1===c.$Preferences.defaults.SOGoMailFetchAllUnseenCountFolders,n=c.$Preferences.defaults.SOGoRefreshViewCheck;if(s)t=[];else{if(!e)throw Error("SOGoMailFetchAllUnseenCountFolders is disabled and no folders list provided");t=e}_.forEach(c.$accounts,function(e){s?_.forEach(e.$$flattenMailboxes,function(e){t.push(e.id)}):(_.includes(t,e.id+"/folderINBOX")||t.push(e.id+"/folderINBOX"),_.forEach(e.$$flattenMailboxes,function(e){angular.isDefined(e.unseenCount)&&!_.includes(t,e.id)&&t.push(e.id)}))}),c.$$resource.post("","unseenCount",{mailboxes:t}).then(function(t){_.forEach(c.$accounts,function(e){_.forEach(e.$$flattenMailboxes,function(e){angular.isDefined(t[e.id])&&(e.unseenCount=t[e.id])})})}),n&&"manually"!=n&&(c.$refreshUnseenCount&&c.$timeout.cancel(c.$refreshUnseenCount),c.$refreshUnseenCount=c.$timeout(angular.bind(this,c.refreshUnseenCount,e),1e3*n.timeInterval()))},c.prototype.getLength=function(){return this.$expanded?this.$flattenMailboxes().length:0},c.prototype.getItemAtIndex=function(e){var t=this.$flattenMailboxes();return 0<=e&&e<t.length?t[e]:null},c.prototype.$getMailboxes=function(e){var a=this,t=e&&e.reload;return this.$mailboxes&&!t?c.$q.when(this.$mailboxes):(!t&&this.$futureMailboxesData||(this.$futureMailboxesData=c.$Mailbox.$find(this,e).then(function(e){function s(e){_.forEach(e,function(e){var t=_.find(n,["id",e.id]);t&&(e.unseenCount=t.unseenCount),e.children&&0<e.children.length&&s(e.children)})}var n=a.$flattenMailboxes({all:!0});a.$mailboxes=e,a.$expanded=!1;s(a.$mailboxes);function t(e){_.forEach(e,function(e){e.$expanded=0<=i.indexOf("/"+e.id),e.children&&0<e.children.length&&t(e.children)})}var i;if(c.$Preferences.settings.Mail.ExpandedFolders){if(angular.isString(c.$Preferences.settings.Mail.ExpandedFolders))try{i=angular.fromJson(c.$Preferences.settings.Mail.ExpandedFolders)}catch(e){c.$log.warn("Can't parse list of expanded folders. String was: "+c.$Preferences.settings.Mail.ExpandedFolders),i=[]}else i=c.$Preferences.settings.Mail.ExpandedFolders;a.$expanded=0<=i.indexOf("/"+a.id),0<i.length&&t(a.$mailboxes)}return c.$accounts&&(a.$expanded|=1==c.$accounts.length),a.$flattenMailboxes({reload:!0}),a.$mailboxes})),this.$futureMailboxesData)},c.prototype.$flattenMailboxes=function(t){function s(e){_.forEach(e,function(e){n.push(e),(t&&t.all||e.$expanded)&&e.children&&0<e.children.length&&s(e.children)})}var n=[],i=[];return!this.$$flattenMailboxes||t&&(t.reload||t.all)?(s(this.$mailboxes),t&&t.all||(this.$$flattenMailboxes=n,t&&t.saveState&&(_.forEach(c.$accounts,function(e){e.$expanded&&i.push("/"+e.id),_.reduce(e.$$flattenMailboxes,function(e,t){return t.$expanded&&e.push("/"+t.id),e},i)}),c.$$resource.post(null,"saveFoldersState",i)))):n=this.$$flattenMailboxes,n},c.prototype.$getMailboxByType=function(s){function n(e){var t=_.find(e,function(e){return e.type==s});return t||angular.forEach(e,function(e){!t&&e.children&&0<e.children.length&&(t=n(e.children))}),t}return n(this.$mailboxes)},c.prototype.$getMailboxByPath=function(s){function n(e){var t=_.find(e,function(e){return e.path==s});return t||angular.forEach(e,function(e){!t&&e.children&&0<e.children.length&&(t=n(e.children))}),t}var e;if(null==(e=n(this.$mailboxes)))throw Error("No mailbox found matching path "+s);return e},c.prototype.$newMailbox=function(e,t){var s=this;return c.$$resource.post(e.toString(),"createFolder",{name:t}).then(function(){s.$getMailboxes({reload:!0})})},c.prototype.getTextSignature=function(e){var t;return e.signature?(t=angular.element("<div>"+e.signature+"</div>"),e.textSignature=_.map(t.contents(),"textContent").join(" ").trim()):e.textSignature="",e.textSignature},c.prototype.$hasCertificate=function(){return this.security&&this.security.hasCLine truncated
//# sourceMappingURL=Mailer.services.js.map
@@ -1 +1 @@
{"version":3,"file":"Mailer.services.js","sources":["Mailer/Account.service.js","Mailer/Mailbox.service.js","Mailer/Message.service.js","Mailer/sgImageGallery.service.js","Mailer/VirtualMailbox.service.js","Mailer/MailboxController.js","Mailer/MailboxesController.js","Mailer/MessageController.js","Mailer/MessageEditorController.js","Mailer/sgAccountSection.directive.js","Mailer/sgIMIP.directive.js","Mailer/sgMailboxListItem.directive.js","Mailer/sgMessageListItem.directive.js","Mailer/sgMessageListItemMain.directive.js","Mailer/sgZoomableImage.directive.js"],"names":["Account","futureAccountData","_this","this","then","angular","extend","_","forEach","identities","identity","element","fullName","email","full","signature","textSignature","map","contents","join","trim","$mailboxes","$Mailbox","$unwrapCollection","$q","when","mailboxes","collection","$factory","$timeout","$log","Settings","Resource","Preferences","Mailbox","Message","$$resource","activeUser","$Preferences","$Message","module","e","factory","$findAll","data","$accounts","fetch","o","i","id","refreshUnseenCount","folders","unseenCountFolders","fetchAllUnseenCountFolders","defaults","SOGoMailFetchAllUnseenCountFolders","refreshViewCheck","SOGoRefreshViewCheck","Error","account","$$flattenMailboxes","mailbox","push","includes","isDefined","unseenCount","post","$refreshUnseenCount","cancel","bind","timeInterval","prototype","getLength","$expanded","$flattenMailboxes","length","getItemAtIndex","index","expandedMailboxes","$getMailboxes","options","reload","$futureMailboxesData","$find","_visitForUnseencount","previousMailbox","find","previousMailboxes","children","all","_visitForExpanded","expandedFolders","indexOf","settings","Mail","ExpandedFolders","isString","fromJson","warn","_visit","allMailboxes","saveState","reduce","$getMailboxByType","type","_find","$getMailboxByPath","path","$newMailbox","name","toString","getTextSignature","$hasCertificate","security","hasCertificate","$certificate","$$certificate","reject","$removeCertificate","updateQuota","percent","description","maxQuota","Math","round","usedSpace","l","formatted","maxMessages","messagesCount","$quota","$newMessage","debug","JSON","stringify","undefined","accountId","mailboxPath","message","$absolutePath","asDraft","accountDefaults","AuxiliaryMailAccounts","alwaysSign","sign","alwaysEncrypt","encrypt","editable","isNew","mailto","isObject","$parseMailto","$addDelegate","user","deferred","defer","param","uid","delegates","resolve","users","status","promise","$removeDelegate","splice","$omit","deep","defaultIdentity","value","key","copy","isReadOnly","pick","isDefault","forceDefaultIdentity","futureMailboxData","$account","init","newMailboxData","create","$unwrap","$rootScope","Acl","PRELOAD","BATCH_DELETE_LIMIT","$$Acl","$query","sort","asc","selectedFolder","$refreshTimeout","$virtualMode","$virtualPath","$searchMode","SortingState","parseInt","constant","LOOKAHEAD","SIZE","createMailboxes","level","isSentFolder","isDraftsFolder","quotas","split","component","asCSSIdentifier","isUndefined","uidsMap","headers","$isLoading","$messages","$visibleMessages","$selectedMessages","$highlightWords","$id","$acl","threaded","$collapsedThreads","threadsCollapsed","$displayName","$isEditable","isEditable","$isSpecial","$icon","$isNoInferiors","isNoInferiors","$shadowData","selectFolder","setSearchMode","searchMode","$lastVisibleIndex","max","$loadMessage","selectedMessages","updateCache","filter","selected","selectedCount","$unselectMessages","isSelectedMessage","messageId","$selectedMessage","selectedMessage","$selectedMessageIndex","hasSelectedMessage","$filter","sortingAttributes","filters","action","labels","searchBy","searchInput","word","cleanedWord","toLowerCase","match","secondFilter","$flaggedOnly","$unseenOnly","$syncToken","syncToken","unseenOnly","flaggedOnly","keys","$filteredLabels","k","f","endIndex","uids","startIndex","loaded","subject","min","loading","futureHeadersData","$unwrapHeaders","flags","isNoSelect","isWritable","getClassName","base","$rename","findParent","parent","parentMailbox","$save","_updateChildren","child","replace","pathRE","oldPath","sibling","localeCompare","RegExp","$compact","$canFolderAs","$setFolderAs","$empty","capitalize","substring","$markAsRead","isread","getLabels","$labels","filteredByLabel","$flagMessages","messages","operation","msgUIDs","forwardMessages","saveSelectedMessages","selectedUIDs","download","exportFolder","filename","open","$delete","$_deleteMessages","firstIndex","forEachRight","selectedIndex","findIndex","updateVisibleMessages","$deleteMessages","batchSize","_deleteMessages","start","end","currentUids","slice","$markOrUnMarkMessagesAsJunk","method","$copyMessages","folder","$moveMessages","$move","parentPath","finally","save","response","error","$reset","Object","assign","childrenArray","collapsedThread","msg","first","collapsed","$broadcast","$futureMailboxData","headersFields","msgObject","hasNewMessages","$topIndex","deleted","jLine truncated
{"version":3,"file":"Mailer.services.js","sources":["Mailer/Account.service.js","Mailer/Mailbox.service.js","Mailer/Message.service.js","Mailer/sgImageGallery.service.js","Mailer/VirtualMailbox.service.js","Mailer/MailboxController.js","Mailer/MailboxesController.js","Mailer/MessageController.js","Mailer/MessageEditorController.js","Mailer/sgAccountSection.directive.js","Mailer/sgIMIP.directive.js","Mailer/sgMailboxListItem.directive.js","Mailer/sgMessageListItem.directive.js","Mailer/sgMessageListItemMain.directive.js","Mailer/sgZoomableImage.directive.js"],"names":["Account","futureAccountData","_this","this","then","angular","extend","_","forEach","identities","identity","element","fullName","email","full","signature","textSignature","map","contents","join","trim","$mailboxes","$Mailbox","$unwrapCollection","$q","when","mailboxes","collection","$factory","$timeout","$log","Settings","Resource","Preferences","Mailbox","Message","$$resource","activeUser","$Preferences","$Message","module","e","factory","$findAll","data","$accounts","fetch","o","i","id","refreshUnseenCount","folders","unseenCountFolders","fetchAllUnseenCountFolders","defaults","SOGoMailFetchAllUnseenCountFolders","refreshViewCheck","SOGoRefreshViewCheck","Error","account","$$flattenMailboxes","mailbox","push","includes","isDefined","unseenCount","post","$refreshUnseenCount","cancel","bind","timeInterval","prototype","getLength","$expanded","$flattenMailboxes","length","getItemAtIndex","index","expandedMailboxes","$getMailboxes","options","reload","$futureMailboxesData","$find","_visitForUnseencount","previousMailbox","find","previousMailboxes","children","all","_visitForExpanded","expandedFolders","indexOf","settings","Mail","ExpandedFolders","isString","fromJson","warn","_visit","allMailboxes","saveState","reduce","$getMailboxByType","type","_find","$getMailboxByPath","path","$newMailbox","name","toString","getTextSignature","$hasCertificate","security","hasCertificate","$certificate","$$certificate","reject","$removeCertificate","updateQuota","percent","description","maxQuota","Math","round","usedSpace","l","formatted","maxMessages","messagesCount","$quota","$newMessage","debug","JSON","stringify","undefined","accountId","mailboxPath","message","$absolutePath","asDraft","accountDefaults","AuxiliaryMailAccounts","alwaysSign","sign","alwaysEncrypt","encrypt","editable","isNew","mailto","isObject","$parseMailto","$addDelegate","user","deferred","defer","param","uid","delegates","resolve","users","status","promise","$removeDelegate","splice","$omit","deep","defaultIdentity","value","key","copy","isReadOnly","pick","isDefault","forceDefaultIdentity","futureMailboxData","$account","init","newMailboxData","create","$unwrap","$rootScope","Acl","PRELOAD","BATCH_DELETE_LIMIT","$$Acl","$query","sort","asc","selectedFolder","$refreshTimeout","$virtualMode","$virtualPath","$searchMode","SortingState","parseInt","constant","LOOKAHEAD","SIZE","createMailboxes","level","isSentFolder","isDraftsFolder","quotas","split","component","asCSSIdentifier","isUndefined","uidsMap","headers","$isLoading","$messages","$visibleMessages","$selectedMessages","$highlightWords","$id","$acl","threaded","$collapsedThreads","threadsCollapsed","$displayName","$isEditable","isEditable","$isSpecial","$icon","mailJunkIcon","$isNoInferiors","isNoInferiors","$shadowData","selectFolder","setSearchMode","searchMode","$lastVisibleIndex","max","$loadMessage","selectedMessages","updateCache","filter","selected","selectedCount","$unselectMessages","isSelectedMessage","messageId","$selectedMessage","selectedMessage","$selectedMessageIndex","hasSelectedMessage","$filter","sortingAttributes","filters","action","labels","searchBy","searchInput","word","cleanedWord","toLowerCase","match","secondFilter","$flaggedOnly","$unseenOnly","$syncToken","syncToken","unseenOnly","flaggedOnly","keys","$filteredLabels","k","f","endIndex","uids","startIndex","loaded","subject","min","loading","futureHeadersData","$unwrapHeaders","flags","isNoSelect","isWritable","getClassName","base","$rename","findParent","parent","parentMailbox","$save","_updateChildren","child","replace","pathRE","oldPath","sibling","localeCompare","RegExp","$compact","$canFolderAs","$setFolderAs","$empty","capitalize","substring","$markAsRead","isread","getLabels","$labels","filteredByLabel","$flagMessages","messages","operation","msgUIDs","forwardMessages","saveSelectedMessages","selectedUIDs","download","exportFolder","filename","open","$delete","$_deleteMessages","firstIndex","forEachRight","selectedIndex","findIndex","updateVisibleMessages","$deleteMessages","batchSize","_deleteMessages","start","end","currentUids","slice","$markOrUnMarkMessagesAsJunk","method","$copyMessages","folder","$moveMessages","$move","parentPath","finally","save","response","error","$reset","Object","assign","childrenArray","collapsedThread","msg","first","collapsed","$broadcast","$futureMailboxData","headersFields","msgObject","hasNewMessages","$topIndeLine truncated
@@ -207,7 +207,7 @@
}
else if (this.type == 'junk') {
this.$displayName = l('JunkFolderName');
this.$icon = 'thumb_down';
this.$icon = Mailbox.$Preferences.defaults.mailJunkIcon;
}
else if (this.type == 'templates') {
this.$displayName = l('TemplatesFolderName');
@@ -36,6 +36,7 @@
this.allSelected = false;
this.isLoadingMessage = false;
this.nextAction = null;
this.junkIcon = Preferences.defaults.mailJunkIcon;
if (!Mailbox.$virtualMode)
this.selectedFolder.getLabels(); // fetch labels from server
@@ -31,6 +31,7 @@
this.$showDetailedRecipients = this.$alwaysShowDetailedRecipients;
this.showRawSource = false;
this.mailInDeletion = -1;
this.junkIcon = Preferences.defaults.mailJunkIcon;
_registerHotkeys(hotkeys);
@@ -319,10 +320,60 @@
}
};
// Select either the next or previous message once the current message
// has been removed from the mailbox (deleted or moved), then close the
// popup if any.
function _selectNextMessage(mailbox, state, index) {
var nextMessage, previousMessage,
nextIndex = index,
$timeout = vm.service.$timeout;
if (angular.isDefined(state)) {
if (index > 0) {
nextIndex -= 1;
nextMessage = mailbox.getItemAtIndex(nextIndex);
}
if (index < mailbox.getLength())
previousMessage = mailbox.getItemAtIndex(index);
if (nextMessage) {
if (nextMessage.isread && previousMessage && !previousMessage.isread) {
nextIndex = index;
nextMessage = previousMessage;
}
}
else if (previousMessage) {
nextIndex = index;
nextMessage = previousMessage;
}
try {
if (nextMessage && $mdMedia(sgConstant['gt-md'])) {
if (Mailbox.$virtualMode)
state.go('mail.account.virtualMailbox.message', {mailboxId: encodeUriFilter(nextMessage.$mailbox.path), messageId: nextMessage.uid});
else
state.go('mail.account.mailbox.message', {messageId: nextMessage.uid});
$timeout(function() {
if (nextIndex < mailbox.$topIndex)
mailbox.$topIndex = nextIndex;
else if (nextIndex > mailbox.$lastVisibleIndex)
mailbox.$topIndex = nextIndex - (mailbox.$lastVisibleIndex - mailbox.$topIndex);
});
}
else {
state.go('mail.account.mailbox').then(function() {
delete mailbox.$selectedMessage;
});
}
}
catch (error) {}
}
vm.closePopup();
}
this.deleteMessage = function() {
var mailbox, message, state, nextMessage, previousMessage,
parentCtrls = $parentControllers(),
$timeout = this.service.$timeout;
var mailbox, message, state,
parentCtrls = $parentControllers();
if (parentCtrls.messageCtrl) {
mailbox = parentCtrls.mailboxCtrl.selectedFolder;
@@ -340,52 +391,9 @@
vm.mailInDeletion = message.uid;
function _success(index) {
var nextIndex = index;
// Remove message object from scope
message = null;
if (angular.isDefined(state)) {
// Select either the next or previous message
if (index > 0) {
nextIndex -= 1;
nextMessage = mailbox.getItemAtIndex(nextIndex);
}
if (index < mailbox.getLength())
previousMessage = mailbox.getItemAtIndex(index);
if (nextMessage) {
if (nextMessage.isread && previousMessage && !previousMessage.isread) {
nextIndex = index;
nextMessage = previousMessage;
}
}
else if (previousMessage) {
nextIndex = index;
nextMessage = previousMessage;
}
try {
if (nextMessage && $mdMedia(sgConstant['gt-md'])) {
if (Mailbox.$virtualMode)
state.go('mail.account.virtualMailbox.message', {mailboxId: encodeUriFilter(nextMessage.$mailbox.path), messageId: nextMessage.uid});
else
state.go('mail.account.mailbox.message', {messageId: nextMessage.uid});
$timeout(function() {
if (nextIndex < mailbox.$topIndex)
mailbox.$topIndex = nextIndex;
else if (nextIndex > mailbox.$lastVisibleIndex)
mailbox.$topIndex = nextIndex - (mailbox.$lastVisibleIndex - mailbox.$topIndex);
});
}
else {
state.go('mail.account.mailbox').then(function() {
message = null;
delete mailbox.$selectedMessage;
});
}
}
catch (error) {}
}
vm.closePopup();
_selectNextMessage(mailbox, state, index);
}
mailbox.$deleteMessages([message]).then(_success, function(response) {
@@ -407,6 +415,39 @@
});
};
this.markOrUnMarkAsJunk = function() {
var mailbox, message, state, account, dstFolder,
parentCtrls = $parentControllers();
if (parentCtrls.messageCtrl) {
mailbox = parentCtrls.mailboxCtrl.selectedFolder;
message = parentCtrls.messageCtrl.message;
state = parentCtrls.messageCtrl.$state;
account = parentCtrls.messageCtrl.account;
}
else {
mailbox = stateMailbox;
message = stateMessage;
state = $state;
account = stateAccount;
}
if (Mailbox.$virtualMode) {
mailbox = Mailbox.selectedFolder; // the VirtualMailbox instance
}
if (mailbox.type == 'junk')
dstFolder = '/' + account.id + '/folderINBOX';
else
dstFolder = '/' + account.$getMailboxByType('junk').id;
mailbox.$markOrUnMarkMessagesAsJunk([message]).then(function() {
mailbox.$moveMessages([message], dstFolder).then(function(index) {
message = null;
_selectNextMessage(mailbox, state, index);
});
});
};
function _showMailEditor($event, message) {
if (_messageDialog() === null) {
var onCompleteDeferred = $q.defer();
@@ -11,10 +11,9 @@ $icon-badge-size: 2 * $baseline-grid;
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url('../fonts/MaterialIcons-Regular.eot'); /* For IE6-8 */
src: local('Material Icons'),
local('MaterialIcons-Regular'),
url('../fonts/MaterialIcons-Regular.woff2') format('woff2'),
/* No local() sources: always use the bundled font so an outdated
Material Icons font installed on the OS can't shadow it. */
src: url('../fonts/MaterialIcons-Regular.woff2') format('woff2'),
url('../fonts/MaterialIcons-Regular.woff') format('woff'),
url('../fonts/MaterialIcons-Regular.ttf') format('truetype');
}
+1 -1
View File
@@ -1,3 +1,3 @@
MAJOR_VERSION=5
MINOR_VERSION=12
SUBMINOR_VERSION=9
SUBMINOR_VERSION=11