Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117756341
mattermost.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
mattermost.js
View Options
/**
* Mattermost driver
* Websocket code based on https://github.com/mattermost/mattermost-redux/master/client/websocket_client.js
*
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
* Copyright (C) 2015-2018, Kolab Systems AG <contact@kolabsys.com>
* Copyright (C) 2015-2018, Mattermost, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @licend The above is the entire license notice
* for the JavaScript code in this file.
*/
var
MAX_WEBSOCKET_FAILS
=
7
;
var
MIN_WEBSOCKET_RETRY_TIME
=
3000
;
// 3 sec
var
MAX_WEBSOCKET_RETRY_TIME
=
300000
;
// 5 mins
function
MMWebSocketClient
()
{
var
Socket
;
this
.
conn
=
null
;
this
.
connectionUrl
=
null
;
this
.
token
=
null
;
this
.
sequence
=
1
;
this
.
connectFailCount
=
0
;
this
.
eventCallback
=
null
;
this
.
firstConnectCallback
=
null
;
this
.
reconnectCallback
=
null
;
this
.
errorCallback
=
null
;
this
.
closeCallback
=
null
;
this
.
connectingCallback
=
null
;
this
.
dispatch
=
null
;
this
.
getState
=
null
;
this
.
stop
=
false
;
this
.
platform
=
''
;
this
.
initialize
=
function
(
token
,
dispatch
,
getState
,
opts
)
{
var
forceConnection
=
opts
.
forceConnection
||
true
,
webSocketConnector
=
opts
.
webSocketConnector
||
WebSocket
,
connectionUrl
=
opts
.
connectionUrl
,
platform
=
opts
.
platform
,
self
=
this
;
if
(
platform
)
{
this
.
platform
=
platform
;
}
if
(
forceConnection
)
{
this
.
stop
=
false
;
}
return
new
Promise
(
function
(
resolve
,
reject
)
{
if
(
self
.
conn
)
{
resolve
();
return
;
}
if
(
connectionUrl
==
null
)
{
console
.
log
(
'websocket must have connection url'
);
reject
(
'websocket must have connection url'
);
return
;
}
if
(
!
dispatch
)
{
console
.
log
(
'websocket must have a dispatch'
);
reject
(
'websocket must have a dispatch'
);
return
;
}
if
(
self
.
connectFailCount
===
0
)
{
console
.
log
(
'websocket connecting to '
+
connectionUrl
);
}
Socket
=
webSocketConnector
;
if
(
self
.
connectingCallback
)
{
self
.
connectingCallback
(
dispatch
,
getState
);
}
var
regex
=
/^(?:https?|wss?):(?:\/\/)?[^/]*/
;
var
captured
=
(
regex
).
exec
(
connectionUrl
);
var
origin
;
if
(
captured
)
{
origin
=
captured
[
0
];
if
(
platform
===
'android'
)
{
// this is done cause for android having the port 80 or 443 will fail the connection
// the websocket will append them
var
split
=
origin
.
split
(
':'
);
var
port
=
split
[
2
];
if
(
port
===
'80'
||
port
===
'443'
)
{
origin
=
split
[
0
]
+
':'
+
split
[
1
];
}
}
}
else
{
// If we're unable to set the origin header, the websocket won't connect, but the URL is likely malformed anyway
console
.
warn
(
'websocket failed to parse origin from '
+
connectionUrl
);
}
self
.
conn
=
new
Socket
(
connectionUrl
,
[],
{
headers
:
{
origin
}});
self
.
connectionUrl
=
connectionUrl
;
self
.
token
=
token
;
self
.
dispatch
=
dispatch
;
self
.
getState
=
getState
;
self
.
conn
.
onopen
=
function
()
{
if
(
token
&&
platform
!==
'android'
)
{
// we check for the platform as a workaround until we fix on the server that further authentications
// are ignored
self
.
sendMessage
(
'authentication_challenge'
,
{
token
});
}
if
(
self
.
connectFailCount
>
0
)
{
console
.
log
(
'websocket re-established connection'
);
if
(
self
.
reconnectCallback
)
{
self
.
reconnectCallback
(
self
.
dispatch
,
self
.
getState
);
}
}
else
if
(
self
.
firstConnectCallback
)
{
self
.
firstConnectCallback
(
self
.
dispatch
,
self
.
getState
);
}
self
.
connectFailCount
=
0
;
resolve
();
};
self
.
conn
.
onclose
=
function
()
{
self
.
conn
=
null
;
self
.
sequence
=
1
;
if
(
self
.
connectFailCount
===
0
)
{
console
.
log
(
'websocket closed'
);
}
self
.
connectFailCount
++
;
if
(
self
.
closeCallback
)
{
self
.
closeCallback
(
self
.
connectFailCount
,
self
.
dispatch
,
self
.
getState
);
}
var
retryTime
=
MIN_WEBSOCKET_RETRY_TIME
;
// If we've failed a bunch of connections then start backing off
if
(
self
.
connectFailCount
>
MAX_WEBSOCKET_FAILS
)
{
retryTime
=
MIN_WEBSOCKET_RETRY_TIME
*
self
.
connectFailCount
;
if
(
retryTime
>
MAX_WEBSOCKET_RETRY_TIME
)
{
retryTime
=
MAX_WEBSOCKET_RETRY_TIME
;
}
}
setTimeout
(
function
()
{
if
(
self
.
stop
)
{
return
;
}
self
.
initialize
(
token
,
dispatch
,
getState
,
Object
.
assign
({},
opts
,
{
forceConnection
:
true
}));
},
retryTime
);
};
self
.
conn
.
onerror
=
function
(
evt
)
{
if
(
self
.
connectFailCount
<=
1
)
{
console
.
log
(
'websocket error'
);
console
.
log
(
evt
);
}
if
(
self
.
errorCallback
)
{
self
.
errorCallback
(
evt
,
self
.
dispatch
,
self
.
getState
);
}
};
self
.
conn
.
onmessage
=
function
(
evt
)
{
var
msg
=
JSON
.
parse
(
evt
.
data
);
if
(
msg
.
seq_reply
)
{
if
(
msg
.
error
)
{
console
.
warn
(
msg
);
}
}
else
if
(
self
.
eventCallback
)
{
self
.
eventCallback
(
msg
,
self
.
dispatch
,
self
.
getState
);
}
};
});
}
this
.
setConnectingCallback
=
function
(
callback
)
{
this
.
connectingCallback
=
callback
;
}
this
.
setEventCallback
=
function
(
callback
)
{
this
.
eventCallback
=
callback
;
}
this
.
setFirstConnectCallback
=
function
(
callback
)
{
this
.
firstConnectCallback
=
callback
;
}
this
.
setReconnectCallback
=
function
(
callback
)
{
this
.
reconnectCallback
=
callback
;
}
this
.
setErrorCallback
=
function
(
callback
)
{
this
.
errorCallback
=
callback
;
}
this
.
setCloseCallback
=
function
(
callback
)
{
this
.
closeCallback
=
callback
;
}
this
.
close
=
function
(
stop
)
{
this
.
stop
=
stop
;
this
.
connectFailCount
=
0
;
this
.
sequence
=
1
;
if
(
this
.
conn
&&
this
.
conn
.
readyState
===
Socket
.
OPEN
)
{
this
.
conn
.
onclose
=
function
(){};
this
.
conn
.
close
();
this
.
conn
=
null
;
console
.
log
(
'websocket closed'
);
}
}
this
.
sendMessage
=
function
(
action
,
data
)
{
var
msg
=
{
action
,
seq
:
this
.
sequence
++
,
data
};
if
(
this
.
conn
&&
this
.
conn
.
readyState
===
Socket
.
OPEN
)
{
this
.
conn
.
send
(
JSON
.
stringify
(
msg
));
}
else
if
(
!
this
.
conn
||
this
.
conn
.
readyState
===
Socket
.
CLOSED
)
{
this
.
conn
=
null
;
this
.
initialize
(
this
.
token
,
this
.
dispatch
,
this
.
getState
,
{
forceConnection
:
true
,
platform
:
this
.
platform
});
}
}
}
/**
* Initializes and starts websocket connection with Mattermost
*/
function
mattermost_websocket_init
(
url
,
token
)
{
var
api
=
new
MMWebSocketClient
();
api
.
setEventCallback
(
function
(
e
)
{
mattermost_event_handler
(
e
);
});
api
.
initialize
(
token
,
{},
{},
{
connectionUrl
:
url
});
}
/**
* Handles websocket events
*/
function
mattermost_event_handler
(
event
)
{
var
msg
,
type
;
// Direct message notification
if
(
event
.
event
==
'posted'
&&
event
.
data
.
channel_type
==
'D'
)
{
msg
=
rcmail
.
gettext
(
'kolab_chat.directmessage'
);
type
=
'user'
;
}
// Mention notification
else
if
(
event
.
event
==
'posted'
&&
String
(
event
.
data
.
mentions
).
indexOf
(
rcmail
.
env
.
mattermost_user
)
>
0
)
{
msg
=
rcmail
.
gettext
(
'kolab_chat.mentionmessage'
);
type
=
'channel'
;
}
if
(
msg
)
{
var
link
=
$
(
'<a>'
).
text
(
rcmail
.
gettext
(
'kolab_chat.openchat'
)),
user
=
event
.
data
.
sender_name
,
channel
=
event
.
data
.
channel_display_name
,
channel_id
=
event
.
broadcast
.
channel_id
,
msg_id
=
'chat-'
+
type
+
'-'
+
(
type
==
'channel'
?
channel_id
:
user
),
href
=
'?_task=kolab-chat&_channel='
+
urlencode
(
channel_id
);
msg
=
msg
.
replace
(
'$u'
,
user
).
replace
(
'$c'
,
channel
);
if
(
rcmail
.
env
.
kolab_chat_extwin
)
{
link
.
attr
(
'target'
,
'_blank'
);
href
+=
'&redirect=1'
;
}
if
(
event
.
data
.
team_id
)
{
href
+=
'&_team='
+
urlencode
(
event
.
data
.
team_id
);
}
link
.
attr
(
'href'
,
href
);
msg
=
$
(
'<p>'
).
text
(
msg
+
' '
).
append
(
link
).
html
();
// FIXME: Should we display it indefinitely?
rcmail
.
display_message
(
msg
,
'notice chat'
,
10
*
60
*
1000
,
msg_id
);
}
}
window
.
WebSocket
&&
window
.
rcmail
&&
rcmail
.
addEventListener
(
'init'
,
function
()
{
// Use ajax to get the token for websocket connection
$
.
ajax
({
type
:
'GET'
,
url
:
'?_task=kolab-chat&_action=action&_get=token'
,
success
:
function
(
data
)
{
data
=
JSON
.
parse
(
data
);
if
(
data
&&
data
.
token
)
{
rcmail
.
set_env
({
mattermost_url
:
data
.
url
,
mattermost_token
:
data
.
token
,
mattermost_user
:
data
.
user_id
});
mattermost_websocket_init
(
data
.
url
,
data
.
token
);
}
}
});
});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 4, 8:37 AM (2 w, 4 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
42/1a/fe60cd42e8122ce7b973fa961970
Default Alt Text
mattermost.js (10 KB)
Attached To
Mode
rRPK roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline