Browse Source

Merge branch 'release-0.24'

pull/222/merge v0.24.0
Simon Eisenmann 10 years ago
parent
commit
00bf1efa6a
  1. 5
      .travis.yml
  2. 2
      Makefile.am
  3. 2
      README.md
  4. 21
      build/build.js
  5. 7
      configure.ac
  6. 41
      debian/changelog
  7. 75
      doc/CHANNELING-API.txt
  8. 1
      html/logo.html
  9. 53
      html/main.html
  10. 2
      package.json
  11. 5
      server.conf.in
  12. 2
      src/app/spreed-webrtc-server/api.go
  13. 2
      src/app/spreed-webrtc-server/buffercache.go
  14. 13
      src/app/spreed-webrtc-server/channelling.go
  15. 308
      src/app/spreed-webrtc-server/channelling_api.go
  16. 97
      src/app/spreed-webrtc-server/channelling_api_test.go
  17. 33
      src/app/spreed-webrtc-server/client.go
  18. 2
      src/app/spreed-webrtc-server/common_test.go
  19. 8
      src/app/spreed-webrtc-server/config.go
  20. 2
      src/app/spreed-webrtc-server/connection.go
  21. 2
      src/app/spreed-webrtc-server/contact.go
  22. 2
      src/app/spreed-webrtc-server/context.go
  23. 2
      src/app/spreed-webrtc-server/hub.go
  24. 2
      src/app/spreed-webrtc-server/images.go
  25. 2
      src/app/spreed-webrtc-server/incoming_codec.go
  26. 18
      src/app/spreed-webrtc-server/main.go
  27. 2
      src/app/spreed-webrtc-server/random.go
  28. 49
      src/app/spreed-webrtc-server/room_manager.go
  29. 20
      src/app/spreed-webrtc-server/room_manager_test.go
  30. 2
      src/app/spreed-webrtc-server/rooms.go
  31. 26
      src/app/spreed-webrtc-server/roomworker.go
  32. 8
      src/app/spreed-webrtc-server/roomworker_test.go
  33. 23
      src/app/spreed-webrtc-server/session.go
  34. 2
      src/app/spreed-webrtc-server/session_manager.go
  35. 2
      src/app/spreed-webrtc-server/sessions.go
  36. 234
      src/app/spreed-webrtc-server/sleepy/core.go
  37. 2
      src/app/spreed-webrtc-server/stats.go
  38. 2
      src/app/spreed-webrtc-server/stats_manager.go
  39. 2
      src/app/spreed-webrtc-server/tickets.go
  40. 2
      src/app/spreed-webrtc-server/tls.go
  41. 2
      src/app/spreed-webrtc-server/tokenprovider.go
  42. 5
      src/app/spreed-webrtc-server/tokens.go
  43. 8
      src/app/spreed-webrtc-server/user.go
  44. 13
      src/app/spreed-webrtc-server/users.go
  45. 2
      src/app/spreed-webrtc-server/ws.go
  46. 2
      src/i18n/helpers/po2json
  47. 91
      src/i18n/messages-de.po
  48. 75
      src/i18n/messages-ja.po
  49. 75
      src/i18n/messages-ko.po
  50. 75
      src/i18n/messages-zh-cn.po
  51. 75
      src/i18n/messages-zh-tw.po
  52. 71
      src/i18n/messages.pot
  53. 16
      src/styles/Makefile.am
  54. 2
      src/styles/_shame.scss
  55. 4
      src/styles/components/_audiolevel.scss
  56. 218
      src/styles/components/_audiovideo.scss
  57. 14
      src/styles/components/_bar.scss
  58. 34
      src/styles/components/_buddylist.scss
  59. 6
      src/styles/components/_buddypicturecapture.scss
  60. 15
      src/styles/components/_buddypictureupload.scss
  61. 358
      src/styles/components/_chat.scss
  62. 4
      src/styles/components/_contactsmanager.scss
  63. 112
      src/styles/components/_fileinfo.scss
  64. 204
      src/styles/components/_presentation.scss
  65. 17
      src/styles/components/_rightslide.scss
  66. 9
      src/styles/components/_roombar.scss
  67. 24
      src/styles/components/_screenshare.scss
  68. 11
      src/styles/components/_settings.scss
  69. 2
      src/styles/components/_social.scss
  70. 31
      src/styles/components/_usability.scss
  71. 127
      src/styles/components/_youtubevideo.scss
  72. 2
      src/styles/csp.scss
  73. 5
      src/styles/font-awesome.scss
  74. 2
      src/styles/global/_animations.scss
  75. 4
      src/styles/global/_base.scss
  76. 4
      src/styles/global/_loader.scss
  77. 2
      src/styles/global/_nicescroll.scss
  78. 46
      src/styles/global/_overlaybar.scss
  79. 4
      src/styles/global/_pages.scss
  80. 2
      src/styles/global/_variables.scss
  81. 2
      src/styles/global/_views.scss
  82. 2
      src/styles/global/_withs.scss
  83. 2
      src/styles/libs/_dialogs.scss
  84. 2
      src/styles/libs/angular/angular-csp.scss
  85. 2
      src/styles/libs/angular/angular.scss
  86. 2
      src/styles/main.scss
  87. 4
      src/styles/scss.yml
  88. 2
      static/css/csp.min.css
  89. 2
      static/css/font-awesome.min.css
  90. 4
      static/css/main.min.css
  91. 34
      static/js/app.js
  92. 6
      static/js/base.js
  93. 122
      static/js/controllers/appcontroller.js
  94. 70
      static/js/controllers/chatroomcontroller.js
  95. 2
      static/js/controllers/contactsmanagercontroller.js
  96. 2
      static/js/controllers/contactsmanagereditcontroller.js
  97. 12
      static/js/controllers/controllers.js
  98. 2
      static/js/controllers/statusmessagecontroller.js
  99. 211
      static/js/controllers/uicontroller.js
  100. 2
      static/js/controllers/usersettingscontroller.js
  101. Some files were not shown because too many files have changed in this diff Show More

5
.travis.yml

@ -6,6 +6,7 @@ go: @@ -6,6 +6,7 @@ go:
- 1.1
- 1.2
- 1.3
- 1.4
- tip
env:
@ -25,7 +26,11 @@ script: @@ -25,7 +26,11 @@ script:
- ./autogen.sh
- ./configure
- make get
- make styleshint
# TODO(fancycode): enable styleslint once all styles have been fixed
# - make styleslint
- make styles
- make jshint
- make javascript
- make binary
- make build-i18n

2
Makefile.am

@ -131,6 +131,7 @@ install: @@ -131,6 +131,7 @@ install:
$(INSTALL) -d $(SHARE)/www/static/translation
$(INSTALL) -d $(SHARE)/www/static/css
$(INSTALL) -d $(SHARE)/www/static/js/libs/pdf
$(INSTALL) -d $(SHARE)/www/static/js/sandboxes
$(INSTALL) bin/$(EXENAME) $(BIN)
$(INSTALL) html/* $(SHARE)/www/html
$(INSTALL) static/img/* $(SHARE)/www/static/img
@ -142,6 +143,7 @@ install: @@ -142,6 +143,7 @@ install:
$(INSTALL) $(OUTPUT_JS)/*.js $(SHARE)/www/static/js
$(INSTALL) $(OUTPUT_JS)/libs/pdf/*.js $(SHARE)/www/static/js/libs/pdf
$(INSTALL) -D static/js/libs/webodf.js $(SHARE)/www/static/js/libs/webodf.js
$(INSTALL) $(OUTPUT_JS)/sandboxes/*.js $(SHARE)/www/static/js/sandboxes
clean:
$(GO) clean -i -r app/... 2>/dev/null || true

2
README.md

@ -95,7 +95,7 @@ locally by running `npm install` from the project root. Consult the @@ -95,7 +95,7 @@ locally by running `npm install` from the project root. Consult the
`package.json` file for more details.
- [autoprefixer](https://www.npmjs.org/package/autoprefixer) >= 1.1
- [po2json](https://github.com/mikeedwards/po2json)
- [po2json](https://github.com/mikeedwards/po2json) >= 0.4.1
- [JSHint](http://www.jshint.com/) >= 2.0.0
- [scss-lint](https://github.com/causes/scss-lint) >= 0.33.0

21
build/build.js

@ -76,6 +76,27 @@ @@ -76,6 +76,27 @@
override: {
skipModuleInsertion: true
}
},
{
name: 'sandboxes/youtube',
dir: './out/sandboxes',
override: {
skipModuleInsertion: true
}
},
{
name: 'sandboxes/pdf',
dir: './out/sandboxes',
override: {
skipModuleInsertion: true
}
},
{
name: 'sandboxes/webodf',
dir: './out/sandboxes',
override: {
skipModuleInsertion: true
}
}
]
})

7
configure.ac

@ -38,6 +38,7 @@ NODEJS_VERSION_MIN=0.6.0 @@ -38,6 +38,7 @@ NODEJS_VERSION_MIN=0.6.0
NODEJS_VERSION_STYLES_MIN=0.10.0
SASS_VERSION_MIN=3.3.0
SCSS_LINT_VERSION_MIN=0.33.0
PO2JSON_VERSION_MIN=0.4.1
AC_CONFIG_SRCDIR([src/app/spreed-webrtc-server/main.go])
AC_CONFIG_MACRO_DIR([m4])
@ -183,9 +184,11 @@ if test x"${NPM}" != x"" ; then @@ -183,9 +184,11 @@ if test x"${NPM}" != x"" ; then
else
AC_MSG_RESULT([ok])
AC_MSG_CHECKING([for version of po2json])
PO2JSON_VERSION=`{ $NPM list --global & $NPM list; } 2>&1 | $GREP -v 'required' | $GREP po2json@ | $SED 's/^.*po2json@//'`
PO2JSON_VERSION=`{ $NPM list --global & $NPM list; } 2>&1 | $GREP -v 'required' | $GREP po2json@ | tail -n1 | $SED 's/^.*po2json@//'`
AC_MSG_RESULT([$PO2JSON_VERSION])
NODEJS_SUPPORT_PO2JSON=yes
AX_COMPARE_VERSION([$PO2JSON_VERSION], [lt], [$PO2JSON_VERSION_MIN],
[AC_MSG_WARN([Please install po2json version $PO2JSON_VERSION_MIN or newer before trying to build translations (found po2json $PO2JSON_VERSION).])
NODEJS_SUPPORT_PO2JSON=no],[NODEJS_SUPPORT_PO2JSON=yes])
fi
else
AC_MSG_WARN([Please install npm and the the node.js module po2json to build i18n.])

41
debian/changelog vendored

@ -1,3 +1,44 @@ @@ -1,3 +1,44 @@
spreed-webrtc-server (0.24.0) precise; urgency=low
* Added hover actions on buddy picture in group chat.
* Jed.js was updated to 1.1.0 including API update for translations.
* Fixed replaced session data receive problem.
* Chat rooms are now reenabled on certain conditions.
* Session close notification is now always sent both directions.
* Reorganized scss.
* Improved null pointer handling in server code.
* Improved server API names to follow general rules.
* TURN and STUN data is now created in constraints service.
* Added screen sharing support for Firefox >= 38.
* Added video resolution selection for Firefox >= 38.
* Split up mediastreamcontroller in multiple parts.
* Reconnect delay is now gradually increased.
* Added basic romm type support.
* Server API was bumped to 1.2.
* Added room name support (Server API 1.2).
* Slashes are now allowed unquoted in room names.
* Spaces are no longer stripped in room path parts.
* Sleepy was replaced by external library Sloth.
* Authorizing flag is now available in scope to avoid flash of sign-in button.
* Copyright was bumped to 2015.
* Youtube player now runs in sandboxed iframe.
* Allow HD video constraints for Firefox >= 38.
* Presentaion (WebODF) now runs in sandboxed iframe.
* Example CSP was updated to work with sandboxed iframe of Youtube and WebODF.
* Load of web fonts is now detected to avoid fouf.
* Added support to enable Opus DTX constraint.
* Fixed problem where a stream without audio was added to audio processor.
* Added support for renegotiation to web client.
* Added audio only styles in web client.
* Receiver can now receive a connection without a stream.
* Youtube playback now has error handling.
* Avoid some fout.
* Firefox will now hang up on renegotiation (if enabled).
* Styles were split up, so they can be built seperately.
* Fixed a problem, where Chrome thought it already had an offer.
-- Simon Eisenmann <simon@struktur.de> Tue, 16 Jun 2015 22:50:46 +0200
spreed-webrtc-server (0.23.8) precise; urgency=low
* Session subscriptions now notify close both ways.

75
doc/CHANNELING-API.txt

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
Spreed WebRTC Channeling API v1.3.0
Spreed WebRTC Channeling API v1.4.0
=================================================
(c)2014 struktur AG
(c)2015 struktur AG
The server provides a Websocket connection end point as channeling API to
share peer information for peer to peer connectivity.
@ -98,6 +98,13 @@ Error returns @@ -98,6 +98,13 @@ Error returns
"Message": "A description of the error condition"
}
The following predefined error codes may implicitly be returned by any call
which returns an error document:
unknown: An internal server error, the message may provide more information.
bad_request: The structure or content of the client's request was invalid,
the message may contain specifics.
Special purpose documents for channling
Self
@ -110,6 +117,7 @@ Special purpose documents for channling @@ -110,6 +117,7 @@ Special purpose documents for channling
"Suserid": "",
"Token": "some-very-long-string",
"Version": "server-version-number",
"ApiVersion": 1.4,
"Turn": {
"username": "turn-username",
"password": "turn-password",
@ -132,11 +140,16 @@ Special purpose documents for channling @@ -132,11 +140,16 @@ Special purpose documents for channling
Type : Self (string)
Id : Public Session id for this connection (string).
Sid : Secure (non public) id for this session (string).
Userid : User id if this session belongs to an authenticated user. Else empty.
Suserid : Secure (non public) user id if session has an user id. Else empty.
Token : Security token (string), to restablish connection with the same
session. Pass the value as URL query parameter t, to the websocket URL.
Userid : User id if this session belongs to an authenticated user.
Else empty.
Suserid : Secure (non public) user id if session has an user id.
Else empty.
Token : Security token (string), to restablish connection with the
same session. Pass the value as URL query parameter t, to
the websocket URL.
Version : Server version number. Use this to detect server upgrades.
ApiVersion : Server channeling API base version. Use this version to select
client side compatibility with the connected server.
Turn : Mapping (interface{}) to contain TURN server details, like
urls, password and username. See
http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00
@ -155,9 +168,10 @@ Special purpose documents for channling @@ -155,9 +168,10 @@ Special purpose documents for channling
{
Type: "Hello",
Hello: {
Version: "1.0.0",
Ua: "Test client 1.0",
Id: "",
"Version": "1.0.0",
"Ua": "Test client 1.0",
"Name": "",
"Type": "",
"Credentials": {...}
}
}
@ -172,7 +186,10 @@ Special purpose documents for channling @@ -172,7 +186,10 @@ Special purpose documents for channling
Version : Channel protocol version (string).
Ua : User agent description (string).
Id : Room id. The default Room has the empty string Id ("") (string).
Name : Room name. The default Room has the empty string name ("") (string).
Type : Room type. Use empty string to let the server select the
default type.
Id : Same as 'Name' (kept for compatibility).
Credentials : An optional RoomCredentials document containing room
authentication information. See the Room document for
information on how such credentials should be handled after
@ -234,7 +251,7 @@ Special purpose documents for channling @@ -234,7 +251,7 @@ Special purpose documents for channling
Room
{
"Type": "Room",
"Type": "room-type",
"Name": "room-name-here"
"Credentials": {...}
}
@ -249,8 +266,10 @@ Special purpose documents for channling @@ -249,8 +266,10 @@ Special purpose documents for channling
Keys under Room:
Name : The human readable ID of the room, currently must be globally
unique.
Type : The room type. This field should only be send to alter
the room type. It will always contain the type of the room
when returned by the server.
Name : The human readable name of the room.
Credentials : Optional authentication information for the room, see the
documentation of the RoomCredentials document for more
details. This field shall only be present when sending or
@ -427,9 +446,10 @@ Additional types for session listing and notifications @@ -427,9 +446,10 @@ Additional types for session listing and notifications
Rev is the status update sequence for this status update entry. It
is a positive integer. Higher numbers are later status updates.
When the current session is in a room (means sent Hello), a Users request
can be sent, to receive a list of sessions in that particular room. This
always returns the sessions in the same room as the calling session.
When the current session has successfully joined a room (see Hello for more
details), a Users request will return a Users document containing session
details for the current room. An Error document will be returned if no room
has been joined or session information cannot be retrieved.
Users (Request uses empty data)
@ -470,6 +490,10 @@ Additional types for session listing and notifications @@ -470,6 +490,10 @@ Additional types for session listing and notifications
Note: The Userid field is only present, if that session belongs to a known user.
Error codes:
not_in_room: Clients must join a room before requesting users.
Alive
{
@ -506,12 +530,19 @@ User authorization and session authentication @@ -506,12 +530,19 @@ User authorization and session authentication
The Authentication document binds a userid to the current session. The
Nonce and Userid need to be validateable by the server. If Authentication
was successfull, a new Self document will be sent. The Nonce value can
be generated by using the REST API (sessions end point).
was successful, a new Self document will be sent. Otherwise an Error
document will be returned describing why authentication failed. Note that
the Nonce value can be generated by using the REST API (sessions end point).
There is no way to undo authentication for a session. For log out, close
the session (disconnect) and forget the token.
Error codes:
already_authenticated: This session has already authenticated, follow
the reauthentication procedure above.
invalid_session_token: The provided session token information is invalid,
the error message may contain more information.
Information retrieval
@ -532,6 +563,9 @@ Information retrieval @@ -532,6 +563,9 @@ Information retrieval
Token data retrieved on incoming messages as A field (attestation
token).
If session information retrieval fails, an Error document with one of the
listed codes will be returned.
Sessions (Response with Id, Token and Type from request and
populated Session list).
@ -558,6 +592,11 @@ Information retrieval @@ -558,6 +592,11 @@ Information retrieval
]
}
Error codes:
contacts_not_enabled: Requests with subtype `contact` are not enabled.
bad_attestation: The requested session attestation is invalid.
no_such_session: The requested session could not be found.
Chat messages and status information

1
html/logo.html

@ -1 +0,0 @@ @@ -1 +0,0 @@
<%define "logo"%><span class="logo" title="<%.Cfg.Title%>"><span><a target="_blank" href="https://github.com/strukturag/spreed-webrtc">WebRTC</a></span></span><%end%>

53
html/main.html

@ -1,61 +1,12 @@ @@ -1,61 +1,12 @@
<%define "mainPage"%><!doctype html>
<html class="no-js"<%if.Csp%> ng-csp<%end%>>
<html class="no-js wf-loading"<%if.Csp%> ng-csp<%end%>>
<head>
<%template "head" .%>
</head>
<body spreed-webrtc>
<div id="background"></div>
<div id="loader"><div><i class="fa fa-circle-o-notch fa-spin"></i><div class="loader-message"></div></div></div>
<page></page>
<div id="bar" class="navbar navbar-default navbar-fixed-top bar">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" ng-click="isCollapsed = !isCollapsed">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-brand left">
<%template "logo" .%>
</div>
</div>
<div class="navbar-collapse collapse" collapse="isCollapsed">
<ul class="nav navbar-nav navbar-right right">
<li class="ng-cloak">
<menu></menu>
</li>
</ul>
</div>
</div>
<div class="callbar middle">
<status-message></status-message>
</div>
</div>
<div id="roombar" class="ng-cloak">
<room-bar/>
</div>
<div id="audiolevel" class="ng-cloak">
<div class="audio-level" title="{{_('Your audio level')}}"></div>
</div>
<div id="audiovideo" class="ng-cloak" ng-show="peer">
<audio-video/>
</div>
<div id="screenshare" class="ng-cloak mainview">
<screenshare/>
</div>
<div id="presentation" class="ng-cloak mainview">
<presentation/>
</div>
<div id="youtubevideo" class="ng-cloak mainview">
<youtubevideo/>
</div>
<div class="ng-cloak" id="rightslide">
<div class="rightslidepane">
<div id="buddylist"><buddy-list/></div>
<div id="chat"><chat/></div>
</div>
</div>
<div class="ng-cloak" id="settings" ng-class="{show: layout.settings}"><settings/></div>
<ui></ui>
<script data-main="<%.Cfg.S%>/js/<%.App%>" data-plugin="<%.Cfg.Plugin%>" src="<%.Cfg.S%>/js/libs/require/require.js"></script>
<%template "extra-body" .%>
</body>

2
package.json

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
"private": true,
"dependencies": {
"autoprefixer": ">= 3.1.0",
"po2json": ">= 0.3.0",
"po2json": ">= 0.4.1",
"jshint": ">= 2.5.5"
}
}

5
server.conf.in

@ -57,6 +57,10 @@ listen = 127.0.0.1:8080 @@ -57,6 +57,10 @@ listen = 127.0.0.1:8080
; See http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 for details.
; A supported TURN server is https://code.google.com/p/rfc5766-turn-server/.
;turnSecret = the-default-turn-shared-secret-do-not-keep
; Enable renegotiation support. Set to true to tell clients that they can
; renegotiate peer connections when required. Firefox support is not complete,
; so do not enable if you want compatibility with Firefox clients.
;renegotiation = false
; Session secret to use for session id generator. 32 or 64 bytes of random data
; are recommented (hex encoded). A warning will be logged if hex decode fails.
; You can generate a secret easily with "xxd -ps -l 32 -c 32 /dev/random".
@ -109,6 +113,7 @@ serverRealm = local @@ -109,6 +113,7 @@ serverRealm = local
; data: URL for images.
; The currently recommended CSP is:
; default-src 'self';
; frame-src 'self' blob:;
; style-src 'self' 'unsafe-inline';
; img-src 'self' data: blob:;
; connect-src 'self' wss://server:port/ws blob:;

2
src/app/spreed-webrtc-server/api.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/buffercache.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

13
src/app/spreed-webrtc-server/channelling.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -42,7 +42,9 @@ type DataRoomCredentials struct { @@ -42,7 +42,9 @@ type DataRoomCredentials struct {
type DataHello struct {
Version string
Ua string
Id string
Id string // Compatibility with old clients.
Name string // Room name.
Type string // Room type.
Credentials *DataRoomCredentials
}
@ -53,8 +55,8 @@ type DataWelcome struct { @@ -53,8 +55,8 @@ type DataWelcome struct {
}
type DataRoom struct {
Type string
Name string
Type string // Room type.
Name string // Room name.
Credentials *DataRoomCredentials
}
@ -83,7 +85,8 @@ type DataSelf struct { @@ -83,7 +85,8 @@ type DataSelf struct {
Userid string
Suserid string
Token string
Version string
Version string // Server version.
ApiVersion float64 // Server channelling API version.
Turn *DataTurn
Stun []string
}

308
src/app/spreed-webrtc-server/channelling_api.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -28,12 +28,13 @@ import ( @@ -28,12 +28,13 @@ import (
const (
maxConferenceSize = 100
apiVersion = 1.4 // Keep this in sync with CHANNELING-API docs.Hand
)
type ChannellingAPI interface {
OnConnect(Client, *Session)
OnConnect(Client, *Session) (interface{}, error)
OnDisconnect(Client, *Session)
OnIncoming(ResponseSender, *Session, *DataIncoming)
OnIncoming(Sender, *Session, *DataIncoming) (interface{}, error)
}
type channellingAPI struct {
@ -60,182 +61,269 @@ func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEnco @@ -60,182 +61,269 @@ func NewChannellingAPI(config *Config, roomStatus RoomStatusManager, sessionEnco
}
}
func (api *channellingAPI) OnConnect(client Client, session *Session) {
func (api *channellingAPI) OnConnect(client Client, session *Session) (interface{}, error) {
api.Unicaster.OnConnect(client, session)
api.SendSelf(client, session)
return api.HandleSelf(session)
}
func (api *channellingAPI) OnDisconnect(client Client, session *Session) {
api.Unicaster.OnDisconnect(client, session)
}
func (api *channellingAPI) OnIncoming(c ResponseSender, session *Session, msg *DataIncoming) {
func (api *channellingAPI) OnIncoming(sender Sender, session *Session, msg *DataIncoming) (interface{}, error) {
switch msg.Type {
case "Self":
api.SendSelf(c, session)
return api.HandleSelf(session)
case "Hello":
//log.Println("Hello", msg.Hello, c.Index())
// TODO(longsleep): Filter room id and user agent.
session.Update(&SessionUpdate{Types: []string{"Ua"}, Ua: msg.Hello.Ua})
room, err := session.JoinRoom(msg.Hello.Id, msg.Hello.Credentials, c)
// NOTE(lcooper): Iid filtered for compatibility's sake.
// Evaluate sending unconditionally when supported by all clients.
if msg.Iid != "" {
if err == nil {
c.Reply(msg.Iid, &DataWelcome{
Type: "Welcome",
Room: room,
Users: api.RoomUsers(session),
})
} else {
c.Reply(msg.Iid, err)
}
if msg.Hello == nil {
return nil, NewDataError("bad_request", "message did not contain Hello")
}
return api.HandleHello(session, msg.Hello, sender)
case "Offer":
if msg.Offer == nil {
log.Println("Received invalid offer message.", msg)
break
}
// TODO(longsleep): Validate offer
session.Unicast(msg.Offer.To, msg.Offer)
case "Candidate":
if msg.Candidate == nil {
log.Println("Received invalid candidate message.", msg)
break
}
// TODO(longsleep): Validate candidate
session.Unicast(msg.Candidate.To, msg.Candidate)
case "Answer":
if msg.Answer == nil {
log.Println("Received invalid answer message.", msg)
break
}
// TODO(longsleep): Validate Answer
session.Unicast(msg.Answer.To, msg.Answer)
case "Users":
if session.Hello {
sessions := &DataSessions{Type: "Users", Users: api.RoomUsers(session)}
c.Reply(msg.Iid, sessions)
}
return api.HandleUsers(session)
case "Authentication":
st := msg.Authentication.Authentication
if st == nil {
return
if msg.Authentication == nil || msg.Authentication.Authentication == nil {
return nil, NewDataError("bad_request", "message did not contain Authentication")
}
if err := api.Authenticate(session, st, ""); err == nil {
log.Println("Authentication success", session.Userid())
api.SendSelf(c, session)
session.BroadcastStatus()
} else {
log.Println("Authentication failed", err, st.Userid, st.Nonce)
}
return api.HandleAuthentication(session, msg.Authentication.Authentication)
case "Bye":
if msg.Bye == nil {
log.Println("Received invalid bye message.", msg)
break
}
session.Unicast(msg.Bye.To, msg.Bye)
case "Status":
if msg.Status == nil {
log.Println("Received invalid status message.", msg)
break
}
//log.Println("Status", msg.Status)
session.Update(&SessionUpdate{Types: []string{"Status"}, Status: msg.Status.Status})
session.BroadcastStatus()
case "Chat":
if msg.Chat == nil || msg.Chat.Chat == nil {
log.Println("Received invalid chat message.", msg)
break
}
api.HandleChat(session, msg.Chat)
case "Conference":
if msg.Conference == nil {
log.Println("Received invalid conference message.", msg)
break
}
api.HandleConference(session, msg.Conference)
case "Alive":
return msg.Alive, nil
case "Sessions":
if msg.Sessions == nil || msg.Sessions.Sessions == nil {
return nil, NewDataError("bad_request", "message did not contain Sessions")
}
return api.HandleSessions(session, msg.Sessions.Sessions)
case "Room":
if msg.Room == nil {
return nil, NewDataError("bad_request", "message did not contain Room")
}
return api.HandleRoom(session, msg.Room)
default:
log.Println("OnText unhandled message type", msg.Type)
}
return nil, nil
}
func (api *channellingAPI) HandleSelf(session *Session) (*DataSelf, error) {
token, err := api.EncodeSessionToken(session)
if err != nil {
log.Println("Error in OnRegister", err)
return nil, err
}
log.Println("Created new session token", len(token), token)
self := &DataSelf{
Type: "Self",
Id: session.Id,
Sid: session.Sid,
Userid: session.Userid(),
Suserid: api.EncodeSessionUserID(session),
Token: token,
Version: api.Version,
ApiVersion: apiVersion,
Turn: api.CreateTurnData(session),
Stun: api.StunURIs,
}
return self, nil
}
func (api *channellingAPI) HandleHello(session *Session, hello *DataHello, sender Sender) (*DataWelcome, error) {
// TODO(longsleep): Filter room id and user agent.
session.Update(&SessionUpdate{Types: []string{"Ua"}, Ua: hello.Ua})
// Compatibily for old clients.
roomName := hello.Name
if roomName == "" {
roomName = hello.Id
}
room, err := session.JoinRoom(roomName, hello.Type, hello.Credentials, sender)
if err != nil {
return nil, err
}
return &DataWelcome{
Type: "Welcome",
Room: room,
Users: api.RoomUsers(session),
}, nil
}
func (api *channellingAPI) HandleUsers(session *Session) (sessions *DataSessions, err error) {
if session.Hello {
sessions = &DataSessions{Type: "Users", Users: api.RoomUsers(session)}
} else {
err = NewDataError("not_in_room", "Cannot list users without a current room")
}
return
}
func (api *channellingAPI) HandleAuthentication(session *Session, st *SessionToken) (*DataSelf, error) {
if err := api.Authenticate(session, st, ""); err != nil {
log.Println("Authentication failed", err, st.Userid, st.Nonce)
return nil, err
}
log.Println("Authentication success", session.Userid())
self, err := api.HandleSelf(session)
if err == nil {
session.BroadcastStatus()
}
return self, err
}
func (api *channellingAPI) HandleChat(session *Session, chat *DataChat) {
// TODO(longsleep): Limit sent chat messages per incoming connection.
if !msg.Chat.Chat.NoEcho {
session.Unicast(session.Id, msg.Chat)
msg := chat.Chat
to := chat.To
if !msg.NoEcho {
session.Unicast(session.Id, chat)
}
msg.Chat.Chat.Time = time.Now().Format(time.RFC3339)
if msg.Chat.To == "" {
msg.Time = time.Now().Format(time.RFC3339)
if to == "" {
// TODO(longsleep): Check if chat broadcast is allowed.
if session.Hello {
api.CountBroadcastChat()
session.Broadcast(msg.Chat)
session.Broadcast(chat)
}
} else {
if msg.Chat.Chat.Status != nil && msg.Chat.Chat.Status.ContactRequest != nil {
if msg.Status != nil {
if msg.Status.ContactRequest != nil {
if !api.Config.WithModule("contacts") {
return
}
if err := api.contactrequestHandler(session, msg.Chat.To, msg.Chat.Chat.Status.ContactRequest); err != nil {
if err := api.contactrequestHandler(session, to, msg.Status.ContactRequest); err != nil {
log.Println("Ignoring invalid contact request.", err)
return
}
msg.Chat.Chat.Status.ContactRequest.Userid = session.Userid()
msg.Status.ContactRequest.Userid = session.Userid()
}
if msg.Chat.Chat.Status == nil {
} else {
api.CountUnicastChat()
}
session.Unicast(msg.Chat.To, msg.Chat)
if msg.Chat.Chat.Mid != "" {
session.Unicast(to, chat)
if msg.Mid != "" {
// Send out delivery confirmation status chat message.
session.Unicast(session.Id, &DataChat{To: msg.Chat.To, Type: "Chat", Chat: &DataChatMessage{Mid: msg.Chat.Chat.Mid, Status: &DataChatStatus{State: "sent"}}})
session.Unicast(session.Id, &DataChat{To: to, Type: "Chat", Chat: &DataChatMessage{Mid: msg.Mid, Status: &DataChatStatus{State: "sent"}}})
}
}
case "Conference":
}
func (api *channellingAPI) HandleConference(session *Session, conference *DataConference) {
// Check conference maximum size.
if len(msg.Conference.Conference) > maxConferenceSize {
log.Println("Refusing to create conference above limit.", len(msg.Conference.Conference))
} else {
if len(conference.Conference) > maxConferenceSize {
log.Println("Refusing to create conference above limit.", len(conference.Conference))
return
}
// Send conference update to anyone.
for _, id := range msg.Conference.Conference {
for _, id := range conference.Conference {
if id != session.Id {
session.Unicast(id, msg.Conference)
session.Unicast(id, conference)
}
}
}
case "Alive":
c.Reply(msg.Iid, msg.Alive)
case "Sessions":
var users []*DataSession
switch msg.Sessions.Sessions.Type {
func (api *channellingAPI) HandleSessions(session *Session, sessions *DataSessionsRequest) (*DataSessions, error) {
switch sessions.Type {
case "contact":
if api.Config.WithModule("contacts") {
if userID, err := api.getContactID(session, msg.Sessions.Sessions.Token); err == nil {
users = api.GetUserSessions(session, userID)
} else {
log.Printf(err.Error())
if !api.Config.WithModule("contacts") {
return nil, NewDataError("contacts_not_enabled", "incoming contacts session request with contacts disabled")
}
} else {
log.Printf("Incoming contacts session request with contacts disabled")
userID, err := api.getContactID(session, sessions.Token)
if err != nil {
return nil, err
}
return &DataSessions{
Type: "Sessions",
Users: api.GetUserSessions(session, userID),
Sessions: sessions,
}, nil
case "session":
id, err := session.attestation.Decode(msg.Sessions.Sessions.Token)
id, err := session.attestation.Decode(sessions.Token)
if err != nil {
log.Printf("Failed to decode incoming attestation", err, msg.Sessions.Sessions.Token)
break
return nil, NewDataError("bad_attestation", err.Error())
}
session, ok := api.GetSession(id)
if !ok {
log.Printf("Cannot retrieve session for id %s", id)
break
}
users = make([]*DataSession, 1, 1)
users[0] = session.Data()
default:
log.Printf("Unkown incoming sessions request type %s", msg.Sessions.Sessions.Type)
}
// TODO(lcooper): We ought to reply with a *DataError here if failed.
if users != nil {
c.Reply(msg.Iid, &DataSessions{Type: "Sessions", Users: users, Sessions: msg.Sessions.Sessions})
}
case "Room":
if room, err := api.UpdateRoom(session, msg.Room); err == nil {
session.Broadcast(room)
c.Reply(msg.Iid, room)
} else {
c.Reply(msg.Iid, err)
return nil, NewDataError("no_such_session", "cannot retrieve session")
}
return &DataSessions{
Type: "Sessions",
Users: []*DataSession{session.Data()},
Sessions: sessions,
}, nil
default:
log.Println("OnText unhandled message type", msg.Type)
}
return nil, NewDataError("bad_request", "unknown sessions request type")
}
func (api *channellingAPI) SendSelf(c Responder, session *Session) {
token, err := api.EncodeSessionToken(session)
if err != nil {
log.Println("Error in OnRegister", err)
return
}
log.Println("Created new session token", len(token), token)
self := &DataSelf{
Type: "Self",
Id: session.Id,
Sid: session.Sid,
Userid: session.Userid(),
Suserid: api.EncodeSessionUserID(session),
Token: token,
Version: api.Version,
Turn: api.CreateTurnData(session),
Stun: api.StunURIs,
func (api *channellingAPI) HandleRoom(session *Session, room *DataRoom) (*DataRoom, error) {
room, err := api.UpdateRoom(session, room)
if err == nil {
session.Broadcast(room)
}
c.Reply("", self)
return room, err
}

97
src/app/spreed-webrtc-server/channelling_api_test.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -23,24 +23,16 @@ package main @@ -23,24 +23,16 @@ package main
import (
"errors"
"fmt"
"testing"
)
type fakeClient struct {
replies map[string]interface{}
}
func (fake *fakeClient) Send(_ Buffer) {
}
func (fake *fakeClient) Reply(iid string, msg interface{}) {
if fake.replies == nil {
fake.replies = make(map[string]interface{})
}
fake.replies[iid] = msg
}
type fakeRoomManager struct {
joinedRoomID string
leftRoomID string
@ -57,9 +49,9 @@ func (fake *fakeRoomManager) RoomUsers(session *Session) []*DataSession { @@ -57,9 +49,9 @@ func (fake *fakeRoomManager) RoomUsers(session *Session) []*DataSession {
return fake.roomUsers
}
func (fake *fakeRoomManager) JoinRoom(id string, _ *DataRoomCredentials, session *Session, sessionAuthenticated bool, _ Sender) (*DataRoom, error) {
func (fake *fakeRoomManager) JoinRoom(id, roomName, roomType string, _ *DataRoomCredentials, session *Session, sessionAuthenticated bool, _ Sender) (*DataRoom, error) {
fake.joinedID = id
return &DataRoom{Name: id}, fake.joinError
return &DataRoom{Name: roomName, Type: roomType}, fake.joinError
}
func (fake *fakeRoomManager) LeaveRoom(roomID, sessionID string) {
@ -74,27 +66,11 @@ func (fake *fakeRoomManager) UpdateRoom(_ *Session, _ *DataRoom) (*DataRoom, err @@ -74,27 +66,11 @@ func (fake *fakeRoomManager) UpdateRoom(_ *Session, _ *DataRoom) (*DataRoom, err
return fake.updatedRoom, fake.updateError
}
func assertReply(t *testing.T, client *fakeClient, iid string) interface{} {
msg, ok := client.replies[iid]
if !ok {
t.Fatalf("No response received for Iid %v", iid)
}
return msg
}
func assertErrorReply(t *testing.T, client *fakeClient, iid, code string) {
err, ok := assertReply(t, client, iid).(*DataError)
if !ok {
t.Fatalf("Expected response message to be an Error")
}
if err.Type != "Error" {
t.Error("Message did not have the correct type")
}
if err.Code != code {
t.Errorf("Expected error code to be %v, but was %v", code, err.Code)
func (fake *fakeRoomManager) MakeRoomID(roomName, roomType string) string {
if roomType == "" {
roomType = "Room"
}
return fmt.Sprintf("%s:%s", roomType, roomName)
}
func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomManager) {
@ -109,10 +85,10 @@ func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomMa @@ -109,10 +85,10 @@ func NewTestChannellingAPI() (ChannellingAPI, *fakeClient, *Session, *fakeRoomMa
}
func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing.T) {
roomID, ua := "foobar", "unit tests"
roomID, roomName, ua := "Room:foobar", "foobar", "unit tests"
api, client, session, roomManager := NewTestChannellingAPI()
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomID, Ua: ua}})
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName, Ua: ua}})
if roomManager.joinedID != roomID {
t.Errorf("Expected to have joined room %v, but got %v", roomID, roomManager.joinedID)
@ -133,10 +109,10 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing @@ -133,10 +109,10 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_JoinsTheSelectedRoom(t *testing
}
func Test_ChannellingAPI_OnIncoming_HelloMessage_LeavesAnyPreviouslyJoinedRooms(t *testing.T) {
roomID := "foobar"
roomID, roomName := "Room:foobar", "foobar"
api, client, session, roomManager := NewTestChannellingAPI()
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomID}})
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: "baz"}})
if roomManager.leftID != roomID {
@ -168,21 +144,19 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te @@ -168,21 +144,19 @@ func Test_ChannellingAPI_OnIncoming_HelloMessage_DoesNotJoinIfNotPermitted(t *te
}
}
func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t *testing.T) {
iid, roomID := "foo", "a-room"
func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAWelcome(t *testing.T) {
roomID := "a-room"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.roomUsers = []*DataSession{&DataSession{}}
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: iid, Hello: &DataHello{Id: roomID}})
msg, ok := client.replies[iid]
if !ok {
t.Fatalf("No response received for Iid %v", iid)
reply, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomID}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
welcome, ok := msg.(*DataWelcome)
welcome, ok := reply.(*DataWelcome)
if !ok {
t.Fatalf("Expected response message %#v to be a Welcome", msg)
t.Fatalf("Expected response %#v to be a Welcome", reply)
}
if welcome.Type != "Welcome" {
@ -198,25 +172,31 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t @@ -198,25 +172,31 @@ func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAWelcome(t
}
}
func Test_ChannellingAPI_OnIncoming_HelloMessageWithAnIid_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
iid := "foo"
func Test_ChannellingAPI_OnIncoming_HelloMessage_RespondsWithAnErrorIfTheRoomCannotBeJoined(t *testing.T) {
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.joinError = NewDataError("bad_join", "")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: iid, Hello: &DataHello{}})
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{}})
assertErrorReply(t, client, iid, "bad_join")
assertDataError(t, err, "bad_join")
}
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpdatedRoom(t *testing.T) {
iid, roomName := "123", "foo"
roomName := "foo"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updatedRoom = &DataRoom{Name: "FOO"}
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: "0", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Room", Iid: iid, Room: &DataRoom{Name: roomName}})
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
room, ok := assertReply(t, client, iid).(*DataRoom)
reply, err := api.OnIncoming(client, session, &DataIncoming{Type: "Room", Room: &DataRoom{Name: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
room, ok := reply.(*DataRoom)
if !ok {
t.Fatalf("Expected response message to be a Room")
}
@ -235,12 +215,15 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda @@ -235,12 +215,15 @@ func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAndBroadcastsTheUpda
}
func Test_ChannellingAPI_OnIncoming_RoomMessage_RespondsWithAnErrorIfUpdatingTheRoomFails(t *testing.T) {
iid, roomName := "123", "foo"
roomName := "foo"
api, client, session, roomManager := NewTestChannellingAPI()
roomManager.updateError = NewDataError("a_room_error", "")
api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Iid: "0", Hello: &DataHello{Id: roomName}})
api.OnIncoming(client, session, &DataIncoming{Type: "Room", Iid: iid, Room: &DataRoom{Name: roomName}})
_, err := api.OnIncoming(client, session, &DataIncoming{Type: "Hello", Hello: &DataHello{Id: roomName}})
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
_, err = api.OnIncoming(client, session, &DataIncoming{Type: "Room", Room: &DataRoom{Name: roomName}})
assertErrorReply(t, client, iid, "a_room_error")
assertDataError(t, err, "a_room_error")
}

33
src/app/spreed-webrtc-server/client.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -29,17 +29,8 @@ type Sender interface { @@ -29,17 +29,8 @@ type Sender interface {
Send(Buffer)
}
type ResponseSender interface {
Sender
Responder
}
type Responder interface {
Reply(iid string, m interface{})
}
type Client interface {
ResponseSender
Sender
Session() *Session
Index() uint64
Close()
@ -59,7 +50,11 @@ func NewClient(codec Codec, api ChannellingAPI, session *Session) *client { @@ -59,7 +50,11 @@ func NewClient(codec Codec, api ChannellingAPI, session *Session) *client {
func (client *client) OnConnect(conn Connection) {
client.Connection = conn
client.ChannellingAPI.OnConnect(client, client.session)
if reply, err := client.ChannellingAPI.OnConnect(client, client.session); err == nil {
client.reply("", reply)
} else {
log.Println("OnConnect error", err)
}
}
func (client *client) OnDisconnect() {
@ -68,14 +63,20 @@ func (client *client) OnDisconnect() { @@ -68,14 +63,20 @@ func (client *client) OnDisconnect() {
}
func (client *client) OnText(b Buffer) {
if incoming, err := client.DecodeIncoming(b); err == nil {
client.OnIncoming(client, client.session, incoming)
} else {
incoming, err := client.DecodeIncoming(b)
if err != nil {
log.Println("OnText error while processing incoming message", err)
return
}
if reply, err := client.OnIncoming(client, client.session, incoming); err != nil {
client.reply(incoming.Iid, err)
} else if reply != nil {
client.reply(incoming.Iid, reply)
}
}
func (client *client) Reply(iid string, m interface{}) {
func (client *client) reply(iid string, m interface{}) {
outgoing := &DataOutgoing{From: client.session.Id, Iid: iid, Data: m}
if b, err := client.EncodeOutgoing(outgoing); err == nil {
client.Send(b)

2
src/app/spreed-webrtc-server/common_test.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

8
src/app/spreed-webrtc-server/config.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -36,6 +36,7 @@ type Config struct { @@ -36,6 +36,7 @@ type Config struct {
S string // Static URL prefix with version
B string // Base URL
Token string // Server token
Renegotiation bool // Renegotiation flag
StunURIs []string // STUN server URIs
TurnURIs []string // TURN server URIs
Tokens bool // True when we got a tokens file
@ -52,6 +53,7 @@ type Config struct { @@ -52,6 +53,7 @@ type Config struct {
globalRoomID string // Id of the global room (not exported to Javascript)
contentSecurityPolicy string // HTML content security policy
contentSecurityPolicyReportOnly string // HTML content security policy in report only mode
roomTypeDefault string // New rooms default to this type
}
func NewConfig(container phoenix.Container, tokens bool) *Config {
@ -96,7 +98,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config { @@ -96,7 +98,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config {
"contacts": true,
}
modules := []string{}
for module, _ := range modulesTable {
for module := range modulesTable {
if container.GetBoolDefault("modules", module, true) {
modules = append(modules, module)
} else {
@ -111,6 +113,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config { @@ -111,6 +113,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config {
S: fmt.Sprintf("static/ver=%s", ver),
B: basePath,
Token: serverToken,
Renegotiation: container.GetBoolDefault("app", "renegotiation", false),
StunURIs: stunURIs,
TurnURIs: turnURIs,
Tokens: tokens,
@ -127,6 +130,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config { @@ -127,6 +130,7 @@ func NewConfig(container phoenix.Container, tokens bool) *Config {
globalRoomID: container.GetStringDefault("app", "globalRoom", ""),
contentSecurityPolicy: container.GetStringDefault("app", "contentSecurityPolicy", ""),
contentSecurityPolicyReportOnly: container.GetStringDefault("app", "contentSecurityPolicyReportOnly", ""),
roomTypeDefault: "Room",
}
}

2
src/app/spreed-webrtc-server/connection.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/contact.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/context.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/hub.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/images.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/incoming_codec.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

18
src/app/spreed-webrtc-server/main.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -22,7 +22,6 @@ @@ -22,7 +22,6 @@
package main
import (
"app/spreed-webrtc-server/sleepy"
"bytes"
"crypto/rand"
"encoding/hex"
@ -32,6 +31,7 @@ import ( @@ -32,6 +31,7 @@ import (
"github.com/strukturag/goacceptlanguageparser"
"github.com/strukturag/httputils"
"github.com/strukturag/phoenix"
"github.com/strukturag/sloth"
"html/template"
"log"
"net/http"
@ -196,7 +196,7 @@ func runner(runtime phoenix.Runtime) error { @@ -196,7 +196,7 @@ func runner(runtime phoenix.Runtime) error {
sessionSecretString, err := runtime.GetString("app", "sessionSecret")
if err != nil {
return fmt.Errorf("No sessionSecret in config file.")
} else {
}
sessionSecret, err = hex.DecodeString(sessionSecretString)
if err != nil {
log.Println("Warning: sessionSecret value is not a hex encoded", err)
@ -205,7 +205,6 @@ func runner(runtime phoenix.Runtime) error { @@ -205,7 +205,6 @@ func runner(runtime phoenix.Runtime) error {
if len(sessionSecret) < 32 {
return fmt.Errorf("Length of sessionSecret must be at least 32 bytes.")
}
}
if len(sessionSecret) < 32 {
log.Printf("Weak sessionSecret (only %d bytes). It is recommended to use a key with 32 or 64 bytes.\n", len(sessionSecret))
@ -215,7 +214,7 @@ func runner(runtime phoenix.Runtime) error { @@ -215,7 +214,7 @@ func runner(runtime phoenix.Runtime) error {
encryptionSecretString, err := runtime.GetString("app", "encryptionSecret")
if err != nil {
return fmt.Errorf("No encryptionSecret in config file.")
} else {
}
encryptionSecret, err = hex.DecodeString(encryptionSecretString)
if err != nil {
log.Println("Warning: encryptionSecret value is not a hex encoded", err)
@ -228,7 +227,6 @@ func runner(runtime phoenix.Runtime) error { @@ -228,7 +227,6 @@ func runner(runtime phoenix.Runtime) error {
default:
return fmt.Errorf("Length of encryptionSecret must be exactly 16, 24 or 32 bytes to select AES-128, AES-192 or AES-256.")
}
}
var turnSecret []byte
turnSecretString, err := runtime.GetString("app", "turnSecret")
@ -352,10 +350,12 @@ func runner(runtime phoenix.Runtime) error { @@ -352,10 +350,12 @@ func runner(runtime phoenix.Runtime) error {
r.Handle("/robots.txt", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static")))))
r.Handle("/favicon.ico", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static", "img")))))
r.Handle("/ws", makeWSHandler(statsManager, sessionManager, codec, channellingAPI))
// Simple room handler.
r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler))
// Add API end points.
api := sleepy.NewAPI()
api := sloth.NewAPI()
api.SetMux(r.PathPrefix("/api/v1/").Subrouter())
api.AddResource(&Rooms{}, "/rooms")
api.AddResource(config, "/config")
@ -382,6 +382,10 @@ func runner(runtime phoenix.Runtime) error { @@ -382,6 +382,10 @@ func runner(runtime phoenix.Runtime) error {
}
}
// Map everything else to a room when it is a GET.
rooms := r.PathPrefix("/").Methods("GET").Subrouter()
rooms.HandleFunc("/{room:.*}", httputils.MakeGzipHandler(roomHandler))
return runtime.Start()
}

2
src/app/spreed-webrtc-server/random.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

49
src/app/spreed-webrtc-server/room_manager.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -22,15 +22,17 @@ @@ -22,15 +22,17 @@
package main
import (
"fmt"
"log"
"sync"
)
type RoomStatusManager interface {
RoomUsers(*Session) []*DataSession
JoinRoom(roomID string, credentials *DataRoomCredentials, session *Session, sessionAuthenticated bool, sender Sender) (*DataRoom, error)
JoinRoom(roomID, roomName, roomType string, credentials *DataRoomCredentials, session *Session, sessionAuthenticated bool, sender Sender) (*DataRoom, error)
LeaveRoom(roomID, sessionID string)
UpdateRoom(*Session, *DataRoom) (*DataRoom, error)
MakeRoomID(roomName, roomType string) string
}
type Broadcaster interface {
@ -52,15 +54,20 @@ type roomManager struct { @@ -52,15 +54,20 @@ type roomManager struct {
*Config
OutgoingEncoder
roomTable map[string]RoomWorker
globalRoomID string
defaultRoomID string
}
func NewRoomManager(config *Config, encoder OutgoingEncoder) RoomManager {
return &roomManager{
sync.RWMutex{},
config,
encoder,
make(map[string]RoomWorker),
rm := &roomManager{
RWMutex: sync.RWMutex{},
Config: config,
OutgoingEncoder: encoder,
roomTable: make(map[string]RoomWorker),
}
rm.globalRoomID = rm.MakeRoomID(config.globalRoomID, "")
rm.defaultRoomID = rm.MakeRoomID("", "")
return rm
}
func (rooms *roomManager) RoomUsers(session *Session) []*DataSession {
@ -71,12 +78,12 @@ func (rooms *roomManager) RoomUsers(session *Session) []*DataSession { @@ -71,12 +78,12 @@ func (rooms *roomManager) RoomUsers(session *Session) []*DataSession {
return []*DataSession{}
}
func (rooms *roomManager) JoinRoom(roomID string, credentials *DataRoomCredentials, session *Session, sessionAuthenticated bool, sender Sender) (*DataRoom, error) {
if roomID == "" && !rooms.DefaultRoomEnabled {
func (rooms *roomManager) JoinRoom(roomID, roomName, roomType string, credentials *DataRoomCredentials, session *Session, sessionAuthenticated bool, sender Sender) (*DataRoom, error) {
if roomID == rooms.defaultRoomID && !rooms.DefaultRoomEnabled {
return nil, NewDataError("default_room_disabled", "The default room is not enabled")
}
roomWorker, err := rooms.GetOrCreate(roomID, credentials, sessionAuthenticated)
roomWorker, err := rooms.GetOrCreate(roomID, roomName, roomType, credentials, sessionAuthenticated)
if err != nil {
return nil, err
}
@ -91,15 +98,18 @@ func (rooms *roomManager) LeaveRoom(roomID, sessionID string) { @@ -91,15 +98,18 @@ func (rooms *roomManager) LeaveRoom(roomID, sessionID string) {
}
func (rooms *roomManager) UpdateRoom(session *Session, room *DataRoom) (*DataRoom, error) {
if !session.Hello || session.Roomid != room.Name {
var roomID string
if room != nil {
roomID = rooms.MakeRoomID(room.Name, room.Type)
}
if !session.Hello || session.Roomid != roomID {
return nil, NewDataError("not_in_room", "Cannot update other rooms")
}
// XXX(lcooper): We'll process and send documents without this field
// correctly, however clients cannot not handle it currently.
room.Type = "Room"
if roomWorker, ok := rooms.Get(session.Roomid); ok {
return room, roomWorker.Update(room)
}
// Set default room type if room was not found.
room.Type = rooms.roomTypeDefault
// TODO(lcooper): We should almost certainly return an error in this case.
return room, nil
}
@ -145,7 +155,7 @@ func (rooms *roomManager) Get(roomID string) (room RoomWorker, ok bool) { @@ -145,7 +155,7 @@ func (rooms *roomManager) Get(roomID string) (room RoomWorker, ok bool) {
return
}
func (rooms *roomManager) GetOrCreate(roomID string, credentials *DataRoomCredentials, sessionAuthenticated bool) (RoomWorker, error) {
func (rooms *roomManager) GetOrCreate(roomID, roomName, roomType string, credentials *DataRoomCredentials, sessionAuthenticated bool) (RoomWorker, error) {
if rooms.AuthorizeRoomJoin && rooms.UsersEnabled && !sessionAuthenticated {
return nil, NewDataError("room_join_requires_account", "Room join requires a user account")
}
@ -167,7 +177,7 @@ func (rooms *roomManager) GetOrCreate(roomID string, credentials *DataRoomCreden @@ -167,7 +177,7 @@ func (rooms *roomManager) GetOrCreate(roomID string, credentials *DataRoomCreden
return nil, NewDataError("room_join_requires_account", "Room creation requires a user account")
}
room := NewRoomWorker(rooms, roomID, credentials)
room := NewRoomWorker(rooms, roomID, roomName, roomType, credentials)
rooms.roomTable[roomID] = room
rooms.Unlock()
go func() {
@ -196,3 +206,10 @@ func (rooms *roomManager) GlobalUsers() []*roomUser { @@ -196,3 +206,10 @@ func (rooms *roomManager) GlobalUsers() []*roomUser {
rooms.RUnlock()
return make([]*roomUser, 0)
}
func (rooms *roomManager) MakeRoomID(roomName, roomType string) string {
if roomType == "" {
roomType = rooms.roomTypeDefault
}
return fmt.Sprintf("%s:%s", roomType, roomName)
}

20
src/app/spreed-webrtc-server/room_manager_test.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -36,16 +36,16 @@ func Test_RoomManager_JoinRoom_ReturnsAnErrorForUnauthenticatedSessionsWhenCreat @@ -36,16 +36,16 @@ func Test_RoomManager_JoinRoom_ReturnsAnErrorForUnauthenticatedSessionsWhenCreat
config.AuthorizeRoomCreation = true
unauthenticatedSession := &Session{}
_, err := roomManager.JoinRoom("foo", nil, unauthenticatedSession, false, nil)
_, err := roomManager.JoinRoom("Room:foo", "foo", "Room", nil, unauthenticatedSession, false, nil)
assertDataError(t, err, "room_join_requires_account")
authenticatedSession := &Session{userid: "9870457"}
_, err = roomManager.JoinRoom("foo", nil, authenticatedSession, true, nil)
_, err = roomManager.JoinRoom("Room:foo", "foo", "Room", nil, authenticatedSession, true, nil)
if err != nil {
t.Fatalf("Unexpected error %v joining room while authenticated", err)
}
_, err = roomManager.JoinRoom("foo", nil, unauthenticatedSession, false, nil)
_, err = roomManager.JoinRoom("Room:foo", "foo", "Room", nil, unauthenticatedSession, false, nil)
if err != nil {
t.Fatalf("Unexpected error %v joining room while unauthenticated", err)
}
@ -57,16 +57,16 @@ func Test_RoomManager_JoinRoom_ReturnsAnErrorForUnauthenticatedSessionsWhenJoinR @@ -57,16 +57,16 @@ func Test_RoomManager_JoinRoom_ReturnsAnErrorForUnauthenticatedSessionsWhenJoinR
config.AuthorizeRoomJoin = true
unauthenticatedSession := &Session{}
_, err := roomManager.JoinRoom("foo", nil, unauthenticatedSession, false, nil)
_, err := roomManager.JoinRoom("Room:foo", "foo", "Room", nil, unauthenticatedSession, false, nil)
assertDataError(t, err, "room_join_requires_account")
authenticatedSession := &Session{userid: "9870457"}
_, err = roomManager.JoinRoom("foo", nil, authenticatedSession, true, nil)
_, err = roomManager.JoinRoom("Room:foo", "foo", "Room", nil, authenticatedSession, true, nil)
if err != nil {
t.Fatalf("Unexpected error %v joining room while authenticated", err)
}
_, err = roomManager.JoinRoom("foo", nil, unauthenticatedSession, false, nil)
_, err = roomManager.JoinRoom("Room:foo", "foo", "Room", nil, unauthenticatedSession, false, nil)
assertDataError(t, err, "room_join_requires_account")
}
@ -79,15 +79,15 @@ func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfNoRoomHasBeenJoined(t *testing. @@ -79,15 +79,15 @@ func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfNoRoomHasBeenJoined(t *testing.
func Test_RoomManager_UpdateRoom_ReturnsAnErrorIfUpdatingAnUnjoinedRoom(t *testing.T) {
roomManager, _ := NewTestRoomManager()
session := &Session{Hello: true, Roomid: "foo"}
session := &Session{Hello: true, Roomid: "Room:foo"}
_, err := roomManager.UpdateRoom(session, &DataRoom{Name: "bar"})
assertDataError(t, err, "not_in_room")
}
func Test_RoomManager_UpdateRoom_ReturnsACorrectlyTypedDocument(t *testing.T) {
roomManager, _ := NewTestRoomManager()
session := &Session{Hello: true, Roomid: "foo"}
room, err := roomManager.UpdateRoom(session, &DataRoom{Name: session.Roomid})
session := &Session{Hello: true, Roomid: "Room:foo"}
room, err := roomManager.UpdateRoom(session, &DataRoom{Name: "foo"})
if err != nil {
t.Fatalf("Unexpected error %v updating room", err)
}

2
src/app/spreed-webrtc-server/rooms.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

26
src/app/spreed-webrtc-server/roomworker.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -56,7 +56,9 @@ type roomWorker struct { @@ -56,7 +56,9 @@ type roomWorker struct {
mutex sync.RWMutex
// Metadata.
Id string
id string
Name string
Type string
credentials *DataRoomCredentials
}
@ -65,13 +67,15 @@ type roomUser struct { @@ -65,13 +67,15 @@ type roomUser struct {
Sender
}
func NewRoomWorker(manager *roomManager, id string, credentials *DataRoomCredentials) RoomWorker {
func NewRoomWorker(manager *roomManager, roomID, roomName, roomType string, credentials *DataRoomCredentials) RoomWorker {
log.Printf("Creating worker for room '%s'\n", id)
log.Printf("Creating worker for room '%s'\n", roomID)
r := &roomWorker{
manager: manager,
Id: id,
id: roomID,
Name: roomName,
Type: roomType,
workers: make(chan func(), roomMaxWorkers),
expired: make(chan bool),
users: make(map[string]*roomUser),
@ -107,7 +111,7 @@ L: @@ -107,7 +111,7 @@ L:
if len(r.users) == 0 {
// Cleanup room when it is empty.
r.mutex.RUnlock()
log.Printf("Room worker not in use - cleaning up '%s'\n", r.Id)
log.Printf("Room worker not in use - cleaning up '%s'\n", r.id)
break L
} else {
r.mutex.RUnlock()
@ -149,7 +153,7 @@ func (r *roomWorker) Run(f func()) bool { @@ -149,7 +153,7 @@ func (r *roomWorker) Run(f func()) bool {
case r.workers <- f:
return true
default:
log.Printf("Room worker channel full or closed '%s'\n", r.Id)
log.Printf("Room worker channel full or closed '%s'\n", r.id)
return false
}
@ -159,6 +163,10 @@ func (r *roomWorker) Update(room *DataRoom) error { @@ -159,6 +163,10 @@ func (r *roomWorker) Update(room *DataRoom) error {
fault := make(chan error, 1)
worker := func() {
r.mutex.Lock()
// Enforce room type and name.
room.Type = r.Type
room.Name = r.Name
// Update credentials.
if room.Credentials != nil {
if len(room.Credentials.PIN) > 0 {
r.credentials = room.Credentials
@ -184,7 +192,7 @@ func (r *roomWorker) GetUsers() []*DataSession { @@ -184,7 +192,7 @@ func (r *roomWorker) GetUsers() []*DataSession {
session.Type = "Online"
sl = append(sl, session)
if len(sl) > maxUsersLength {
log.Println("Limiting users response length in channel", r.Id)
log.Println("Limiting users response length in channel", r.id)
return false
}
}
@ -264,7 +272,7 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se @@ -264,7 +272,7 @@ func (r *roomWorker) Join(credentials *DataRoomCredentials, session *Session, se
r.users[session.Id] = &roomUser{session, sender}
// NOTE(lcooper): Needs to be a copy, else we risk races with
// a subsequent modification of room properties.
result := joinResult{&DataRoom{Name: r.Id}, nil}
result := joinResult{&DataRoom{Name: r.Name, Type: r.Type}, nil}
r.mutex.Unlock()
results <- result
}

8
src/app/spreed-webrtc-server/roomworker_test.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -26,18 +26,20 @@ import ( @@ -26,18 +26,20 @@ import (
)
const (
testRoomID string = "Room:a-room-name"
testRoomName string = "a-room-name"
testRoomType string = "Room"
)
func NewTestRoomWorker() RoomWorker {
worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomName, nil)
worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomID, testRoomName, testRoomType, nil)
go worker.Start()
return worker
}
func NewTestRoomWorkerWithPIN(t *testing.T) (RoomWorker, string) {
pin := "asdf"
worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomName, &DataRoomCredentials{PIN: pin})
worker := NewRoomWorker(&roomManager{Config: &Config{}}, testRoomID, testRoomName, testRoomType, &DataRoomCredentials{PIN: pin})
go worker.Start()
return worker, pin
}

23
src/app/spreed-webrtc-server/session.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -22,7 +22,6 @@ @@ -22,7 +22,6 @@
package main
import (
"errors"
"fmt"
"github.com/gorilla/securecookie"
"strings"
@ -117,7 +116,8 @@ func (s *Session) RemoveSubscriber(id string) { @@ -117,7 +116,8 @@ func (s *Session) RemoveSubscriber(id string) {
s.mutex.Unlock()
}
func (s *Session) JoinRoom(roomID string, credentials *DataRoomCredentials, sender Sender) (*DataRoom, error) {
func (s *Session) JoinRoom(roomName, roomType string, credentials *DataRoomCredentials, sender Sender) (*DataRoom, error) {
roomID := s.RoomStatusManager.MakeRoomID(roomName, roomType)
s.mutex.Lock()
defer s.mutex.Unlock()
@ -134,7 +134,7 @@ func (s *Session) JoinRoom(roomID string, credentials *DataRoomCredentials, send @@ -134,7 +134,7 @@ func (s *Session) JoinRoom(roomID string, credentials *DataRoomCredentials, send
})
}
room, err := s.RoomStatusManager.JoinRoom(roomID, credentials, s, s.authenticated(), sender)
room, err := s.RoomStatusManager.JoinRoom(roomID, roomName, roomType, credentials, s, s.authenticated(), sender)
if err == nil {
s.Hello = true
s.Roomid = roomID
@ -310,15 +310,18 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) { @@ -310,15 +310,18 @@ func (s *Session) Authorize(realm string, st *SessionToken) (string, error) {
defer s.mutex.Unlock()
if s.Id != st.Id || s.Sid != st.Sid {
return "", errors.New("session id mismatch")
return "", NewDataError("invalid_session_token", "session id mismatch")
}
if s.userid != "" {
return "", errors.New("session already authenticated")
return "", NewDataError("already_authenticated", "session already authenticated")
}
// Create authentication nonce.
var err error
s.Nonce, err = sessionNonces.Encode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Userid)
if err != nil {
err = NewDataError("unknown", err.Error())
}
return s.Nonce, err
@ -330,18 +333,18 @@ func (s *Session) Authenticate(realm string, st *SessionToken, userid string) er @@ -330,18 +333,18 @@ func (s *Session) Authenticate(realm string, st *SessionToken, userid string) er
defer s.mutex.Unlock()
if s.userid != "" {
return errors.New("session already authenticated")
return NewDataError("already_authenticated", "session already authenticated")
}
if userid == "" {
if s.Nonce == "" || s.Nonce != st.Nonce {
return errors.New("nonce validation failed")
return NewDataError("invalid_session_token", "nonce validation failed")
}
err := sessionNonces.Decode(fmt.Sprintf("%s@%s", s.Sid, realm), st.Nonce, &userid)
if err != nil {
return err
return NewDataError("invalid_session_token", err.Error())
}
if st.Userid != userid {
return errors.New("user id mismatch")
return NewDataError("invalid_session_token", "user id mismatch")
}
s.Nonce = ""
}

2
src/app/spreed-webrtc-server/session_manager.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/sessions.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

234
src/app/spreed-webrtc-server/sleepy/core.go

@ -1,234 +0,0 @@ @@ -1,234 +0,0 @@
/**
* A RESTful framework for Go
*
* Modified version of sleepy to support Gorilla muxers.
* https://github.com/strukturag/sleepy
*
* Copyright (c) 2014 struktur AG
* Copyright (c) 2013-2014 Doug Black and the Sleepy authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
**/
package sleepy
import (
"encoding/json"
"errors"
"fmt"
"github.com/gorilla/mux"
"net/http"
)
const (
GET = "GET"
POST = "POST"
PUT = "PUT"
DELETE = "DELETE"
HEAD = "HEAD"
PATCH = "PATCH"
)
// GetSupported is the interface that provides the Get
// method a resource must support to receive HTTP GETs.
type GetSupported interface {
Get(*http.Request) (int, interface{}, http.Header)
}
// PostSupported is the interface that provides the Post
// method a resource must support to receive HTTP POSTs.
type PostSupported interface {
Post(*http.Request) (int, interface{}, http.Header)
}
// PutSupported is the interface that provides the Put
// method a resource must support to receive HTTP PUTs.
type PutSupported interface {
Put(*http.Request) (int, interface{}, http.Header)
}
// DeleteSupported is the interface that provides the Delete
// method a resource must support to receive HTTP DELETEs.
type DeleteSupported interface {
Delete(*http.Request) (int, interface{}, http.Header)
}
// HeadSupported is the interface that provides the Head
// method a resource must support to receive HTTP HEADs.
type HeadSupported interface {
Head(*http.Request) (int, interface{}, http.Header)
}
// PatchSupported is the interface that provides the Patch
// method a resource must support to receive HTTP PATCHs.
type PatchSupported interface {
Patch(*http.Request) (int, interface{}, http.Header)
}
// Interface for arbitrary muxer support (like http.ServeMux).
type APIMux interface {
HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) *mux.Route
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
// An API manages a group of resources by routing requests
// to the correct method on a matching resource and marshalling
// the returned data to JSON for the HTTP response.
//
// You can instantiate multiple APIs on separate ports. Each API
// will manage its own set of resources.
type API struct {
mux APIMux
muxInitialized bool
}
// NewAPI allocates and returns a new API.
func NewAPI() *API {
return &API{}
}
func (api *API) requestHandler(resource interface{}) http.HandlerFunc {
return func(rw http.ResponseWriter, request *http.Request) {
if request.ParseForm() != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
var handler func(*http.Request) (int, interface{}, http.Header)
switch request.Method {
case GET:
if resource, ok := resource.(GetSupported); ok {
handler = resource.Get
}
case POST:
if resource, ok := resource.(PostSupported); ok {
handler = resource.Post
}
case PUT:
if resource, ok := resource.(PutSupported); ok {
handler = resource.Put
}
case DELETE:
if resource, ok := resource.(DeleteSupported); ok {
handler = resource.Delete
}
case HEAD:
if resource, ok := resource.(HeadSupported); ok {
handler = resource.Head
}
case PATCH:
if resource, ok := resource.(PatchSupported); ok {
handler = resource.Patch
}
}
if handler == nil {
rw.WriteHeader(http.StatusMethodNotAllowed)
return
}
code, data, header := handler(request)
var content []byte
var err error
switch data.(type) {
case string:
content = []byte(data.(string))
case []byte:
content = data.([]byte)
default:
// Encode JSON.
content, err = json.MarshalIndent(data, "", " ")
if err != nil {
if header == nil {
header = http.Header{"Content-Type": {"application/json"}}
} else if header.Get("Content-Type") == "" {
header.Set("Content-Type", "application/json")
}
}
}
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
return
}
for name, values := range header {
for _, value := range values {
rw.Header().Add(name, value)
}
}
rw.WriteHeader(code)
rw.Write(content)
}
}
// Mux returns the muxer used by an API. If a ServeMux does not
// yet exist, a new *http.ServeMux will be created and returned.
func (api *API) Mux() APIMux {
if api.muxInitialized {
return api.mux
} else {
api.mux = mux.NewRouter()
api.muxInitialized = true
return api.mux
}
}
// SetMux sets the muxer to use by an API. A muxer needs to
// implement the APIMux interface (eg. http.ServeMux).
func (api *API) SetMux(mux APIMux) error {
if api.muxInitialized {
return errors.New("You cannot set a muxer when already initialized.")
} else {
api.mux = mux
api.muxInitialized = true
return nil
}
}
// AddResource adds a new resource to an API. The API will route
// requests that match one of the given paths to the matching HTTP
// method on the resource.
func (api *API) AddResource(resource interface{}, paths ...string) {
for _, path := range paths {
api.Mux().HandleFunc(path, api.requestHandler(resource))
}
}
// AddResourceWithWrapper behaves exactly like AddResource but wraps
// the generated handler function with a give wrapper function to allow
// to hook in Gzip support and similar.
func (api *API) AddResourceWithWrapper(resource interface{}, wrapper func(handler http.HandlerFunc) http.HandlerFunc, paths ...string) {
for _, path := range paths {
api.Mux().HandleFunc(path, wrapper(api.requestHandler(resource)))
}
}
// Start causes the API to begin serving requests on the given port.
func (api *API) Start(port int) error {
if !api.muxInitialized {
return errors.New("You must add at least one resource to this API.")
}
portString := fmt.Sprintf(":%d", port)
return http.ListenAndServe(portString, api.Mux())
}

2
src/app/spreed-webrtc-server/stats.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/stats_manager.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/tickets.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/app/spreed-webrtc-server/tls.go

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
/*
* TLS helpers for Go based on crypto/tls package.
*
* Copyright (C) 2014 struktur AG. All rights reserved.
* Copyright (C) 2015 struktur AG. All rights reserved.
* Copyright 2011 The Go Authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

2
src/app/spreed-webrtc-server/tokenprovider.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

5
src/app/spreed-webrtc-server/tokens.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -49,9 +49,8 @@ func (tokens Tokens) Post(request *http.Request) (int, interface{}, http.Header) @@ -49,9 +49,8 @@ func (tokens Tokens) Post(request *http.Request) (int, interface{}, http.Header)
if valid != "" {
log.Printf("Good incoming token request: %s\n", auth)
return 200, &Token{Token: valid, Success: true}, http.Header{"Content-Type": {"application/json"}}
} else {
}
log.Printf("Wrong incoming token request: %s\n", auth)
return 403, NewApiError("invalid_token", "Invalid token"), http.Header{"Content-Type": {"application/json"}}
}
}

8
src/app/spreed-webrtc-server/user.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -43,7 +43,8 @@ func NewUser(id string) *User { @@ -43,7 +43,8 @@ func NewUser(id string) *User {
}
// Return true if first session.
// AddSession adds a session to the session table and returns true if
// s is the first session.
func (u *User) AddSession(s *Session) bool {
first := false
u.mutex.Lock()
@ -56,7 +57,8 @@ func (u *User) AddSession(s *Session) bool { @@ -56,7 +57,8 @@ func (u *User) AddSession(s *Session) bool {
return first
}
// Return true if no session left.
// RemoveSession removes a session from the session table abd returns
// true if no session is left left.
func (u *User) RemoveSession(sessionID string) bool {
last := false
u.mutex.Lock()

13
src/app/spreed-webrtc-server/users.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -46,7 +46,7 @@ import ( @@ -46,7 +46,7 @@ import (
)
var (
serialNumberLimit *big.Int = new(big.Int).Lsh(big.NewInt(1), 128)
serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128)
)
type UsersHandler interface {
@ -229,12 +229,12 @@ func (uh *UsersCertificateHandler) Create(un *UserNonce, request *http.Request) @@ -229,12 +229,12 @@ func (uh *UsersCertificateHandler) Create(un *UserNonce, request *http.Request)
}
spkacDerBytes, err := base64.StdEncoding.DecodeString(spkac)
if err != nil {
return nil, errors.New(fmt.Sprintf("spkac invalid: %s", err))
return nil, fmt.Errorf("spkac invalid: %s", err)
}
publicKey, err := pkac.ParseSPKAC(spkacDerBytes)
if err != nil {
return nil, errors.New(fmt.Sprintf("unable to parse spkac: %s", err))
return nil, fmt.Errorf("unable to parse spkac: %s", err)
}
template, err := uh.makeTemplate(un.Userid)
@ -244,7 +244,7 @@ func (uh *UsersCertificateHandler) Create(un *UserNonce, request *http.Request) @@ -244,7 +244,7 @@ func (uh *UsersCertificateHandler) Create(un *UserNonce, request *http.Request)
certDerBytes, err := x509.CreateCertificate(rand.Reader, template, uh.certificate, publicKey, uh.privateKey)
if err != nil {
return nil, errors.New(fmt.Sprintf("failed to create certificate: %s", err))
return nil, fmt.Errorf("failed to create certificate: %s", err)
}
log.Println("Generated new certificate", un.Userid)
@ -284,11 +284,10 @@ func (un *UserNonce) Response() (int, interface{}, http.Header) { @@ -284,11 +284,10 @@ func (un *UserNonce) Response() (int, interface{}, http.Header) {
if un.contentType != "" {
header.Set("Content-Type", un.contentType)
return 200, un.raw, header
} else {
}
header.Set("Content-Type", "application/json")
return 200, un, header
}
}
type Users struct {
SessionValidator

2
src/app/spreed-webrtc-server/ws.go

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/i18n/helpers/po2json

@ -7,7 +7,7 @@ var po2json = require('po2json'), @@ -7,7 +7,7 @@ var po2json = require('po2json'),
assert.equal(argv.length, 4, 'Usage: po2json <input_file.po> <output_file.json>');
var result = po2json.parseFileSync(argv[2], { stringify: true, format: 'jed', pretty: false }),
var result = po2json.parseFileSync(argv[2], { stringify: true, format: 'jed1.x', pretty: false }),
stream = fs.createWriteStream(argv[3], {});
stream.write(result);

91
src/i18n/messages-de.po

@ -8,8 +8,8 @@ msgid "" @@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Spreed WebRTC 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2015-02-18 14:46+0100\n"
"PO-Revision-Date: 2015-02-18 14:49+0100\n"
"POT-Creation-Date: 2015-04-30 19:22+0200\n"
"PO-Revision-Date: 2015-04-30 19:27+0100\n"
"Last-Translator: Simon Eisenmann <simon@struktur.de>\n"
"Language-Team: struktur AG <opensource@struktur.de>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
@ -18,9 +18,6 @@ msgstr "" @@ -18,9 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
msgid "Your audio level"
msgstr "Ihr Audio-Pegel"
msgid "Standard view"
msgstr "Standardansicht"
@ -246,6 +243,42 @@ msgstr "Optionen für Bildschirmfreigabe" @@ -246,6 +243,42 @@ msgstr "Optionen für Bildschirmfreigabe"
msgid "Fit screen."
msgstr "Bildschirm einpassen."
msgid "Share screen"
msgstr "Bildschirm teilen"
msgid "Please select what to share."
msgstr "Bitte wählen Sie aus, was geteilt werden soll."
msgid "Screen"
msgstr "Bildschirm"
msgid "Window"
msgstr "Fenster"
msgid "Application"
msgstr "Anwendung"
msgid "Share the whole screen. Click share to select the screen."
msgstr ""
"Gesamten Bildschirm teilen. Klicken Sie auf Teilen um den Bildschirm "
"auszuwählen."
msgid "Share a single window. Click share to select the window."
msgstr ""
"Einzelnes Fenster teilen. Klicken Sie auf Teilen um das Fenster "
"auszuwählen."
msgid ""
"Share all windows of a application. This can leak content behind windows "
"when windows get moved. Click share to select the application."
msgstr ""
"Alle Fenster einer Anwendung teilen. Es wird u.U. Inhalt hinter Fenstern "
"der Anwendung geteilt, wenn diese verschoben werden. Klicken Sie auf "
"Teilen um die Anwendung auszuwählen."
msgid "Share"
msgstr "Teilen"
msgid "Profile"
msgstr "Profil"
@ -466,6 +499,9 @@ msgstr "Anruf annehmen" @@ -466,6 +499,9 @@ msgstr "Anruf annehmen"
msgid "Waiting for camera/microphone access"
msgstr "Warte auf Kamera/Mikrofon Freigabe"
msgid "Your audio level"
msgstr "Ihr Audio-Pegel"
msgid "Checking camera and microphone access."
msgstr "Prüfe Zugriff auf Kamera und Mikrofon."
@ -519,9 +555,6 @@ msgstr "Das Video wird bei allen Gesprächsteilnehmern angezeigt." @@ -519,9 +555,6 @@ msgstr "Das Video wird bei allen Gesprächsteilnehmern angezeigt."
msgid "YouTube URL"
msgstr "YouTube URL"
msgid "Share"
msgstr "Teilen"
msgid ""
"Could not load YouTube player API, please check your network / firewall "
"settings."
@ -711,6 +744,48 @@ msgstr "Meeting:" @@ -711,6 +744,48 @@ msgstr "Meeting:"
msgid "Room name"
msgstr "Raum-Name"
msgid ""
"The request contains an invalid parameter value. Please check the URL of "
"the video you want to share and try again."
msgstr ""
"Die Anfrage enthält falsche Parameter. Bitte prüfen Sie die URL des "
"Videos."
msgid ""
"The requested content cannot be played in an HTML5 player or another "
"error related to the HTML5 player has occurred. Please try again later."
msgstr ""
"Dieser Inhalt kann nicht im HTML5-Player abgespielt werden oder ein "
"anderer HTML5-Player-Fehler ist aufgetreten. Bitte versuchen Sie es "
"später wieder."
msgid ""
"The video requested was not found. Please check the URL of the video you "
"want to share and try again."
msgstr "Das Video wurde nicht gefunden. Bitte prüfen Sie die URL des Videos."
msgid ""
"The owner of the requested video does not allow it to be played in "
"embedded players."
msgstr ""
"Der Eigentümer des Videos hat das Video nicht für eingebettete Anzeige "
"freigegeben."
#, python-format
msgid ""
"An unknown error occurred while playing back the video (%s). Please try "
"again later."
msgstr ""
"Beim Abspielen des Videos ist ein unbekannter Fehler aufgetreten (%s). "
"Bitte versuchen Sie es später wieder."
msgid ""
"An unknown error occurred while playing back the video. Please try again "
"later."
msgstr ""
"Beim Abspielen des Videos ist ein unbekannter Fehler aufgetreten. Bitte "
"versuchen Sie es später wieder."
msgid "Unknown URL format. Please make sure to enter a valid YouTube URL."
msgstr "Unbekanntes URL-Format. Bitte geben Sie eine gültige YouTube URL ein."

75
src/i18n/messages-ja.po

@ -8,7 +8,7 @@ msgid "" @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Spreed WebRTC 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2015-02-18 14:46+0100\n"
"POT-Creation-Date: 2015-04-30 19:22+0200\n"
"PO-Revision-Date: 2014-04-23 22:25+0100\n"
"Last-Translator: Curt Frisemo <curt.frisemo@spreed.com>\n"
"Language-Team: Curt Frisemo <curt.frisemo@spreed.com>\n"
@ -18,9 +18,6 @@ msgstr "" @@ -18,9 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
msgid "Your audio level"
msgstr "あなたの音量"
msgid "Standard view"
msgstr ""
@ -246,6 +243,38 @@ msgstr "画面共有オプション" @@ -246,6 +243,38 @@ msgstr "画面共有オプション"
msgid "Fit screen."
msgstr "画面に合わせる"
#, fuzzy
msgid "Share screen"
msgstr "画面を共有する."
msgid "Please select what to share."
msgstr ""
#, fuzzy
msgid "Screen"
msgstr "画面に合わせる"
msgid "Window"
msgstr ""
msgid "Application"
msgstr ""
msgid "Share the whole screen. Click share to select the screen."
msgstr ""
msgid "Share a single window. Click share to select the window."
msgstr ""
msgid ""
"Share all windows of a application. This can leak content behind windows "
"when windows get moved. Click share to select the application."
msgstr ""
#, fuzzy
msgid "Share"
msgstr "共有取り消し"
msgid "Profile"
msgstr ""
@ -460,6 +489,9 @@ msgstr "通話" @@ -460,6 +489,9 @@ msgstr "通話"
msgid "Waiting for camera/microphone access"
msgstr "カメラ・マイクの接続待ち."
msgid "Your audio level"
msgstr "あなたの音量"
msgid "Checking camera and microphone access."
msgstr "カメラ・マイクの接続確認中."
@ -510,10 +542,6 @@ msgstr "" @@ -510,10 +542,6 @@ msgstr ""
msgid "YouTube URL"
msgstr ""
#, fuzzy
msgid "Share"
msgstr "共有取り消し"
msgid ""
"Could not load YouTube player API, please check your network / firewall "
"settings."
@ -694,6 +722,37 @@ msgstr "ここで私と会う:" @@ -694,6 +722,37 @@ msgstr "ここで私と会う:"
msgid "Room name"
msgstr "あなたの名前"
msgid ""
"The request contains an invalid parameter value. Please check the URL of "
"the video you want to share and try again."
msgstr ""
msgid ""
"The requested content cannot be played in an HTML5 player or another "
"error related to the HTML5 player has occurred. Please try again later."
msgstr ""
msgid ""
"The video requested was not found. Please check the URL of the video you "
"want to share and try again."
msgstr ""
msgid ""
"The owner of the requested video does not allow it to be played in "
"embedded players."
msgstr ""
#, python-format
msgid ""
"An unknown error occurred while playing back the video (%s). Please try "
"again later."
msgstr ""
msgid ""
"An unknown error occurred while playing back the video. Please try again "
"later."
msgstr ""
msgid "Unknown URL format. Please make sure to enter a valid YouTube URL."
msgstr ""

75
src/i18n/messages-ko.po

@ -8,7 +8,7 @@ msgid "" @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Spreed WebRTC 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2015-02-18 14:46+0100\n"
"POT-Creation-Date: 2015-04-30 19:22+0200\n"
"PO-Revision-Date: 2014-04-13 20:30+0900\n"
"Last-Translator: Curt Frisemo <curt.frisemo@spreed.com>\n"
"Language-Team: Curt Frisemo <curt.frisemo@spreed.com>\n"
@ -18,9 +18,6 @@ msgstr "" @@ -18,9 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
msgid "Your audio level"
msgstr "음성크기"
msgid "Standard view"
msgstr ""
@ -246,6 +243,38 @@ msgstr "화면 공유 옵션" @@ -246,6 +243,38 @@ msgstr "화면 공유 옵션"
msgid "Fit screen."
msgstr "화면에 맟춤"
#, fuzzy
msgid "Share screen"
msgstr "화면 공유하기"
msgid "Please select what to share."
msgstr ""
#, fuzzy
msgid "Screen"
msgstr "화면에 맟춤"
msgid "Window"
msgstr ""
msgid "Application"
msgstr ""
msgid "Share the whole screen. Click share to select the screen."
msgstr ""
msgid "Share a single window. Click share to select the window."
msgstr ""
msgid ""
"Share all windows of a application. This can leak content behind windows "
"when windows get moved. Click share to select the application."
msgstr ""
#, fuzzy
msgid "Share"
msgstr "비공유"
msgid "Profile"
msgstr ""
@ -460,6 +489,9 @@ msgstr "전화 받음" @@ -460,6 +489,9 @@ msgstr "전화 받음"
msgid "Waiting for camera/microphone access"
msgstr "카메라/마이크 사용을 기다림"
msgid "Your audio level"
msgstr "음성크기"
msgid "Checking camera and microphone access."
msgstr "카메라와 마이크의 사용을 확인 하세요"
@ -510,10 +542,6 @@ msgstr "" @@ -510,10 +542,6 @@ msgstr ""
msgid "YouTube URL"
msgstr ""
#, fuzzy
msgid "Share"
msgstr "비공유"
msgid ""
"Could not load YouTube player API, please check your network / firewall "
"settings."
@ -694,6 +722,37 @@ msgstr "나를 여기서 만납니다:" @@ -694,6 +722,37 @@ msgstr "나를 여기서 만납니다:"
msgid "Room name"
msgstr "사용자 이름"
msgid ""
"The request contains an invalid parameter value. Please check the URL of "
"the video you want to share and try again."
msgstr ""
msgid ""
"The requested content cannot be played in an HTML5 player or another "
"error related to the HTML5 player has occurred. Please try again later."
msgstr ""
msgid ""
"The video requested was not found. Please check the URL of the video you "
"want to share and try again."
msgstr ""
msgid ""
"The owner of the requested video does not allow it to be played in "
"embedded players."
msgstr ""
#, python-format
msgid ""
"An unknown error occurred while playing back the video (%s). Please try "
"again later."
msgstr ""
msgid ""
"An unknown error occurred while playing back the video. Please try again "
"later."
msgstr ""
msgid "Unknown URL format. Please make sure to enter a valid YouTube URL."
msgstr ""

75
src/i18n/messages-zh-cn.po

@ -8,7 +8,7 @@ msgid "" @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Spreed WebRTC 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2015-02-18 14:46+0100\n"
"POT-Creation-Date: 2015-04-30 19:22+0200\n"
"PO-Revision-Date: 2014-05-21 09:54+0800\n"
"Last-Translator: Michael P.\n"
"Language-Team: Curt Frisemo <curt.frisemo@spreed.com>\n"
@ -18,9 +18,6 @@ msgstr "" @@ -18,9 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
msgid "Your audio level"
msgstr "您的通话音量"
msgid "Standard view"
msgstr ""
@ -246,6 +243,38 @@ msgstr "屏幕共享设置" @@ -246,6 +243,38 @@ msgstr "屏幕共享设置"
msgid "Fit screen."
msgstr "适合屏幕"
#, fuzzy
msgid "Share screen"
msgstr "共享您的屏幕"
msgid "Please select what to share."
msgstr ""
#, fuzzy
msgid "Screen"
msgstr "适合屏幕"
msgid "Window"
msgstr ""
msgid "Application"
msgstr ""
msgid "Share the whole screen. Click share to select the screen."
msgstr ""
msgid "Share a single window. Click share to select the window."
msgstr ""
msgid ""
"Share all windows of a application. This can leak content behind windows "
"when windows get moved. Click share to select the application."
msgstr ""
#, fuzzy
msgid "Share"
msgstr "停止分享"
msgid "Profile"
msgstr ""
@ -460,6 +489,9 @@ msgstr "接受通话" @@ -460,6 +489,9 @@ msgstr "接受通话"
msgid "Waiting for camera/microphone access"
msgstr "等待摄像头/麦克风连接"
msgid "Your audio level"
msgstr "您的通话音量"
msgid "Checking camera and microphone access."
msgstr "正在检查摄像头及麦克风连接"
@ -510,10 +542,6 @@ msgstr "" @@ -510,10 +542,6 @@ msgstr ""
msgid "YouTube URL"
msgstr ""
#, fuzzy
msgid "Share"
msgstr "停止分享"
msgid ""
"Could not load YouTube player API, please check your network / firewall "
"settings."
@ -693,6 +721,37 @@ msgstr "我们这里见:" @@ -693,6 +721,37 @@ msgstr "我们这里见:"
msgid "Room name"
msgstr "您的名字"
msgid ""
"The request contains an invalid parameter value. Please check the URL of "
"the video you want to share and try again."
msgstr ""
msgid ""
"The requested content cannot be played in an HTML5 player or another "
"error related to the HTML5 player has occurred. Please try again later."
msgstr ""
msgid ""
"The video requested was not found. Please check the URL of the video you "
"want to share and try again."
msgstr ""
msgid ""
"The owner of the requested video does not allow it to be played in "
"embedded players."
msgstr ""
#, python-format
msgid ""
"An unknown error occurred while playing back the video (%s). Please try "
"again later."
msgstr ""
msgid ""
"An unknown error occurred while playing back the video. Please try again "
"later."
msgstr ""
msgid "Unknown URL format. Please make sure to enter a valid YouTube URL."
msgstr ""

75
src/i18n/messages-zh-tw.po

@ -8,7 +8,7 @@ msgid "" @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Spreed WebRTC 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2015-02-18 14:46+0100\n"
"POT-Creation-Date: 2015-04-30 19:22+0200\n"
"PO-Revision-Date: 2014-05-21 09:55+0800\n"
"Last-Translator: Michael P.\n"
"Language-Team: Curt Frisemo <curt.frisemo@spreed.com>\n"
@ -18,9 +18,6 @@ msgstr "" @@ -18,9 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
msgid "Your audio level"
msgstr "您的通話音量"
msgid "Standard view"
msgstr ""
@ -246,6 +243,38 @@ msgstr "屏幕共享設置" @@ -246,6 +243,38 @@ msgstr "屏幕共享設置"
msgid "Fit screen."
msgstr "適合屏幕"
#, fuzzy
msgid "Share screen"
msgstr "共享您的屏幕"
msgid "Please select what to share."
msgstr ""
#, fuzzy
msgid "Screen"
msgstr "適合屏幕"
msgid "Window"
msgstr ""
msgid "Application"
msgstr ""
msgid "Share the whole screen. Click share to select the screen."
msgstr ""
msgid "Share a single window. Click share to select the window."
msgstr ""
msgid ""
"Share all windows of a application. This can leak content behind windows "
"when windows get moved. Click share to select the application."
msgstr ""
#, fuzzy
msgid "Share"
msgstr "停止分享"
msgid "Profile"
msgstr ""
@ -460,6 +489,9 @@ msgstr "接受通話" @@ -460,6 +489,9 @@ msgstr "接受通話"
msgid "Waiting for camera/microphone access"
msgstr "等待攝像頭/麥克風連接"
msgid "Your audio level"
msgstr "您的通話音量"
msgid "Checking camera and microphone access."
msgstr "正在檢查攝像頭及麥克風連接"
@ -510,10 +542,6 @@ msgstr "" @@ -510,10 +542,6 @@ msgstr ""
msgid "YouTube URL"
msgstr ""
#, fuzzy
msgid "Share"
msgstr "停止分享"
msgid ""
"Could not load YouTube player API, please check your network / firewall "
"settings."
@ -693,6 +721,37 @@ msgstr "我們這裡見:" @@ -693,6 +721,37 @@ msgstr "我們這裡見:"
msgid "Room name"
msgstr "您的名字"
msgid ""
"The request contains an invalid parameter value. Please check the URL of "
"the video you want to share and try again."
msgstr ""
msgid ""
"The requested content cannot be played in an HTML5 player or another "
"error related to the HTML5 player has occurred. Please try again later."
msgstr ""
msgid ""
"The video requested was not found. Please check the URL of the video you "
"want to share and try again."
msgstr ""
msgid ""
"The owner of the requested video does not allow it to be played in "
"embedded players."
msgstr ""
#, python-format
msgid ""
"An unknown error occurred while playing back the video (%s). Please try "
"again later."
msgstr ""
msgid ""
"An unknown error occurred while playing back the video. Please try again "
"later."
msgstr ""
msgid "Unknown URL format. Please make sure to enter a valid YouTube URL."
msgstr ""

71
src/i18n/messages.pot

@ -9,7 +9,7 @@ msgid "" @@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Spreed WebRTC 1.0\n"
"Report-Msgid-Bugs-To: simon@struktur.de\n"
"POT-Creation-Date: 2015-02-18 14:46+0100\n"
"POT-Creation-Date: 2015-04-30 19:22+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,9 +18,6 @@ msgstr "" @@ -18,9 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.6\n"
msgid "Your audio level"
msgstr ""
msgid "Standard view"
msgstr ""
@ -242,6 +239,35 @@ msgstr "" @@ -242,6 +239,35 @@ msgstr ""
msgid "Fit screen."
msgstr ""
msgid "Share screen"
msgstr ""
msgid "Please select what to share."
msgstr ""
msgid "Screen"
msgstr ""
msgid "Window"
msgstr ""
msgid "Application"
msgstr ""
msgid "Share the whole screen. Click share to select the screen."
msgstr ""
msgid "Share a single window. Click share to select the window."
msgstr ""
msgid ""
"Share all windows of a application. This can leak content behind windows "
"when windows get moved. Click share to select the application."
msgstr ""
msgid "Share"
msgstr ""
msgid "Profile"
msgstr ""
@ -454,6 +480,9 @@ msgstr "" @@ -454,6 +480,9 @@ msgstr ""
msgid "Waiting for camera/microphone access"
msgstr ""
msgid "Your audio level"
msgstr ""
msgid "Checking camera and microphone access."
msgstr ""
@ -503,9 +532,6 @@ msgstr "" @@ -503,9 +532,6 @@ msgstr ""
msgid "YouTube URL"
msgstr ""
msgid "Share"
msgstr ""
msgid ""
"Could not load YouTube player API, please check your network / firewall "
"settings."
@ -679,6 +705,37 @@ msgstr "" @@ -679,6 +705,37 @@ msgstr ""
msgid "Room name"
msgstr ""
msgid ""
"The request contains an invalid parameter value. Please check the URL of "
"the video you want to share and try again."
msgstr ""
msgid ""
"The requested content cannot be played in an HTML5 player or another "
"error related to the HTML5 player has occurred. Please try again later."
msgstr ""
msgid ""
"The video requested was not found. Please check the URL of the video you "
"want to share and try again."
msgstr ""
msgid ""
"The owner of the requested video does not allow it to be played in "
"embedded players."
msgstr ""
#, python-format
msgid ""
"An unknown error occurred while playing back the video (%s). Please try "
"again later."
msgstr ""
msgid ""
"An unknown error occurred while playing back the video. Please try again "
"later."
msgstr ""
msgid "Unknown URL format. Please make sure to enter a valid YouTube URL."
msgstr ""

16
src/styles/Makefile.am

@ -23,19 +23,29 @@ SASSFLAGS = --style=compressed --no-cache --sourcemap=none @@ -23,19 +23,29 @@ SASSFLAGS = --style=compressed --no-cache --sourcemap=none
AUTOPREFIXER_BROWSER_SUPPORT := "> 1%, last 2 versions, Firefox ESR, Opera 12.1"
STATIC = ../../static
styles:
pre:
@if [ "$(SASS)" = "" ]; then echo "Command 'sass' not found, required when building styles"; exit 1; fi
@if [ "$(AUTOPREFIXER)" = "" ]; then echo "Command 'autoprefixer' not found, required when building styles"; exit 1; fi
@if [ "$(NODEJS_SUPPORT_STYLES)" = "no" ]; then echo "Your version of node.js does not support building styles"; exit 1; fi
@if [ "$(SASS_SUPPORT_STYLES)" = "no" ]; then echo "Your version of sass does not support building styles"; exit 1; fi
$(MKDIR_P) $(STATIC)/css
styles: bootstrap.min.css font-awesome.min.css csp.min.css main.min.css
main.min.css: pre
@if [ "$(AUTOPREFIXER)" = "" ]; then echo "Command 'autoprefixer' not found, required when building main.css styles"; exit 1; fi
$(SASS) --compass --scss $(SASSFLAGS) \
$(CURDIR)/main.scss:$(STATIC)/css/main.min.css
$(AUTOPREFIXER) --browsers $(AUTOPREFIXER_BROWSER_SUPPORT) $(STATIC)/css/main.min.css
bootstrap.min.css: pre
$(SASS) --compass --scss $(SASSFLAGS) \
$(CURDIR)/bootstrap.scss:$(STATIC)/css/bootstrap.min.css
font-awesome.min.css: pre
$(SASS) --compass --scss $(SASSFLAGS) \
$(CURDIR)/font-awesome.scss:$(STATIC)/css/font-awesome.min.css
csp.min.css: pre
$(SASS) --compass --scss $(SASSFLAGS) \
$(CURDIR)/csp.scss:$(STATIC)/css/csp.min.css
@ -46,4 +56,4 @@ styleshint: @@ -46,4 +56,4 @@ styleshint:
styleslint:
@if [ "$(SCSS_LINT)" = "" ]; then echo "Command 'scss-lint' not found, required when linting styles"; exit 1; fi
$(SCSS_LINT) -c scss.yml
$(FIND) ./ -not -path "./libs/*" -name "*.scss" -print0 | xargs -0 -n1 $(SCSS_LINT) -c scss.yml

2
src/styles/_shame.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

4
src/styles/components/_audiolevel.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
#audiolevel {
#audiolevel { // scss-lint:disable IdSelector
left: 0;
margin: 0 auto;
position: fixed;

218
src/styles/components/_audiovideo.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,16 +19,36 @@ @@ -19,16 +19,36 @@
*
*/
.mainScreenshare,
.mainPresentation {
// scss-lint:disable IdSelector
#audiovideo {
@include breakpt($breakpoint-video-medium, max-width, only screen) {
display: none;
}
}
}
.withChat,
.withBuddylist {
// scss-lint:disable IdSelector
#audiovideo {
right: 260px;
}
}
.withBuddylist.withChat #audiovideo { // scss-lint:disable IdSelector
right: 520px;
}
#audiovideo { // scss-lint:disable IdSelector
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: $minbarheight;
user-select: none;
}
#audiovideo {
@include breakpt($breakpoint-video-small, max-width, only screen) {
right: 0;
}
@ -45,22 +65,6 @@ @@ -45,22 +65,6 @@
}
}
.mainScreenshare #audiovideo,
.mainPresentation #audiovideo {
@include breakpt($breakpoint-video-medium, max-width, only screen) {
display: none;
}
}
.withChat #audiovideo,
.withBuddylist #audiovideo {
right: 260px;
}
.withBuddylist.withChat #audiovideo {
right: 520px;
}
.audiovideo {
bottom: 0;
left: 0;
@ -109,9 +113,33 @@ @@ -109,9 +113,33 @@
video {
object-fit: cover;
}
.onlyaudio {
bottom: 0;
color: $video-onlyaudio;
display: none;
font-size: 1em;
left: 0;
pointer-events: auto;
position: absolute;
right: 0;
text-align: center;
top: 0;
&:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.audiovideo {
> * {
font-size: 6em;
vertical-align: middle;
}
}
.remoteContainer {
bottom: 0;
left: 0;
@ -142,6 +170,19 @@ @@ -142,6 +170,19 @@
}
}
&.cameraMute .miniContainer,
&.cameraMute .localVideos {
background: $video-onlyaudio-background;
.onlyaudio {
display: block;
}
video {
visibility: hidden;
}
}
.miniVideo {
display: block;
height: 100%;
@ -159,9 +200,18 @@ @@ -159,9 +200,18 @@
transition-property: opacity;
width: 100%;
}
.localVideos {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
transition-duration: 2s;
transition-property: opacity;
}
.audiovideo .remoteVideos {
.remoteVideos {
bottom: 0;
left: 0;
opacity: 0;
@ -178,7 +228,30 @@ @@ -178,7 +228,30 @@
}
}
.audiovideo .remoteVideo {
.overlayActions {
background: $video-overlayactions;
bottom: 0;
height: 140px;
left: 0;
margin: auto 0;
opacity: 0;
padding: 3px 0;
position: absolute;
top: 0;
width: 40px;
z-index: 5;
.btn {
color: #ccc;
cursor: pointer;
display: block;
outline: 0;
text-shadow: 0 0 5px #000;
width: 40px;
}
}
.remoteVideo {
background: $video-background;
display: inline-block;
max-height: 100%;
@ -193,38 +266,26 @@ @@ -193,38 +266,26 @@
//visibility: visible;
}
&.onlyaudio {
&.onlyaudioVideo {
background: $video-onlyaudio-background;
//visibility: visible;
}
.onlyaudio {
color: $video-onlyaudio;
display: none;
font-size: 80px;
left: 0;
margin-top: -40px;
pointer-events: auto;
position: absolute;
right: 0;
text-align: center;
top: 45%;
display: block;
}
}
&.onlyaudio video,
&.onlyaudioVideo video,
&.dummy video {
visibility: hidden;
}
&.onlyaudio .onlyaudio {
display: block;
}
&.dummy .onlyaudio {
display: block;
}
.peerActions {
// scss:lint:disable NestingDepth, SelectorDepth
bottom: 5%;
left: 40px;
opacity: 0;
@ -261,29 +322,6 @@ @@ -261,29 +322,6 @@
z-index: 8;
}
}
.audiovideo .overlayActions {
background: $video-overlayactions;
bottom: 0;
height: 140px;
left: 0;
margin: auto 0;
opacity: 0;
padding: 3px 0;
position: absolute;
top: 0;
width: 40px;
z-index: 5;
.btn {
color: #ccc;
cursor: pointer;
display: block;
outline: 0;
text-shadow: 0 0 5px #000;
width: 40px;
}
}
.remoteVideo {
@ -326,29 +364,8 @@ @@ -326,29 +364,8 @@
}
}
.renderer-auditorium {
position: relative;
span:before {
content: '\f183';
left: 50%;
margin-left: -.8em;
margin-top: -.5em;
position: absolute;
top: 50%;
}
span:after {
content: '\f183';
margin-right: -.9em;
margin-top: -.5em;
position: absolute;
right: 50%;
top: 50%;
}
}
.renderer-smally {
// scss-lint:disable SelectorDepth
background: #000;
border-right: 0;
border-top: 0;
@ -379,10 +396,12 @@ @@ -379,10 +396,12 @@
}
.renderer-onepeople {
.miniContainer .onlyaudio {
font-size: .4em;
}
}
.renderer-democrazy {
.remoteVideos .miniContainer {
bottom: auto;
display: inline-block;
@ -396,10 +415,10 @@ @@ -396,10 +415,10 @@
.active .miniContainer {
opacity: 1;
}
}
.renderer-conferencekiosk {
// scss-lint:disable SelectorDepth
.remoteVideos {
background: $video-background;
bottom: 2px;
@ -454,6 +473,26 @@ @@ -454,6 +473,26 @@
}
.renderer-auditorium {
position: relative;
span:before {
content: '\f183';
left: 50%;
margin-left: -.8em;
margin-top: -.5em;
position: absolute;
top: 50%;
}
span:after {
content: '\f183';
margin-right: -.9em;
margin-top: -.5em;
position: absolute;
right: 50%;
top: 50%;
}
.remoteContainer {
border-left: 40px solid #000;
}
@ -464,10 +503,6 @@ @@ -464,10 +503,6 @@
top: 180px;
width: 320px;
.overlayLogo {
display: none;
}
video {
height: 100%;
margin-top: -9px;
@ -511,7 +546,8 @@ @@ -511,7 +546,8 @@
height: 180px;
width: 320px;
video {
.remoteVideo,
.video {
height: 100%;
width: 100%;
}

14
src/styles/components/_bar.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
padding: 2px 5px 0 11px;
padding: 2px 5px 0 11px;
}
}
.logo {
background: $logo no-repeat;
@ -54,25 +55,25 @@ @@ -54,25 +55,25 @@
height: 46px;
width: 46px;
span {
.desc {
display: none;
}
}
span {
.desc {
font-style: italic;
left: 38px;
position: relative;
top: 26px;
}
span a {
a {
color: $bar-logo-text-desc;
}
}
}
.bar .middle {
// scss-lint:disable SelectorDepth
left: 0;
pointer-events: none;
position: absolute;
@ -178,6 +179,7 @@ @@ -178,6 +179,7 @@
color: #fff;
}
}
}
.btn-mutemicrophone i:before {
content: '\f130';
@ -195,8 +197,6 @@ @@ -195,8 +197,6 @@
content: '\f070';
}
}
@keyframes shakeityeah {
0% { transform: translate(2px, 1px) rotate(0deg); }
2% { transform: translate(-1px, -2px) rotate(-1deg); }

34
src/styles/components/_buddylist.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
#buddylist {
#buddylist { // scss-lint:disable IdSelector
bottom: 0;
position: absolute;
right: 0;
@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
z-index: 50;
}
#buddylist:before {
#buddylist:before { // scss-lint:disable IdSelector
background: $buddylist-tab-background;
border-bottom: 1px solid $bordercolor;
border-bottom-left-radius: 6px;
@ -55,12 +55,12 @@ @@ -55,12 +55,12 @@
z-index: 1;
}
.withBuddylist #buddylist:before {
.withBuddylist #buddylist:before { // scss-lint:disable IdSelector
content: '\f101';
padding-right: 0;
}
.withBuddylistAutoHide #buddylist:before {
.withBuddylistAutoHide #buddylist:before { // scss-lint:disable IdSelector
display: block;
}
@ -76,9 +76,7 @@ @@ -76,9 +76,7 @@
position: absolute;
right: 0;
top: 0;
}
.buddylist {
&.loading {
.buddylistloading {
display: block;
@ -139,13 +137,11 @@ @@ -139,13 +137,11 @@
position: relative;
text-align: left;
width: 100%;
}
.buddy:hover {
&:hover {
background: $buddylist-action-background;
}
.buddy {
&.withSubline .buddy1,
&.contact .buddy1 {
top: 15px;
@ -156,10 +152,17 @@ @@ -156,10 +152,17 @@
display: block;
}
&.hovered .buddyactions {
&.hovered {
.buddyactions {
right: 0;
}
.buddysessions {
max-height: 999px;
}
}
.fa.contact:before {
content: '\f006';
}
@ -249,9 +252,8 @@ @@ -249,9 +252,8 @@
white-space: nowrap;
width: 120px;
}
}
.buddy .buddyactions {
.buddyactions {
background: $buddylist-action-background;
height: 66px;
line-height: 66px;
@ -276,7 +278,7 @@ @@ -276,7 +278,7 @@
}
}
.buddy .buddysessions {
.buddysessions {
margin-bottom: 10px;
margin-top: 56px;
max-height: 0;
@ -293,6 +295,7 @@ @@ -293,6 +295,7 @@
}
ul li {
// scss-lint:disable NestingDepth
list-style-type: none;
margin-bottom: 2px;
margin-left: 0;
@ -310,7 +313,4 @@ @@ -310,7 +313,4 @@
font-weight: bold;
}
}
.buddy.hovered .buddysessions {
max-height: 999px;
}

6
src/styles/components/_buddypicturecapture.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -61,11 +61,11 @@ @@ -61,11 +61,11 @@
top: 0;
visibility: hidden;
z-index: 5;
}
.videoFlash.flash {
&.flash {
visibility: visible;
}
}
.preview {
left: 0;

15
src/styles/components/_buddypictureupload.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
}
.showUploadPicture {
// scss-lint:disable NestingDepth
background-color: $componentbg;
border: 1px solid $bordercolor;
height: 200px;
@ -52,12 +53,16 @@ @@ -52,12 +53,16 @@
&.imgData {
background-color: #000;
}
&.imgData .chooseUploadPicture {
.chooseUploadPicture {
display: none;
}
&:hover .imageUtilites {
visibility: visible;
}
}
.chooseUploadPicture {
color: $componentfg4;
left: 0;
@ -95,10 +100,6 @@ @@ -95,10 +100,6 @@
}
}
.showUploadPicture.imgData:hover .imageUtilites {
visibility: visible;
}
.moveHorizontal {
position: relative;
top: -4px;

358
src/styles/components/_chat.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
#chat {
#chat { // scss-lint:disable IdSelector
bottom: 0;
min-width: $chat-width;
opacity: 0;
@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
}
.withChat {
// scss-lint:disable IdSelector
#chat {
opacity: 1;
}
@ -135,6 +136,7 @@ @@ -135,6 +136,7 @@
}
.chat {
// scss-lint:disable NestingDepth
background: $chat-background;
bottom: 0;
display: none;
@ -143,109 +145,106 @@ @@ -143,109 +145,106 @@
position: absolute;
right: 0;
top: 0;
&.newmessage {
.chatheadertitle:after {
content: '***';
position: absolute;
right: 32px;
top: 2px;
}
.chat {
&.active {
&.visible {
display: block;
.chatheader {
animation: newmessage 1s ease -.3s infinite;
}
}
.chatbody {
// nothing
&.active.visible {
display: block;
}
.chatheader {
// nothing
&.with_pictures .message {
&.is_self {
padding-right: 54px;
.timestamp {
right: 58px;
}
}
&.is_remote {
padding-left: 58px;
}
}
.chatmenu {
height: 36px;
.chatbodybottom {
background: $chat-bottom-background;
bottom: 1px;
left: 0;
padding: 2px 4px;
margin: 0 auto;
position: absolute;
right: 0;
top: 36px;
@include breakpt($breakpoint-chat-small, max-height) {
display: none;
height: auto;
}
}
.chatbody {
border-left: 1px solid $bordercolor;
bottom: -1px;
left: 0;
position: absolute;
right: 0;
top: 74px;
.typinghint {
color: $chat-typing;
font-size: .8em;
height: 14px;
overflow: hidden;
padding: 0 6px;
white-space: nowrap;
@include breakpt($breakpoint-chat-small, max-height) {
border-top: 1px solid $bordercolor;
top: 0;
top: 0;
display: none;
}
}
.chatheader {
background: $chat-header;
border-bottom: 1px solid $bordercolor;
border-left: 1px solid $bordercolor;
height: 36px;
left: 0;
line-height: 34px;
padding: 0 4px 0 8px;
position: absolute;
right: 0;
top: 0;
.inputbox {
position: relative;
@include breakpt($breakpoint-chat-small, max-height) {
display: none;
height: auto;
}
.chatstatusicon {
cursor: pointer;
display: block;
font-size: 1.4em;
height: 36px;
left: 0;
.btn {
display: none;
padding: .5em 1em;
position: absolute;
text-align: center;
top: 0;
width: 36px;
right: 6px;
top: 1px;
}
.chatheadertitle {
display: inline;
padding-left: 28px;
> div {
border-top: 1px solid $bordercolor;
}
}
.ctrl {
color: $chat-ctrl;
position: absolute;
right: 1px;
top: 0;
.input {
border-color: transparent;
border-radius: 0;
box-shadow: none;
display: block;
height: 54px;
margin: 0;
max-height: 54px; /* FF hack */
resize: none;
width: 100%;
.#{$fa-css-prefix} {
cursor: pointer;
padding: 6px;
}
@include breakpt($breakpoint-chat-small, max-height) {
max-height: 2.5em;
}
span {
display: inline-block;
max-width: 60%;
overflow: hidden;
pointer-events: none;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
&:active,
&:focus {
border-color: $chat-input-border-color;
}
}
.chat .outputbox {
.outputbox {
bottom: 75px;
left: 0;
position: absolute;
@ -257,7 +256,7 @@ @@ -257,7 +256,7 @@
}
}
.chat .output {
.output {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
@ -278,21 +277,8 @@ @@ -278,21 +277,8 @@
}
}
.chat.with_pictures .message {
&.is_self {
padding-right: 54px;
.timestamp {
right: 58px;
}
}
&.is_remote {
padding-left: 58px;
}
}
.chat .message {
.message {
// scss-lint:disable DuplicateProperty
background: $chat-msg-background;
border: 1px solid $chat-msg-border;
border-radius: 6px;
@ -310,23 +296,6 @@ @@ -310,23 +296,6 @@
padding-left: 0;
}
li {
line-height: 1.1em;
margin: 4px 0;
padding-left: 1.2em;
position: relative;
&:before {
color: $chat-msg-default-icon-color;
content: '\f075';
font-family: FontAwesome;
left: 0;
position: absolute;
text-align: center;
width: 12px;
}
}
.timestamp {
color: $chat-timestamp;
font-size: .8em;
@ -350,15 +319,23 @@ @@ -350,15 +319,23 @@
text-overflow: ellipsis;
white-space: nowrap;
}
}
.chat .message {
&.is_self li:before {
li {
line-height: 1.1em;
margin: 4px 0;
padding-left: 1.2em;
position: relative;
&:before {
color: $chat-msg-default-icon-color;
transform: scale(-1, 1);
content: '\f075';
font-family: FontAwesome;
left: 0;
position: absolute;
text-align: center;
width: 12px;
}
li {
&.unread:before {
color: $chat-msg-unread-icon-color;
content: $chat-msg-unread-icon;
@ -389,13 +366,6 @@ @@ -389,13 +366,6 @@
content: $chat-msg-read-icon;
}
}
}
.chat .message {
&.is_self .buddyPicture {
left: auto;
right: 4px;
}
.buddyPicture {
background: $actioncolor1;
@ -425,10 +395,7 @@ @@ -425,10 +395,7 @@
top: 0;
}
}
}
.chat .message {
// scss-lint:disable DuplicateProperty
&:before,
&:after {
border-style: solid;
@ -479,79 +446,147 @@ @@ -479,79 +446,147 @@
bottom: auto;
right: -11px;
}
li:before {
color: $chat-msg-default-icon-color;
transform: scale(-1, 1);
}
.buddyPicture {
left: auto;
right: 4px;
}
}
&.with_name {
// none
}
&.with_hoverimage {
.buddyPicture {
overflow: visible;
z-index: initial;
&:hover .buddyInfoActions {
height: 40px;
opacity: 1;
}
}
.chat {
.chatbodybottom {
background: $chat-bottom-background;
bottom: 1px;
.buddyInfoActions {
cursor: default;
display: inline-block;
height: 0;
left: 0;
opacity: 0;
overflow: hidden;
position: absolute;
top: 48px;
transition: opacity 0.1s .1s linear, height .4s .1s ease-out;
white-space: nowrap;
z-index: 1;
.btn-group {
display: block;
margin: 0 auto;
width: 55px;
}
.btn-primary {
padding: 2px 5px;
}
.fa {
color: #fff;
line-height: 24px;
}
}
}
}
}
.chatmenu {
height: 36px;
left: 0;
padding: 2px 4px;
position: absolute;
right: 0;
top: 36px;
@include breakpt($breakpoint-chat-small, max-height) {
height: auto;
display: none;
}
}
.typinghint {
color: $chat-typing;
font-size: .8em;
height: 14px;
overflow: hidden;
padding: 0 6px;
white-space: nowrap;
.chatbody {
border-left: 1px solid $bordercolor;
bottom: -1px;
left: 0;
position: absolute;
right: 0;
top: 74px;
@include breakpt($breakpoint-chat-small, max-height) {
display: none;
border-top: 1px solid $bordercolor;
top: 0;
top: 0;
}
}
.inputbox {
position: relative;
.chatheader {
background: $chat-header;
border-bottom: 1px solid $bordercolor;
border-left: 1px solid $bordercolor;
height: 36px;
left: 0;
line-height: 34px;
padding: 0 4px 0 8px;
position: absolute;
right: 0;
top: 0;
@include breakpt($breakpoint-chat-small, max-height) {
height: auto;
display: none;
}
.btn {
display: none;
padding: .5em 1em;
.chatstatusicon {
cursor: pointer;
display: block;
font-size: 1.4em;
height: 36px;
left: 0;
position: absolute;
right: 6px;
top: 1px;
text-align: center;
top: 0;
width: 36px;
}
> div {
border-top: 1px solid $bordercolor;
}
.chatheadertitle {
display: inline;
padding-left: 28px;
}
.input {
border-color: transparent;
border-radius: 0;
box-shadow: none;
display: block;
height: 54px;
margin: 0;
max-height: 54px; /* FF hack */
resize: none;
width: 100%;
.ctrl {
color: $chat-ctrl;
position: absolute;
right: 1px;
top: 0;
@include breakpt($breakpoint-chat-small, max-height) {
max-height: 2.5em;
.#{$fa-css-prefix} {
cursor: pointer;
padding: 6px;
}
&:active,
&:focus {
border-color: $chat-input-border-color;
}
span {
display: inline-block;
max-width: 60%;
overflow: hidden;
pointer-events: none;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
}
@ -560,16 +595,3 @@ @@ -560,16 +595,3 @@
50% {background-color: $componentbg;}
100% {background-color: $actioncolor1;}
}
.chat.newmessage {
.chatheadertitle:after {
content: '***';
position: absolute;
right: 32px;
top: 2px;
}
.chatheader {
animation: newmessage 1s ease -.3s infinite;
}
}

4
src/styles/components/_contactsmanager.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -68,6 +68,7 @@ @@ -68,6 +68,7 @@
.table {
margin-bottom: 0;
}
tr:first-child td {
border-top: 0;
@ -85,7 +86,6 @@ @@ -85,7 +86,6 @@
vertical-align: middle;
}
}
}
.contactsmanageredit {
.buddy .buddyPicture {

112
src/styles/components/_fileinfo.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -27,9 +27,23 @@ @@ -27,9 +27,23 @@
padding: 1em;
position: relative;
text-align: center;
&.downloader {
.anim {
margin-left: -40px;
}
.file-info-size {
margin-bottom: 10px;
}
}
&.downloading {
.file-info-size {
border-color: $fileinfo-downloading-size-border;
}
}
.file-info {
> div {
position: relative;
z-index: 3;
@ -37,17 +51,14 @@ @@ -37,17 +51,14 @@
.file-info-bg {
bottom: 0;
color: $fileinfo-icon-background-color;
font-size: 20em;
left: 41px;
overflow: hidden;
position: absolute;
right: 0;
top: -17px;
top: -82px;
z-index: 2;
.#{$fa-css-prefix} {
color: $fileinfo-icon-background-color;
font-size: 20em;
}
}
.actions {
@ -57,6 +68,37 @@ @@ -57,6 +68,37 @@
text-align: left;
top: 14px;
}
.uploader {
// scss-lint:disable NestingDepth
.file-info-speed {
bottom: 6px;
}
.actions {
margin-left: 30px;
opacity: 0;
}
.anim {
margin-left: 0;
}
.hovercontrol {
&:hover .anim {
margin-left: -50px;
}
&:hover .actions {
margin-left: 0;
opacity: 1;
}
> div {
transition: all .2s ease-in-out;
}
}
}
}
.is_remote .file-info {
@ -64,12 +106,10 @@ @@ -64,12 +106,10 @@
border: 1px solid $fileinfo-border-remote;
.file-info-bg {
.#{$fa-css-prefix} {
color: $fileinfo-icon-background-color-remote;
font-size: 20em;
}
}
}
.file-info-name {
font-size: 1.1em;
@ -96,6 +136,7 @@ @@ -96,6 +136,7 @@
}
> div {
//scss-lint:disable NestingDepth
bottom: 0;
box-shadow: none !important;
left: 0;
@ -106,13 +147,14 @@ @@ -106,13 +147,14 @@
&.progress-bar {
opacity: .5;
}
&.progress-bar.download {
&.download {
opacity: 1;
z-index: 1;
}
}
}
}
.file-info-speed {
@ -123,49 +165,3 @@ @@ -123,49 +165,3 @@
right: 0;
text-align: center;
}
.file-info.uploader {
.file-info-speed {
bottom: 6px;
}
.actions {
margin-left: 30px;
opacity: 0;
}
.anim {
margin-left: 0;
}
.hovercontrol {
&:hover .anim {
margin-left: -50px;
}
&:hover .actions {
margin-left: 0;
opacity: 1;
}
> div {
transition: all .2s ease-in-out;
}
}
}
.file-info.downloader {
.anim {
margin-left: -40px;
}
.file-info-size {
margin-bottom: 10px;
}
}
.file-info.downloading {
.file-info-size {
border-color: $fileinfo-downloading-size-border;
}
}

204
src/styles/components/_presentation.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,126 +19,52 @@ @@ -19,126 +19,52 @@
*
*/
.presentation {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.presentationpane .welcome {
padding: 0;
}
.presentationpane .welcome h1 {
white-space: normal;
}
.presentationpane .welcome button {
margin-top: 30px;
}
.presentationpane .welcome .progress span {
text-shadow: none;
}
.presentationpane .welcome .progress .download-info {
color: $text-color;
left: 0;
position: absolute;
text-shadow: 1px 1px 1px #fff;
width: 100%;
}
.mainPresentation #presentation {
.mainPresentation #presentation { // scss-lint:disable IdSelector
display: block;
}
.presentationpane {
.presentation {
bottom: 0;
left: 0;
overflow: auto;
position: absolute;
right: 0;
top: 0;
}
.presentationpane {
.canvasContainer {
height: 100%;
width: 100%;
}
canvas {
display: block;
margin: 0 auto;
position: relative;
}
.odfcanvas {
cursor: default;
user-select: none;
body {
background-color: transparent;
}
document {
display: block;
}
}
.odfcontainer {
display: none;
margin: 0;
padding: 0;
}
.odfcontainer.showonepage {
overflow: hidden;
text-align: center;
}
}
.presentation .overlaybar {
.overlaybar {
bottom: 0;
left: 0;
right: 0;
text-align: center;
}
.presentation .overlaybar .overlaybar-content {
.overlaybar-content {
// scss-lint:disable NestingDepth
max-width: 100%;
}
.presentation .overlaybar .overlaybar-button {
font-size: 20px;
line-height: 28px;
padding: 4px 6px;
position: absolute;
top: 0;
}
.overlaybar-content .pagecontrol {
.pagecontrol {
height: 30px;
}
}
.presentation .overlaybar .btn-prev {
.btn-prev {
left: 40px;
}
.presentation .overlaybar .btn-next {
.btn-next {
left: auto;
right: 0;
}
.pageinfo input {
display: inline;
width: 70px;
.overlaybar-button {
font-size: 20px;
line-height: 28px;
padding: 4px 6px;
position: absolute;
top: 0;
}
}
.presentation .thumbnail {
.thumbnail {
color: #333;
display: inline-block;
height: 122px;
@ -148,31 +74,38 @@ @@ -148,31 +74,38 @@
text-shadow: none;
vertical-align: middle;
width: 160px;
&:first-child {
margin-left: 0;
}
.presentation .thumbnail.presentable {
&.presentable {
cursor: pointer;
}
.presentation .thumbnail:first-child {
margin-left: 0;
&:hover .presentation-action {
display: block;
}
&:hover .notavailable {
display: block;
}
.presentation .thumbnail .caption {
.caption {
// scss-lint:disable NestingDepth
overflow: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
}
.presentation .thumbnail .caption .size {
.size {
font-size: 10px;
}
.presentation .thumbnail .caption .progress {
.progress {
position: relative;
}
.presentation .thumbnail .caption .download-info {
.download-info {
bottom: 0;
color: $text-color;
left: 0;
@ -182,8 +115,9 @@ @@ -182,8 +115,9 @@
text-shadow: 1px 1px 1px #fff;
top: 0;
}
}
.presentation .thumbnail .active {
.active {
bottom: 0;
color: #84b819;
font-size: 10em;
@ -195,7 +129,7 @@ @@ -195,7 +129,7 @@
top: 0;
}
.presentation .thumbnail .notavailable {
.notavailable {
bottom: 0;
color: #d2322d;
display: none;
@ -208,30 +142,74 @@ @@ -208,30 +142,74 @@
top: 0;
}
.presentation .thumbnail:hover .notavailable {
display: block;
}
.presentation .thumbnail .presentation-action {
.presentation-action {
display: none;
position: absolute;
top: 1px;
}
.presentation .thumbnail .download {
.download {
left: 1px;
}
.presentation .thumbnail .delete {
.delete {
right: 1px;
}
.presentation .thumbnail:hover .presentation-action {
display: block;
.filetype {
font-size: 5em;
}
}
}
.presentation .thumbnail .filetype {
font-size: 5em;
.presentationpane {
bottom: 0;
left: 0;
overflow: auto;
position: absolute;
right: 0;
top: 0;
.welcome {
padding: 0;
h1 {
white-space: normal;
}
.btn {
margin-top: 30px;
}
.progress span {
text-shadow: none;
}
.progress .download-info {
color: $text-color;
left: 0;
position: absolute;
text-shadow: 1px 1px 1px #fff;
width: 100%;
}
}
.canvasContainer {
height: 100%;
width: 100%;
overflow: hidden;
iframe {
border: 0;
height: 100%;
width: 100%;
}
}
}
.pageinfo input {
display: inline;
width: 70px;
}
.presentations {

17
src/styles/components/_rightslide.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,12 @@ @@ -19,7 +19,12 @@
*
*/
#rightslide {
.withBuddylist #rightslide { // scss-lint:disable IdSelector
right: 0;
}
#rightslide { // scss-lint:disable IdSelector
bottom: 0;
left: 0;
pointer-events: none;
@ -28,14 +33,10 @@ @@ -28,14 +33,10 @@
top: $minbarheight;
transition: right 200ms ease-in-out;
z-index: 5;
}
.withBuddylist #rightslide {
right: 0;
}
#rightslide .rightslidepane {
.rightslidepane {
height: 100%;
position: relative;
width: 100%;
}
}

9
src/styles/components/_roombar.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,19 +19,18 @@ @@ -19,19 +19,18 @@
*
*/
#roombar {
#roombar { // scss-lint:disable IdSelector
left: 0;
min-width: 260px;
position: absolute;
right: 0;
top: $minbarheight;
z-index: 4;
}
#roombar .roombar {
.roombar {
left: 0;
position: absolute;
right: 0;
top: 0;
}
}

24
src/styles/components/_screenshare.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,16 +19,23 @@ @@ -19,16 +19,23 @@
*
*/
.mainScreenshare #screenshare { // scss-lint:disable IdSelector
display: block;
}
.screenshare {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.mainScreenshare #screenshare {
display: block;
.overlaybar {
bottom: 0;
left: 0;
right: 0;
}
}
.screensharepane {
@ -39,9 +46,6 @@ @@ -39,9 +46,6 @@
position: absolute;
right: 0;
top: 0;
}
.screensharepane {
.remotescreen {
position: relative;
}
@ -56,9 +60,3 @@ @@ -56,9 +60,3 @@
max-height: none;
width: auto;
}
.screenshare .overlaybar {
bottom: 0;
left: 0;
right: 0;
}

11
src/styles/components/_settings.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,8 @@ @@ -19,7 +19,8 @@
*
*/
#settings {
#settings { // scss-lint:disable IdSelector
// scss-lint:disable NestingDepth
background: $settings-background;
border-left: 1px solid $bordercolor;
bottom: 0;
@ -30,9 +31,8 @@ @@ -30,9 +31,8 @@
transition: right 200ms ease-in-out;
width: 520px;
z-index: 50;
}
#settings.show {
&.show {
right: 0;
@include breakpt($breakpoint-settings-medium, max-width, only screen) {
@ -54,6 +54,7 @@ @@ -54,6 +54,7 @@
}
}
}
}
.settings {
background: $settings-background;
@ -65,9 +66,7 @@ @@ -65,9 +66,7 @@
position: absolute;
right: 0;
top: 0;
}
.settings {
@include breakpt($breakpoint-settings-medium, max-width, only screen) {
padding-bottom: 10px;
}

2
src/styles/components/_social.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

31
src/styles/components/_usability.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,24 @@ @@ -19,7 +19,24 @@
*
*/
.withChat,
.withBuddylist {
// scss-lint:disable IdSelector
#help {
right: 260px;
}
}
.withChat.withBuddylist,
.withSettings {
// scss-lint:disable IdSelector
#help {
right: 520px;
}
}
#help { // scss-lint:disable IdSelector
bottom: 10px;
color: #aaa;
font-size: 1.1em;
@ -34,17 +51,7 @@ @@ -34,17 +51,7 @@
width: 350px;
}
.withChat #help,
.withBuddylist #help {
right: 260px;
}
.withChat.withBuddylist #help,
.withSettings #help {
right: 520px;
}
#help {
.help {
@include breakpt($breakpoint-useability-small, max-width, only screen) {
display: none;
}

127
src/styles/components/_youtubevideo.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,53 +19,18 @@ @@ -19,53 +19,18 @@
*
*/
.youtubevideo {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
.mainYoutubevideo #youtubevideo { // scss-lint:disable IdSelector
display: block;
}
.youtubevideopane {
.youtubevideo {
bottom: 0;
left: 0;
overflow: auto;
position: absolute;
right: 0;
top: 0;
}
#youtubecontainer {
position: relative;
}
#youtubeplayerinfo {
bottom: 10%;
left: 0;
opacity: 0;
pointer-events: auto;
position: absolute;
right: 0;
text-align: center;
transition-duration: .2s;
transition-property: opacity;
z-index: 10;
&:hover {
opacity: .8;
}
}
#youtubeplayerinfo div {
background-color: #f9f2f4;
border-radius: 10px;
display: inline-block;
font-size: 2em;
padding: 20px 40px;
}
.youtubevideo .click-container {
.click-container {
bottom: 0;
left: 0;
position: absolute;
@ -74,48 +39,88 @@ @@ -74,48 +39,88 @@
z-index: 5;
}
.youtubevideo .welcome {
.welcome {
max-width: 700px;
}
.youtubevideo .welcome h1 {
h1 {
margin-top: 10px;
}
.youtubevideo .welcome .welcome-container {
.welcome-container {
max-width: 700px;
}
.youtubevideo .welcome .welcome-logo {
.welcome-logo {
background: transparent;
font-size: 10em;
}
.mainYoutubevideo #youtubevideo {
display: block;
}
.youtubevideo .overlaybar {
.overlaybar {
bottom: 0;
left: 0;
right: 0;
}
.youtubevideo .overlaybar-content {
.overlaybar-content {
max-width: 100%;
width: 100%;
form .overlaybar-buttons {
position: absolute;
right: 23px;
top: 6px;
}
}
.youtubevideo .overlaybar-input {
.overlaybar-input {
padding-right: 15px;
position: relative;
width: 100%;
}
.youtubevideo .overlaybar-content form .overlaybar-buttons {
}
.youtubevideopane {
bottom: 0;
left: 0;
overflow: auto;
position: absolute;
right: 23px;
top: 6px;
right: 0;
top: 0;
}
.youtubecontainer {
position: relative;
&.fullscreen {
width: 100%;
}
}
.youtubeplayerinfo {
bottom: 10%;
left: 0;
opacity: 0;
pointer-events: auto;
position: absolute;
right: 0;
text-align: center;
transition-duration: .2s;
transition-property: opacity;
z-index: 10;
&:hover {
opacity: .8;
}
div {
background-color: #f9f2f4;
border-radius: 10px;
display: inline-block;
font-size: 2em;
padding: 20px 40px;
}
}
.volumecontrol {
@ -143,37 +148,37 @@ @@ -143,37 +148,37 @@
display: inline-block;
padding: 6px 8px;
vertical-align: middle;
}
.volumebar .bar {
.bar {
-webkit-appearance: none;
background-color: #aaa;
border: 1px solid #aaa;
height: 3px;
outline: 0;
width: 100px;
}
.volumebar .bar::-webkit-slider-thumb {
&::-webkit-slider-thumb {
-webkit-appearance: none;
background-color: #fff;
height: 20px;
width: 6px;
}
.volumebar .bar::-moz-range-track {
&::-moz-range-track {
background: #aaa;
border: 0;
}
.volumebar .bar::-moz-range-thumb {
&::-moz-range-thumb {
background-color: #fff;
border-radius: 0;
height: 20px;
width: 6px;
}
.volumebar .bar::-moz-focusring {
&::-moz-focusring {
outline: 1px solid #aaa;
outline-offset: -1px;
}
}
}

2
src/styles/csp.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*!
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

5
src/styles/font-awesome.scss vendored

@ -2,3 +2,8 @@ @@ -2,3 +2,8 @@
@import 'compass';
@import 'global/variables';
@import 'libs/font-awesome/scss/font-awesome';
/* Hide all which requires FA while still loading web font */
.wf-loading .fa {
visibility: hidden !important;
}

2
src/styles/global/_animations.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

4
src/styles/global/_base.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -38,7 +38,7 @@ a { @@ -38,7 +38,7 @@ a {
cursor: pointer;
}
#background {
#background { // scss-lint:disable IdSelector
background: $main-background;
bottom: 0;
left: 0;

4
src/styles/global/_loader.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
#loader {
#loader { // scss-lint:disable IdSelector
background: $load-logo no-repeat center;
background-size: contain;
bottom: 15%;

2
src/styles/global/_nicescroll.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

46
src/styles/global/_overlaybar.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
*/
.overlaybar {
// scss-lint:disable QualifyingElement
background: $overlaybar-background;
border-bottom: 1px solid #222;
border-top: 1px solid #222;
@ -30,14 +31,30 @@ @@ -30,14 +31,30 @@
text-shadow: 0 0 5px #000;
user-select: none;
vertical-align: middle;
}
.overlaybar {
// scss-lint:disable QualifyingElement
&:hover {
background: $componentfg2;
}
&.notvisible {
background: transparent;
border-bottom: 1px solid transparent;
border-top: 1px solid transparent;
pointer-events: none;
&:hover {
background: transparent;
}
.overlaybar-content {
display: none;
}
.overlaybar-overlay {
display: block;
}
}
.btn {
text-shadow: none;
}
@ -59,27 +76,6 @@ @@ -59,27 +76,6 @@
label {
padding-top: 6px !important;
}
}
.overlaybar {
&.notvisible {
background: transparent;
border-bottom: 1px solid transparent;
border-top: 1px solid transparent;
pointer-events: none;
&:hover {
background: transparent;
}
.overlaybar-content {
display: none;
}
.overlaybar-overlay {
display: block;
}
}
.overlaybar-button {
color: $overlaybar-btn;

4
src/styles/global/_pages.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
*
*/
#page {
#page { // scss-lint:disable IdSelector
bottom: 0;
left: 0;
position: absolute;

2
src/styles/global/_variables.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/styles/global/_views.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/styles/global/_withs.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/styles/libs/_dialogs.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/styles/libs/angular/angular-csp.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/styles/libs/angular/angular.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
src/styles/main.scss

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*!
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

4
src/styles/scss.yml

@ -1,7 +1,3 @@ @@ -1,7 +1,3 @@
scss_files: "**/*.scss"
exclude: "libs/**"
linters:
BangFormat:
enabled: true

2
static/css/csp.min.css vendored

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*!
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
static/css/font-awesome.min.css vendored

File diff suppressed because one or more lines are too long

4
static/css/main.min.css vendored

File diff suppressed because one or more lines are too long

34
static/js/app.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -87,6 +87,22 @@ define([ @@ -87,6 +87,22 @@ define([
var src;
if (data && data.locale_data) {
src = data.locale_data[domain];
// Support older po files built for older jed (see https://github.com/SlexAxton/Jed/issues/36).
var count = 0;
var v;
for (var k in src) {
if (src.hasOwnProperty(k)) {
v = src[k];
if (v.constructor === Array && v[0] === null) {
v.shift();
} else {
count++;
}
if (count > 1) {
break;
}
}
}
}
var dst = this.data.locale_data[domain];
if (!dst) {
@ -138,7 +154,7 @@ define([ @@ -138,7 +154,7 @@ define([
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|filesystem|blob):/);
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|filesystem|blob):|data:image\//);
// Setup routing
$routeProvider.when("/:room", {});
$routeProvider.when("/:room*", {});
// Use HTML5 routing.
$locationProvider.html5Mode(true);
}]);
@ -174,7 +190,17 @@ define([ @@ -174,7 +190,17 @@ define([
app.directive("spreedWebrtc", [function() {
return {
restrict: "A",
controller: "MediastreamController"
scope: false,
controller: "AppController"
}
}]);
app.directive("uiLogo", ["globalContext", function(globalContext) {
return {
restrict: "A",
link: function($scope, $element, $attrs) {
$attrs.$set("title", globalContext.Cfg.Title || "");
}
}
}]);
@ -182,7 +208,7 @@ define([ @@ -182,7 +208,7 @@ define([
};
// Our API version as float. This value is incremented on
// Our client side API version as float. This value is incremented on
// breaking changes to plugins can check on it.
var apiversion = 1.1;

6
static/js/base.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -32,4 +32,6 @@ define([ // Helper module to put non dependency base libraries together. @@ -32,4 +32,6 @@ define([ // Helper module to put non dependency base libraries together.
'rAF',
'humanize',
'sha',
'sjcl'], function() {});
'sjcl',
'text',
'webfont'], function() {});

122
static/js/controllers/appcontroller.js

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* 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/>.
*
*/
"use strict";
define(["jquery", "angular", "underscore"], function($, angular, _) {
// AppController
return ["$scope", "$window", "appData", "userSettingsData", "$timeout", function($scope, $window, appData, userSettingsData, $timeout) {
// Disable drag and drop.
$($window).on("dragover dragenter drop", function(event) {
event.preventDefault();
});
appData.set($scope);
// User related scope data.
$scope.authorizing = false;
$scope.roomsHistory = [];
$scope.defaults = {
displayName: null,
buddyPicture: null,
message: null,
settings: {
videoQuality: "high",
sendStereo: false,
maxFrameRate: 20,
defaultRoom: "",
language: "",
audioRenderToAssociatedSkin: true,
videoCpuOveruseDetection: true,
experimental: {
enabled: false,
audioEchoCancellation2: true,
audioAutoGainControl2: true,
audioNoiseSuppression2: true,
audioTypingNoiseDetection: true,
videoLeakyBucket: true,
videoNoiseReduction: false
}
}
};
$scope.master = angular.copy($scope.defaults);
$scope.update = function(user) {
$scope.master = angular.copy(user);
if (appData.flags.connected) {
$scope.updateStatus();
}
$scope.refreshWebrtcSettings();
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.loadUserSettings = function() {
$scope.master = angular.copy($scope.defaults);
var storedUser = userSettingsData.load();
if (storedUser) {
$scope.user = $.extend(true, {}, $scope.master, storedUser);
$scope.user.settings = $.extend(true, {}, $scope.user.settings, $scope.master.settings, $scope.user.settings);
$scope.update($scope.user);
$scope.loadedUser = storedUser.displayName && true;
} else {
$scope.loadedUser = false;
}
$scope.roomsHistory = [];
appData.e.triggerHandler("userSettingsLoaded", [$scope.loadedUser, $scope.user]);
$scope.reset();
};
$scope.manualReloadApp = function(url) {
appData.flags.manualUnload = true;
if (url) {
$window.location.href = url;
$timeout(function() {
appData.flags.manualUnload = false;
}, 0);
} else {
$window.location.reload(true);
}
};
$scope.$on("room.joined", function(event, roomName) {
if (roomName) {
_.pull($scope.roomsHistory, roomName);
$scope.roomsHistory.unshift(roomName);
if ($scope.roomsHistory.length > 15) {
// Limit the history.
$scope.roomsHistory = $scope.roomsHistory.splice(0, 15);
}
}
});
appData.e.on("authorizing", function(event, authorizing) {
$scope.authorizing = !!authorizing;
});
$scope.reset(); // Call once for bootstrap.
}];
});

70
static/js/controllers/chatroomcontroller.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -20,10 +20,10 @@ @@ -20,10 +20,10 @@
*/
"use strict";
define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!partials/contactrequest.html', 'text!partials/geolocation.html'], function($, _, moment, templateFileInfo, templateContactRequest, templateGeolocation) {
define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!partials/contactrequest.html', 'text!partials/geolocation.html', 'text!partials/picturehover.html'], function($, _, moment, templateFileInfo, templateContactRequest, templateGeolocation, templatePictureHover) {
// ChatroomController
return ["$scope", "$element", "$window", "safeMessage", "safeDisplayName", "$compile", "$filter", "translation", function($scope, $element, $window, safeMessage, safeDisplayName, $compile, $filter, translation) {
return ["$scope", "$element", "$window", "safeMessage", "safeDisplayName", "$compile", "$filter", "translation", "mediaStream", function($scope, $element, $window, safeMessage, safeDisplayName, $compile, $filter, translation, mediaStream) {
$scope.outputElement = $element.find(".output");
$scope.inputElement = $element.find(".input");
@ -50,6 +50,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -50,6 +50,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
var fileInfo = $compile(templateFileInfo);
var contactRequest = $compile(templateContactRequest);
var geoLocation = $compile(templateGeolocation);
var pictureHover = $compile(templatePictureHover);
var knowMessage = {
r: {},
@ -101,6 +102,42 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -101,6 +102,42 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
}
};
var addPictureHover = function(from, msg, is_self) {
if (msg.picture && !is_self) {
var subscope = $scope.$new();
subscope.startChat = function() {
$scope.$emit("startchat", from, {
autofocus: true,
restore: true
});
};
subscope.doCall = function() {
mediaStream.webrtc.doCall(from);
};
pictureHover(subscope, function(clonedElement, scope) {
msg.picture.append(clonedElement);
});
} else {
return;
}
msg.extra_css += "with_hoverimage ";
};
var showTitleAndPicture = function(from, msg, is_self) {
if ($scope.isgroupchat) {
msg.title = $("<strong>");
msg.title.html(displayName(from, true));
msg.extra_css += "with_name ";
var imgSrc = buddyImageSrc(from);
msg.picture = $('<div class="buddyPicture"><i class="fa fa-user fa-3x"/><img/></div>');
if (imgSrc) {
msg.picture.find("img").attr("src", imgSrc);
}
addPictureHover(from, msg, is_self);
}
};
// Make sure that chat links are openend in a new window.
$element.on("click", function(event) {
var elem = $(event.target);
@ -302,27 +339,16 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -302,27 +339,16 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
var is_new_message = lastSender !== from;
var is_self = from === sessonid;
var extra_css = "";
var title = null;
var picture = null;
var showTitleAndPicture = function() {
if ($scope.isgroupchat) {
title = $("<strong>");
title.html(displayName(from, true));
extra_css += "with_name ";
var imgSrc = buddyImageSrc(from);
picture = $('<div class="buddyPicture"><i class="fa fa-user fa-3x"/><img/></div>');
if (imgSrc) {
picture.find("img").attr("src", imgSrc);
}
}
var msg = {
extra_css: "",
title: null,
picture: null
};
if (is_new_message) {
lastSender = from;
$scope.showdate(timestamp);
showTitleAndPicture()
showTitleAndPicture(from, msg, is_self);
}
var strMessage = s.join(" ");
@ -336,9 +362,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -336,9 +362,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
}
if (is_self) {
extra_css += "is_self";
msg.extra_css += "is_self";
} else {
extra_css += "is_remote";
msg.extra_css += "is_remote";
}
if (timestamp) {
var ts = $('<div class="timestamp"/>');
@ -349,7 +375,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p @@ -349,7 +375,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p
nodes = ts;
}
}
return $scope.display(strMessage, nodes, extra_css, title, picture);
return $scope.display(strMessage, nodes, msg.extra_css, msg.title, msg.picture);
};

2
static/js/controllers/contactsmanagercontroller.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

2
static/js/controllers/contactsmanagereditcontroller.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

12
static/js/controllers/controllers.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -23,20 +23,22 @@ @@ -23,20 +23,22 @@
define([
'underscore',
'controllers/mediastreamcontroller',
'controllers/uicontroller',
'controllers/statusmessagecontroller',
'controllers/chatroomcontroller',
'controllers/usersettingscontroller',
'controllers/contactsmanagercontroller',
'controllers/contactsmanagereditcontroller'], function(_, MediastreamController, StatusmessageController, ChatroomController, UsersettingsController, ContactsmanagerController, ContactsmanagereditController) {
'controllers/contactsmanagereditcontroller',
'controllers/appcontroller'], function(_, UiController, StatusmessageController, ChatroomController, UsersettingsController, ContactsmanagerController, ContactsmanagereditController, AppController) {
var controllers = {
MediastreamController: MediastreamController,
UiController: UiController,
StatusmessageController: StatusmessageController,
ChatroomController: ChatroomController,
UsersettingsController: UsersettingsController,
ContactsmanagerController: ContactsmanagerController,
ContactsmanagereditController: ContactsmanagereditController
ContactsmanagereditController: ContactsmanagereditController,
AppController: AppController
};
var initialize = function(angModule) {

2
static/js/controllers/statusmessagecontroller.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

211
static/js/controllers/mediastreamcontroller.js → static/js/controllers/uicontroller.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
@ -20,21 +20,13 @@ @@ -20,21 +20,13 @@
*/
"use strict";
define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'webrtc.adapter'], function($, _, angular, BigScreen, moment, sjcl, Modernizr) {
define(['jquery', 'underscore', 'bigscreen', 'moment', 'sjcl', 'modernizr', 'webrtc.adapter'], function($, _, BigScreen, moment, sjcl, Modernizr) {
return ["$scope", "$rootScope", "$element", "$window", "$timeout", "safeDisplayName", "safeApply", "mediaStream", "appData", "playSound", "desktopNotify", "alertify", "toastr", "translation", "fileDownload", "localStorage", "screensharing", "userSettingsData", "localStatus", "dialogs", "rooms", "constraints", function($scope, $rootScope, $element, $window, $timeout, safeDisplayName, safeApply, mediaStream, appData, playSound, desktopNotify, alertify, toastr, translation, fileDownload, localStorage, screensharing, userSettingsData, localStatus, dialogs, rooms, constraints) {
/*console.log("route", $route, $routeParams, $location);*/
// Disable drag and drop.
$($window).on("dragover dragenter drop", function(event) {
event.preventDefault();
});
return ["$scope", "$rootScope", "$element", "$window", "$timeout", "safeDisplayName", "safeApply", "mediaStream", "appData", "playSound", "desktopNotify", "alertify", "toastr", "translation", "fileDownload", "localStorage", "screensharing", "localStatus", "dialogs", "rooms", "constraints", function($scope, $rootScope, $element, $window, $timeout, safeDisplayName, safeApply, mediaStream, appData, playSound, desktopNotify, alertify, toastr, translation, fileDownload, localStorage, screensharing, localStatus, dialogs, rooms, constraints) {
// Avoid accidential reloads or exits when in a call.
var manualUnload = false;
$($window).on("beforeunload", function(event) {
if (manualUnload || !$scope.peer) {
if (appData.flags.manualUnload || !$scope.peer) {
return;
}
return translation._("Close this window and disconnect?");
@ -93,16 +85,13 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -93,16 +85,13 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
"prompt": "question1"
});
appData.set($scope);
var displayName = safeDisplayName;
// Init STUN and TURN servers.
$scope.stun = mediaStream.config.StunURIs || [];
if (!$scope.stun.length) {
$scope.stun.push("stun:stun.l.google.com:19302")
}
$scope.turn = {}; // TURN servers are set on received.self.
// Init STUN from server config.
(function() {
var stun = mediaStream.config.StunURIs || [];
constraints.stun(stun);
})();
// Add browser details for easy access.
$scope.isChrome = $window.webrtcDetectedBrowser === "chrome";
@ -112,8 +101,8 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -112,8 +101,8 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
// Add support status.
$scope.supported = {
screensharing: screensharing.supported,
renderToAssociatedSink: $window.navigator.platform.indexOf("Win") === 0
}
constraints: constraints.supported
};
// Default scope data.
$scope.status = "initializing";
@ -133,50 +122,7 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -133,50 +122,7 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
$scope.chatMessagesUnseen = 0;
$scope.autoAccept = null;
$scope.isCollapsed = true;
$scope.roomsHistory = [];
$scope.defaults = {
displayName: null,
buddyPicture: null,
message: null,
settings: {
videoQuality: "high",
sendStereo: false,
maxFrameRate: 20,
defaultRoom: "",
language: "",
audioRenderToAssociatedSkin: true,
videoCpuOveruseDetection: true,
experimental: {
enabled: false,
audioEchoCancellation2: true,
audioAutoGainControl2: true,
audioNoiseSuppression2: true,
audioTypingNoiseDetection: true,
videoLeakyBucket: true,
videoNoiseReduction: false
}
}
};
$scope.master = angular.copy($scope.defaults);
// Data voids.
var resurrect = null;
var reconnecting = false;
var connected = false;
var autoreconnect = true;
$scope.update = function(user) {
$scope.master = angular.copy(user);
if (connected) {
$scope.updateStatus();
}
$scope.refreshWebrtcSettings();
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.reset(); // Call once for bootstrap.
$scope.usermedia = null;
$scope.setStatus = function(status) {
// This is the connection status to signaling server.
@ -201,34 +147,14 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -201,34 +147,14 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
};
$scope.refreshWebrtcSettings = function() {
if (!$window.webrtcDetectedBrowser) {
console.warn("This is not a WebRTC capable browser.");
return;
}
var settings = $scope.master.settings;
// Create iceServers from scope.
var iceServers = [];
var iceServer;
if ($scope.stun.length) {
iceServer = $window.createIceServers($scope.stun);
if (iceServer.length) {
iceServers.push.apply(iceServers, iceServer);
}
}
if ($scope.turn.urls && $scope.turn.urls.length) {
iceServer = $window.createIceServers($scope.turn.urls, $scope.turn.username, $scope.turn.password);
if (iceServer.length) {
iceServers.push.apply(iceServers, iceServer);
}
}
mediaStream.webrtc.settings.pcConfig.iceServers = iceServers;
// Refresh constraints.
constraints.refresh($scope.master.settings);
constraints.refresh($scope.master.settings).then(function() {
var um = $scope.usermedia;
if (um && um.renegotiation && um.started) {
// Trigger renegotiation if supported and started.
um.doGetUserMediaWithConstraints(mediaStream.webrtc.settings.mediaConstraints);
}
});
};
$scope.refreshWebrtcSettings(); // Call once for bootstrap.
@ -257,34 +183,6 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -257,34 +183,6 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
};
$scope.manualReloadApp = function(url) {
manualUnload = true;
if (url) {
$window.location.href = url;
$timeout(function() {
manualUnload = false;
}, 0);
} else {
$window.location.reload(true);
}
};
$scope.loadUserSettings = function() {
$scope.master = angular.copy($scope.defaults);
var storedUser = userSettingsData.load();
if (storedUser) {
$scope.user = $.extend(true, {}, $scope.master, storedUser);
$scope.user.settings = $.extend(true, {}, $scope.user.settings, $scope.master.settings, $scope.user.settings);
$scope.update($scope.user);
$scope.loadedUser = storedUser.displayName && true;
} else {
$scope.loadedUser = false;
}
$scope.roomsHistory = [];
appData.e.triggerHandler("userSettingsLoaded", [$scope.loadedUser, $scope.user]);
$scope.reset();
};
$scope.toggleBuddylist = (function() {
var oldState = null;
return function(status, force) {
@ -341,10 +239,13 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -341,10 +239,13 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
scope.id = scope.myid = data.Id;
scope.userid = scope.myuserid = data.Userid ? data.Userid : null;
scope.suserid = data.Suserid ? data.Suserid : null;
scope.turn = data.Turn;
scope.stun = data.Stun;
scope.refreshWebrtcSettings();
});
// Set TURN and STUN data and refresh webrtc settings.
constraints.turn(data.Turn);
constraints.stun(data.Stun);
$scope.refreshWebrtcSettings();
if (data.Version !== mediaStream.version) {
console.info("Server was upgraded. Reload required.");
if (!reloadDialog) {
@ -388,9 +289,9 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -388,9 +289,9 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
}
// Support resurrection shrine.
if (resurrect) {
var resurrection = resurrect;
resurrect = null;
if (appData.flags.resurrect) {
var resurrection = appData.flags.resurrect;
appData.flags.resurrect = null;
$timeout(function() {
if (resurrection.id === $scope.id) {
console.log("Using resurrection shrine", resurrection);
@ -513,25 +414,38 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -513,25 +414,38 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
alertify.dialog.alert(translation._("Oops") + "<br/>" + message);
});
mediaStream.webrtc.e.on("usermedia", function(event, usermedia) {
safeApply($scope, function(scope) {
scope.usermedia = usermedia;
});
});
appData.flags.autoreconnect = true;
appData.flags.autoreconnectDelay = 0;
var reconnect = function() {
if (connected && autoreconnect) {
if (resurrect === null) {
if (appData.flags.connected && appData.flags.autoreconnect) {
if (appData.flags.resurrect === null) {
// Storage data at the resurrection shrine.
resurrect = {
appData.flags.resurrect = {
status: $scope.getStatus(),
id: $scope.id
}
console.log("Stored data at the resurrection shrine", resurrect);
console.log("Stored data at the resurrection shrine", appData.flags.resurrect);
}
if (!reconnecting) {
reconnecting = true;
if (!appData.flags.reconnecting) {
var delay = appData.flags.autoreconnectDelay;
if (delay < 10000) {
appData.flags.autoreconnectDelay += 500;
}
appData.flags.reconnecting = true;
_.delay(function() {
if (autoreconnect) {
if (appData.flags.autoreconnect) {
console.log("Requesting to reconnect ...");
mediaStream.reconnect();
}
reconnecting = false;
}, 500);
appData.flags.reconnecting = false;
}, delay);
$scope.setStatus("reconnecting");
} else {
console.warn("Already reconnecting ...");
@ -550,12 +464,13 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -550,12 +464,13 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
$scope.userid = $scope.suserid = null;
switch (event.type) {
case "open":
connected = true;
appData.flags.connected = true;
appData.flags.autoreconnectDelay = 0;
$scope.updateStatus(true);
$scope.setStatus("waiting");
break;
case "error":
if (connected) {
if (appData.flags.connected) {
reconnect();
} else {
$scope.setStatus(event.type);
@ -792,17 +707,6 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -792,17 +707,6 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
$scope.chatMessagesUnseen = $scope.chatMessagesUnseen - count;
});
$scope.$on("room.joined", function(event, roomName) {
if (roomName) {
_.pull($scope.roomsHistory, roomName);
$scope.roomsHistory.unshift(roomName);
if ($scope.roomsHistory.length > 15) {
// Limit the history.
$scope.roomsHistory = $scope.roomsHistory.splice(0, 15);
}
}
});
_.defer(function() {
if (!Modernizr.websockets) {
alertify.dialog.alert(translation._("Your browser is not supported. Please upgrade to a current version."));
@ -813,6 +717,15 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder @@ -813,6 +717,15 @@ define(['jquery', 'underscore', 'angular', 'bigscreen', 'moment', 'sjcl', 'moder
alertify.dialog.alert(translation._("Your browser does not support WebRTC. No calls possible."));
return;
}
if (mediaStream.config.Renegotiation && $window.webrtcDetectedBrowser === "firefox" && $window.webrtcDetectedVersion < 38) {
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1017888
// and https://bugzilla.mozilla.org/show_bug.cgi?id=840728
// and https://bugzilla.mozilla.org/show_bug.cgi?id=842455
// XXX(longsleep): It seems that firefox has implemented new API which
// supports addTrack, removeTrack see http://w3c.github.io/mediacapture-main/#dom-mediastream-removetrack
console.warn("Renegotiation enabled -> currently not compatible with Firefox.");
return;
}
});
}];

2
static/js/controllers/usersettingscontroller.js

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2014 struktur AG
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save