45 changed files with 2284 additions and 406 deletions
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import () |
||||
|
||||
type Contact struct { |
||||
A string |
||||
B string |
||||
} |
||||
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"sort" |
||||
"sync" |
||||
) |
||||
|
||||
type User struct { |
||||
Id string |
||||
sessionTable map[string]*Session |
||||
mutex sync.RWMutex |
||||
} |
||||
|
||||
func NewUser(id string) *User { |
||||
|
||||
user := &User{ |
||||
Id: id, |
||||
sessionTable: make(map[string]*Session), |
||||
} |
||||
return user |
||||
|
||||
} |
||||
|
||||
// Return true if first session.
|
||||
func (u *User) AddSession(s *Session) bool { |
||||
first := false |
||||
u.mutex.Lock() |
||||
u.sessionTable[s.Id] = s |
||||
if len(u.sessionTable) == 1 { |
||||
fmt.Println("First session registered for user", u.Id) |
||||
first = true |
||||
} |
||||
u.mutex.Unlock() |
||||
return first |
||||
} |
||||
|
||||
// Return true if no session left.
|
||||
func (u *User) RemoveSession(s *Session) bool { |
||||
last := false |
||||
u.mutex.Lock() |
||||
delete(u.sessionTable, s.Id) |
||||
if len(u.sessionTable) == 0 { |
||||
fmt.Println("Last session unregistered for user", u.Id) |
||||
last = true |
||||
} |
||||
u.mutex.Unlock() |
||||
return last |
||||
} |
||||
|
||||
func (u *User) Data() *DataUser { |
||||
u.mutex.RLock() |
||||
defer u.mutex.RUnlock() |
||||
return &DataUser{ |
||||
Id: u.Id, |
||||
Sessions: len(u.sessionTable), |
||||
} |
||||
} |
||||
|
||||
func (u *User) SessionsData() []*DataSession { |
||||
|
||||
sessions := make([]*DataSession, 0, len(u.sessionTable)) |
||||
u.mutex.RLock() |
||||
defer u.mutex.RUnlock() |
||||
for _, session := range u.sessionTable { |
||||
sessions = append(sessions, session.Data()) |
||||
} |
||||
sort.Sort(ByPrioAndStamp(sessions)) |
||||
return sessions |
||||
|
||||
} |
||||
|
||||
type ByPrioAndStamp []*DataSession |
||||
|
||||
func (a ByPrioAndStamp) Len() int { |
||||
return len(a) |
||||
} |
||||
|
||||
func (a ByPrioAndStamp) Swap(i, j int) { |
||||
a[i], a[j] = a[j], a[i] |
||||
} |
||||
|
||||
func (a ByPrioAndStamp) Less(i, j int) bool { |
||||
if a[i].Prio < a[j].Prio { |
||||
return true |
||||
} |
||||
if a[i].Prio == a[j].Prio { |
||||
return a[i].stamp < a[j].stamp |
||||
} |
||||
return false |
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define([], function() { |
||||
|
||||
// UsersettingsController
|
||||
return ["$scope", "$element", "mediaStream", "safeApply", function($scope, $element, mediaStream, safeApply) { |
||||
|
||||
$scope.withUsersForget = true; |
||||
|
||||
this.registerUserid = function(btn) { |
||||
|
||||
var successHandler = function(data) { |
||||
console.info("Created new userid:", data.userid); |
||||
// If the server provided us a nonce, we can do everthing on our own.
|
||||
mediaStream.users.store(data); |
||||
$scope.loadedUserlogin = true; |
||||
safeApply($scope); |
||||
// Directly authenticate ourselves with the provided nonce.
|
||||
mediaStream.api.requestAuthentication(data.userid, data.nonce); |
||||
delete data.nonce; |
||||
}; |
||||
|
||||
console.log("No userid - creating one ..."); |
||||
mediaStream.users.register(btn.form, function(data) { |
||||
if (data.nonce) { |
||||
successHandler(data); |
||||
} else { |
||||
// No nonce received. So this means something we cannot do on our own.
|
||||
// Make are GET request and retrieve nonce that way and let the
|
||||
// browser/server do the rest.
|
||||
mediaStream.users.authorize(data, successHandler, function(data, status) { |
||||
console.error("Failed to get nonce after create", status, data); |
||||
}); |
||||
} |
||||
}, function(data, status) { |
||||
console.error("Failed to create userid", status, data); |
||||
}); |
||||
|
||||
}; |
||||
|
||||
this.forgetUserid = function() { |
||||
mediaStream.users.forget(); |
||||
mediaStream.connector.forgetAndReconnect(); |
||||
}; |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define(['jquery', 'underscore'], function($, _) { |
||||
|
||||
return ["translation", "buddyData", "contacts", function(translation, buddyData, contacts) { |
||||
|
||||
var controller = ['$scope', '$element', '$attrs', function($scope, $element, $attrs) { |
||||
|
||||
$scope.state = "request"; |
||||
$scope.doAccept = function() { |
||||
$scope.state = "accepted"; |
||||
$scope.doContact(true); |
||||
}; |
||||
|
||||
$scope.doReject = function() { |
||||
$scope.state = "rejected"; |
||||
$scope.doContact(false); |
||||
}; |
||||
|
||||
$scope.doContact = function(success) { |
||||
var r = $scope.request; |
||||
r.Success = !!success; |
||||
$scope.sendChatServer($scope.id, "Contact request answer", { |
||||
ContactRequest: r |
||||
}); |
||||
}; |
||||
|
||||
$scope.addContact = function(request, status) { |
||||
contacts.add(request, status) |
||||
}; |
||||
|
||||
// Add support for contacts on controller creation.
|
||||
var request = $scope.request; |
||||
if (request.Success && request.Userid && request.Token) { |
||||
var buddy = buddyData.lookup($scope.id); |
||||
var status = {}; |
||||
if (buddy) { |
||||
$.extend(status, buddy.status); |
||||
} |
||||
$scope.addContact(request, status); |
||||
} |
||||
|
||||
}]; |
||||
|
||||
return { |
||||
scope: true, |
||||
restrict: 'EAC', |
||||
controller: controller, |
||||
replace: false |
||||
} |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define(["underscore", "rAF"], function(_) { |
||||
|
||||
// animationFrame
|
||||
return ["$window", function($window) { |
||||
|
||||
var requestAnimationFrame = $window.requestAnimationFrame; |
||||
var registry = []; |
||||
|
||||
var caller = function(f) { |
||||
f(); |
||||
}; |
||||
var worker = function() { |
||||
registry.forEach(caller) |
||||
requestAnimationFrame(worker); |
||||
}; |
||||
|
||||
// Public api.
|
||||
var animationFrame = { |
||||
register: function(f) { |
||||
registry.push(f); |
||||
} |
||||
}; |
||||
|
||||
// Auto start worker.
|
||||
_.defer(worker); |
||||
|
||||
return animationFrame; |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -0,0 +1,191 @@
@@ -0,0 +1,191 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define(["underscore"], function(_) { |
||||
|
||||
// buddySession
|
||||
return [function() { |
||||
|
||||
var sessions = {}; |
||||
var serials = 0; |
||||
|
||||
var BuddySession = function(data) { |
||||
this.serial = serials++; |
||||
this.sessions = {}; |
||||
this.count = 0; |
||||
//console.log("creating session", this.serial, data.Id, data.Userid, this);
|
||||
var id = data.Id; |
||||
if (data.Id) { |
||||
var userid = data.Userid || null; |
||||
if (id === userid) { |
||||
// Add as default with userid.
|
||||
this.use(userid, data); |
||||
} else { |
||||
// Add as session.
|
||||
var sessionData = this.add(id, data); |
||||
if (userid) { |
||||
this.auth(userid, sessionData); |
||||
} |
||||
} |
||||
} else { |
||||
this.use(null, {}); |
||||
} |
||||
}; |
||||
|
||||
BuddySession.prototype.add = function(id, data) { |
||||
this.sessions[id] = data; |
||||
if (this.count === 0) { |
||||
this.use(id, data); |
||||
} |
||||
this.count++; |
||||
sessions[id] = this; |
||||
return data; |
||||
}; |
||||
|
||||
BuddySession.prototype.rm = function(id) { |
||||
delete this.sessions[id]; |
||||
this.count--; |
||||
delete sessions[id]; |
||||
}; |
||||
|
||||
BuddySession.prototype.get = function(id) { |
||||
if (!id) { |
||||
id = this.Id; |
||||
} |
||||
return this.sessions[id]; |
||||
}; |
||||
|
||||
BuddySession.prototype.use = function(id, data) { |
||||
if (id) { |
||||
this.Id = id; |
||||
} else { |
||||
this.Id = null; |
||||
} |
||||
//console.log("Use session as default", id, data, this);
|
||||
}; |
||||
|
||||
BuddySession.prototype.remove = function(id, onEmptyCallback) { |
||||
|
||||
this.rm(id); |
||||
if (id === this.Id) { |
||||
var sessions = this.sessions; |
||||
var sessionData; |
||||
for (var sd in sessions) { |
||||
if (sessions.hasOwnProperty(sd)) { |
||||
sessionData = sessions[sd]; |
||||
break; |
||||
} |
||||
} |
||||
if (sessionData) { |
||||
this.use(sessionData.Id, sessionData); |
||||
} else { |
||||
//console.log("Last session removed", sessions);
|
||||
if (this.Userid) { |
||||
//console.log("Using userid as session id");
|
||||
this.use(this.Userid); |
||||
} else { |
||||
this.use(null); |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
|
||||
}; |
||||
|
||||
BuddySession.prototype.update = function(id, data, onUseridCallback) { |
||||
|
||||
var userid = data.Userid; |
||||
//console.log("session update", id, userid, this, data);
|
||||
|
||||
var sessionData |
||||
if (id === userid) { |
||||
// Fake updates from userid ids.
|
||||
sessionData = data; |
||||
} else { |
||||
sessionData = this.get(id); |
||||
if (!sessionData) { |
||||
sessionData = this.add(id, data); |
||||
} |
||||
} |
||||
if (userid) { |
||||
this.auth(userid, sessionData, onUseridCallback); |
||||
} |
||||
if (data !== sessionData) { |
||||
if (data.Rev) { |
||||
sessionData.Rev = data.Rev; |
||||
} |
||||
if (data.Status) { |
||||
sessionData.Status = data.Status; |
||||
} |
||||
} |
||||
|
||||
if (id === this.Id) { |
||||
return sessionData; |
||||
} else { |
||||
return null; |
||||
} |
||||
|
||||
}; |
||||
|
||||
BuddySession.prototype.auth = function(userid, sessionData, onUseridCallback) { |
||||
|
||||
if (!this.Userid) { |
||||
this.Userid = userid; |
||||
//console.log("Session now has a user id", this.Id, userid);
|
||||
} |
||||
// Trigger callback if defined and not triggered before.
|
||||
if (onUseridCallback && !sessionData.auth) { |
||||
onUseridCallback(this); |
||||
sessionData.auth = true; |
||||
} |
||||
|
||||
}; |
||||
|
||||
BuddySession.prototype.merge = function(otherSession) { |
||||
if (!this.Userid) { |
||||
console.error("Refusing to merge into session as we have no userid", this, otherSession); |
||||
return; |
||||
} |
||||
if (otherSession.Userid !== this.Userid) { |
||||
console.error("Refusing to merge other session with different userid", otherSession, this); |
||||
return; |
||||
} |
||||
_.each(otherSession.sessions, _.bind(function(s, id) { |
||||
if (this.sessions.hasOwnProperty(id)) { |
||||
return; |
||||
} |
||||
this.add(id, s); |
||||
}, this)); |
||||
console.log("Merged sessions", this, otherSession); |
||||
}; |
||||
|
||||
return { |
||||
create: function(data) { |
||||
return new BuddySession(data); |
||||
}, |
||||
sessions: function() { |
||||
return sessions; |
||||
} |
||||
}; |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -0,0 +1,103 @@
@@ -0,0 +1,103 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define(['underscore', 'jquery'], function(underscore, $) { |
||||
|
||||
// contactData
|
||||
return [function() { |
||||
|
||||
var contacts = {}; |
||||
var users = {}; |
||||
var count = 0; |
||||
|
||||
var contactData = { |
||||
clear: function(cb) { |
||||
_.each(users, _.bind(function(idx, userid) { |
||||
var contact = contacts[idx]; |
||||
if (cb && contact) { |
||||
cb(contact); |
||||
} |
||||
this.remove(userid); |
||||
}, this)); |
||||
count = 0; |
||||
}, |
||||
addByRequest: function(request, status) { |
||||
//console.log("addByRequest", request, status);
|
||||
var userid = request.Userid; |
||||
var token = request.Token; |
||||
var id; |
||||
if (users.hasOwnProperty(userid)) { |
||||
// Existing contact. Replace it.
|
||||
id = users[userid]; |
||||
} else { |
||||
id = String(count++); |
||||
users[userid] = id; |
||||
} |
||||
var contact = contacts[id] = { |
||||
Id: "contact-"+id, |
||||
Userid: userid, |
||||
Token: token, |
||||
Status: null |
||||
} |
||||
return contact; |
||||
}, |
||||
addByData: function(data) { |
||||
//console.log("addByData", data.Userid, data);
|
||||
var userid = data.Userid; |
||||
if (users.hasOwnProperty(userid)) { |
||||
id = users[userid] |
||||
} else { |
||||
id = String(count++); |
||||
users[userid] = id; |
||||
} |
||||
var contact = contacts[id] = data; |
||||
contact.Id = id; |
||||
return contact; |
||||
}, |
||||
get: function(userid) { |
||||
if (users.hasOwnProperty(userid)) { |
||||
var id = users[userid]; |
||||
return contacts[id]; |
||||
} |
||||
return null; |
||||
}, |
||||
remove: function(userid) { |
||||
if (users.hasOwnProperty(userid)) { |
||||
var id = users[userid]; |
||||
delete contacts[id]; |
||||
} |
||||
delete users[userid]; |
||||
}, |
||||
getById: function(id) { |
||||
if (id.indexOf("contact-") === 0) { |
||||
id = id.substr(8) |
||||
} |
||||
if (contacts.hasOwnProperty(id)) { |
||||
return contacts[id]; |
||||
} |
||||
return null |
||||
} |
||||
}; |
||||
|
||||
return contactData; |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -0,0 +1,265 @@
@@ -0,0 +1,265 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define(['underscore', 'jquery', 'modernizr', 'sjcl'], function(underscore, $, Modernizr, sjcl) { |
||||
|
||||
var Database = function(name) { |
||||
this.version = 3; |
||||
this.ready = false; |
||||
this.db = null; |
||||
this.name = name; |
||||
this.e = $({}); |
||||
var request = indexedDB.open(this.name, this.version); |
||||
var that = this; |
||||
request.onupgradeneeded = function(event) { |
||||
var db = event.target.result; |
||||
var transaction = event.target.transaction; |
||||
transaction.onerror = _.bind(that.onerror, that); |
||||
that.init(db); |
||||
console.log("Created contacts database.") |
||||
}; |
||||
request.onsuccess = _.bind(that.onsuccess, that); |
||||
}; |
||||
Database.prototype.init = function(db) { |
||||
var createOrUpdateStore = function(name, obj) { |
||||
if (db.objectStoreNames.contains(name)) { |
||||
// TODO(longsleep): Migrate data.
|
||||
db.deleteObjectStore(name); |
||||
} |
||||
db.createObjectStore(name, obj); |
||||
} |
||||
// Create our object stores.
|
||||
createOrUpdateStore("contacts", { |
||||
// We use id field as our unique identifier.
|
||||
keyPath: "id" |
||||
}); |
||||
}; |
||||
Database.prototype.onerror = function(event) { |
||||
console.log("IndexDB database error", event); |
||||
}; |
||||
Database.prototype.onsuccess = function(event) { |
||||
this.db = event.target.result; |
||||
this.ready = true; |
||||
console.log("Openend database", this.db); |
||||
this.e.triggerHandler("ready"); |
||||
}; |
||||
Database.prototype.put = function(storename, data, successCallback, errorCallback) { |
||||
var transaction = this.db.transaction(storename, "readwrite"); |
||||
var store = transaction.objectStore(storename); |
||||
var request = store.put(data); |
||||
if (!errorCallback) { |
||||
errorCallback = _.bind(this.onerror, this); |
||||
} |
||||
transaction.onerror = request.onerror = errorCallback; |
||||
if (successCallback) { |
||||
request.onsuccess = successCallback; |
||||
} |
||||
return request; |
||||
}; |
||||
Database.prototype.delete = function(storename, id, successCallback, errorCallback) { |
||||
var transaction = this.db.transaction(storename, "readwrite"); |
||||
var store = transaction.objectStore(storename); |
||||
var request = store.delete(id); |
||||
if (!errorCallback) { |
||||
errorCallback = _.bind(this.onerror, this); |
||||
} |
||||
transaction.onerror = request.onerror = errorCallback; |
||||
if (successCallback) { |
||||
request.onsuccess = successCallback; |
||||
} |
||||
return request; |
||||
}; |
||||
Database.prototype.all = function(storename, iteratorCallback, errorCallback) { |
||||
var transaction = this.db.transaction(storename); |
||||
var store = transaction.objectStore(storename); |
||||
var keyRange = IDBKeyRange.lowerBound(0); |
||||
var cursorRequest = store.openCursor(keyRange); |
||||
cursorRequest.onsuccess = function(event) { |
||||
var result = event.target.result; |
||||
if (!result) { |
||||
return; |
||||
} |
||||
//console.log("read data idb", result, event);
|
||||
iteratorCallback(result.value); |
||||
result.continue(); |
||||
}; |
||||
if (!errorCallback) { |
||||
errorCallback = _.bind(this.onerror, this); |
||||
} |
||||
transaction.onerror = cursorRequest.onerror = errorCallback; |
||||
return cursorRequest; |
||||
}; |
||||
Database.prototype.close = function() { |
||||
// TODO(longsleep): Database close.
|
||||
this.e.off(); |
||||
if (this.db) { |
||||
this.db.close(); |
||||
this.db = null; |
||||
this.ready = false; |
||||
} |
||||
_.defer(_.bind(function() { |
||||
this.e.triggerHandler("closed"); |
||||
}, this)); |
||||
}; |
||||
|
||||
// contacts
|
||||
return ["appData", "contactData", "mediaStream", function(appData, contactData, mediaStream) { |
||||
|
||||
var Contacts = function() { |
||||
|
||||
this.e = $({}); |
||||
this.userid = null; |
||||
this.key = null; |
||||
this.database = null; |
||||
|
||||
appData.e.on("authenticationChanged", _.bind(function(event, userid, suserid) { |
||||
// TODO(longsleep): Avoid creating empty databases. Create db on store only.
|
||||
var database = this.open(userid, suserid); |
||||
if (database && userid) { |
||||
// Load when database is ready and userid is available.
|
||||
if (database.ready) { |
||||
_.defer(_.bind(function() { |
||||
if (this.database === database) { |
||||
this.load(); |
||||
} |
||||
}, this)); |
||||
} else { |
||||
database.e.one("ready", _.bind(function() { |
||||
if (this.database === database) { |
||||
this.load(); |
||||
} |
||||
}, this)); |
||||
} |
||||
} |
||||
}, this)); |
||||
|
||||
}; |
||||
|
||||
Contacts.prototype.open = function(userid, suserid) { |
||||
|
||||
if (this.database && (!userid || this.userid !== userid)) { |
||||
// Unload existing contacts.
|
||||
this.unload(); |
||||
// Close existing database.
|
||||
this.database.close(); |
||||
this.database = null; |
||||
} |
||||
if (userid) { |
||||
if (!Modernizr.indexeddb) { |
||||
return; |
||||
} |
||||
// Create secure key for hashing and encryption.
|
||||
this.key = sjcl.codec.base64.fromBits(sjcl.hash.sha256.hash(suserid+mediaStream.config.Token)); |
||||
// Create database name for user which.
|
||||
var id = "mediastream-" + this.id(userid); |
||||
console.log("Open of database:", id); |
||||
var database = this.database = new Database(id); |
||||
return database; |
||||
} else { |
||||
this.database = null; |
||||
return null; |
||||
} |
||||
|
||||
}; |
||||
|
||||
Contacts.prototype.id = function(userid) { |
||||
|
||||
var hmac = new sjcl.misc.hmac(this.key); |
||||
return sjcl.codec.base64.fromBits(hmac.encrypt(userid)); |
||||
|
||||
}; |
||||
|
||||
Contacts.prototype.encrypt = function(data) { |
||||
|
||||
return sjcl.encrypt(this.key, JSON.stringify(data)); |
||||
|
||||
}; |
||||
|
||||
Contacts.prototype.decrypt = function(data) { |
||||
|
||||
var result; |
||||
try { |
||||
var s = sjcl.decrypt(this.key, data); |
||||
result = JSON.parse(s); |
||||
} catch(err) { |
||||
console.error("Failed to decrypt contact data", err); |
||||
} |
||||
return result; |
||||
|
||||
}; |
||||
|
||||
Contacts.prototype.load = function() { |
||||
if (this.database) { |
||||
console.log("Load contacts from storage", this); |
||||
var remove = []; |
||||
this.database.all("contacts", _.bind(function(data) { |
||||
var d = this.decrypt(data.contact); |
||||
if (d) { |
||||
var contact = contactData.addByData(d); |
||||
// TODO(longsleep): Convert buddyImage string to Blob.
|
||||
this.e.triggerHandler("contactadded", d); |
||||
} else { |
||||
// Remove empty or invalid entries automatically.
|
||||
remove.push(data.id); |
||||
} |
||||
}, this)); |
||||
// Remove marked entries.
|
||||
if (remove.length) { |
||||
_.each(remove, _.bind(function(id) { |
||||
this.database.delete("contacts", id); |
||||
}, this)); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
Contacts.prototype.unload = function() { |
||||
contactData.clear(_.bind(function(contact) { |
||||
this.e.triggerHandler("contactremoved", contact); |
||||
}, this)); |
||||
}; |
||||
|
||||
Contacts.prototype.add = function(request, status) { |
||||
var contact = contactData.addByRequest(request, status); |
||||
this.e.triggerHandler("contactadded", contact); |
||||
if (this.database) { |
||||
this.database.put("contacts", { |
||||
id: this.id(contact.Userid), |
||||
contact: this.encrypt(contact) |
||||
}) |
||||
} |
||||
}; |
||||
|
||||
Contacts.prototype.remove = function(userid) { |
||||
var contact = contactData.get(userid); |
||||
console.log("contacts remove", userid, contact); |
||||
if (contact) { |
||||
contactData.remove(userid); |
||||
if (this.database) { |
||||
this.database.delete("contacts", this.id(userid)); |
||||
} |
||||
this.e.triggerHandler("contactremoved", contact); |
||||
} |
||||
}; |
||||
|
||||
return new Contacts(); |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* Spreed WebRTC. |
||||
* Copyright (C) 2013-2014 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/>.
|
||||
* |
||||
*/ |
||||
define(["modernizr"], function(Modernizr) { |
||||
|
||||
// localStorage
|
||||
return ["$window", function($window) { |
||||
|
||||
// PersistentStorage (c)2014 struktur AG. MIT license.
|
||||
var PersistentStorage = function(prefix) { |
||||
this.prefix = prefix ? prefix : "ps"; |
||||
this.isPersistentStorage = true; |
||||
}; |
||||
PersistentStorage.prototype.setItem = function(key, data) { |
||||
var name = this.prefix+"_"+key; |
||||
$window.document.cookie = name + "=" + data + "; path=/"; |
||||
}; |
||||
PersistentStorage.prototype.getItem = function(key) { |
||||
var name = this.prefix+"_"+key+"="; |
||||
var ca = $window.document.cookie.split(';'); |
||||
for (var i=0; i<ca.length; i++) { |
||||
var c = ca[i].trim(); |
||||
if (c.indexOf(name) === 0) { |
||||
return c.substring(name.length, c.length); |
||||
} |
||||
} |
||||
return null; |
||||
}; |
||||
PersistentStorage.prototype.removeItem = function(key) { |
||||
var name = this.prefix+"_"+key; |
||||
$window.document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/"; |
||||
}; |
||||
|
||||
var storage; |
||||
if (Modernizr.localstorage) { |
||||
storage = $window.localStorage; |
||||
} else { |
||||
storage = new PersistentStorage(); |
||||
} |
||||
|
||||
// public API.
|
||||
return { |
||||
setItem: function(key, data) { |
||||
return storage.setItem(key, data); |
||||
}, |
||||
getItem: function(key) { |
||||
return storage.getItem(key); |
||||
}, |
||||
removeItem: function(key) { |
||||
return storage.removeItem(key); |
||||
} |
||||
} |
||||
|
||||
}]; |
||||
|
||||
}); |
||||
@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
|
||||
<div class="buddy withSubline"> |
||||
<div class="buddyPicture"><i class="fa fa-user fa-3x"/><img ng-show="status.buddyPicture" alt ng-src="{{status.buddyPicture}}" width="46" height="46"/></div> |
||||
<div class="buddy" ng-class="{'contact': contact, 'withSubline': display.subline || session.Userid}"> |
||||
<div class="buddyPicture"><i class="fa fa-user fa-3x"/><img ng-show="display.buddyPicture" alt ng-src="{{display.buddyPicture}}" width="46" height="46"/></div> |
||||
<div class="buddy1">{{session.Id|displayName}}</div> |
||||
<div class="buddy2"><i ng-show="session.Userid" class="fa fa-star-o"></i> {{session.Ua}}</div> |
||||
<div class="buddy2"><span ng-show="session.Userid"><i class="fa contact" data-action="contact"></i><span ng-show="session.count"> ({{session.count}})</span></span> <span title="{{display.sublineFull}}">{{display.subline}}</span></div> |
||||
</div> |
||||
|
||||
@ -1,4 +1,11 @@
@@ -1,4 +1,11 @@
|
||||
<div class="buddyactions active"> |
||||
<a class="btn btn-info" title="{{_('Start video call')}}"><i class="fa fa-eye"></i></a> |
||||
<a class="btn btn-info" title="{{_('Start chat')}}" ng-click="doChat(session.Id); $event.stopPropagation()"><i class="fa fa-comments-o"></i></a> |
||||
<div class="buddyhover"> |
||||
<div class="buddyactions active"> |
||||
<a class="btn btn-info" data-action="call" title="{{_('Start video call')}}"><i class="fa fa-eye"></i></a> |
||||
<a class="btn btn-info" data-action="chat" title="{{_('Start chat')}}"><i class="fa fa-comments-o"></i></a> |
||||
</div> |
||||
<div class="buddysessions" ng-if="session.count>1"> |
||||
<ul> |
||||
<li ng-repeat="(id, s) in session.sessions" ng-class="{currentsession: s.Id === session.Id}"><!--<div class="buddyPicture buddyPictureSmall"><i class="fa fa-user fa-2x"/><img ng-show="s.Status.buddyPicture" alt ng-src="{{s.Status.buddyPicture}}" width="30" height="30"/></div>--><span class="btn-group"><a class="btn btn-sm btn-default buddy3" ng-click="$event.stopPropagation()"><span>{{s.Status.displayName}}, {{s.Message}}</span></a><a class="btn btn-sm btn-default" title="{{_('Start video call')}}" ng-click="doCall(s.Id); $event.stopPropagation()"><i class="fa fa-eye"></i></a><a class="btn btn-sm btn-default" title="{{_('Start chat')}}" ng-click="doChat(s.Id); $event.stopPropagation()"><i class="fa fa-comments-o"></i></a></span></li> |
||||
</ul> |
||||
</div> |
||||
</div> |
||||
|
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
<div class="contact-request"> |
||||
<div ng-if="!fromself && request.Userid && !request.Success && request.Token"> |
||||
<div ng-switch="state"> |
||||
<div ng-switch-when="request"> |
||||
<button ng-click="doAccept()" class="btn btn-success btn-sm">Accept</button> |
||||
<button ng-click="doReject()" class="btn btn-danger btn-sm">Reject</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
Loading…
Reference in new issue