Pod URL -> pod host. HTTPS is required to make friends.

Reason: in a network with mix http/https pods, https pods won't be able
to play videos from http pod (insecure requests).
This commit is contained in:
Chocobozzz 2016-11-14 20:03:04 +01:00
parent 41b5da1d8c
commit 49abbbbedc
24 changed files with 167 additions and 195 deletions

View File

@ -3,13 +3,13 @@
<div *ngIf="error" class="alert alert-danger">{{ error }}</div> <div *ngIf="error" class="alert alert-danger">{{ error }}</div>
<form (ngSubmit)="makeFriends()" [formGroup]="form"> <form (ngSubmit)="makeFriends()" [formGroup]="form">
<div class="form-group" *ngFor="let url of urls; let id = index; trackBy:customTrackBy"> <div class="form-group" *ngFor="let host of hosts; let id = index; trackBy:customTrackBy">
<label for="username">Url</label> <label for="username">Host</label>
<div class="input-group"> <div class="input-group">
<input <input
type="text" class="form-control" placeholder="http://domain.com" type="text" class="form-control" placeholder="domain.tld"
[id]="'url-' + id" [formControlName]="'url-' + id" [id]="'host-' + id" [formControlName]="'host-' + id"
/> />
<span class="input-group-btn"> <span class="input-group-btn">
<button *ngIf="displayAddField(id)" (click)="addField()" class="btn btn-default" type="button">+</button> <button *ngIf="displayAddField(id)" (click)="addField()" class="btn btn-default" type="button">+</button>
@ -17,8 +17,8 @@
</span> </span>
</div> </div>
<div [hidden]="form.controls['url-' + id].valid || form.controls['url-' + id].pristine" class="alert alert-warning"> <div [hidden]="form.controls['host-' + id].valid || form.controls['host-' + id].pristine" class="alert alert-warning">
It should be a valid url. It should be a valid host.
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { validateUrl } from '../../../shared'; import { validateHost } from '../../../shared';
import { FriendService } from '../shared'; import { FriendService } from '../shared';
@Component({ @Component({
@ -12,7 +12,7 @@ import { FriendService } from '../shared';
}) })
export class FriendAddComponent implements OnInit { export class FriendAddComponent implements OnInit {
form: FormGroup; form: FormGroup;
urls = [ ]; hosts = [ ];
error: string = null; error: string = null;
constructor(private router: Router, private friendService: FriendService) {} constructor(private router: Router, private friendService: FriendService) {}
@ -23,8 +23,8 @@ export class FriendAddComponent implements OnInit {
} }
addField() { addField() {
this.form.addControl(`url-${this.urls.length}`, new FormControl('', [ validateUrl ])); this.form.addControl(`host-${this.hosts.length}`, new FormControl('', [ validateHost ]));
this.urls.push(''); this.hosts.push('');
} }
customTrackBy(index: number, obj: any): any { customTrackBy(index: number, obj: any): any {
@ -32,52 +32,52 @@ export class FriendAddComponent implements OnInit {
} }
displayAddField(index: number) { displayAddField(index: number) {
return index === (this.urls.length - 1); return index === (this.hosts.length - 1);
} }
displayRemoveField(index: number) { displayRemoveField(index: number) {
return (index !== 0 || this.urls.length > 1) && index !== (this.urls.length - 1); return (index !== 0 || this.hosts.length > 1) && index !== (this.hosts.length - 1);
} }
isFormValid() { isFormValid() {
// Do not check the last input // Do not check the last input
for (let i = 0; i < this.urls.length - 1; i++) { for (let i = 0; i < this.hosts.length - 1; i++) {
if (!this.form.controls[`url-${i}`].valid) return false; if (!this.form.controls[`host-${i}`].valid) return false;
} }
const lastIndex = this.urls.length - 1; const lastIndex = this.hosts.length - 1;
// If the last input (which is not the first) is empty, it's ok // If the last input (which is not the first) is empty, it's ok
if (this.urls[lastIndex] === '' && lastIndex !== 0) { if (this.hosts[lastIndex] === '' && lastIndex !== 0) {
return true; return true;
} else { } else {
return this.form.controls[`url-${lastIndex}`].valid; return this.form.controls[`host-${lastIndex}`].valid;
} }
} }
removeField(index: number) { removeField(index: number) {
// Remove the last control // Remove the last control
this.form.removeControl(`url-${this.urls.length - 1}`); this.form.removeControl(`host-${this.hosts.length - 1}`);
this.urls.splice(index, 1); this.hosts.splice(index, 1);
} }
makeFriends() { makeFriends() {
this.error = ''; this.error = '';
const notEmptyUrls = this.getNotEmptyUrls(); const notEmptyHosts = this.getNotEmptyHosts();
if (notEmptyUrls.length === 0) { if (notEmptyHosts.length === 0) {
this.error = 'You need to specify at less 1 url.'; this.error = 'You need to specify at least 1 host.';
return; return;
} }
if (!this.isUrlsUnique(notEmptyUrls)) { if (!this.isHostsUnique(notEmptyHosts)) {
this.error = 'Urls need to be unique.'; this.error = 'Hosts need to be unique.';
return; return;
} }
const confirmMessage = 'Are you sure to make friends with:\n - ' + notEmptyUrls.join('\n - '); const confirmMessage = 'Are you sure to make friends with:\n - ' + notEmptyHosts.join('\n - ');
if (!confirm(confirmMessage)) return; if (!confirm(confirmMessage)) return;
this.friendService.makeFriends(notEmptyUrls).subscribe( this.friendService.makeFriends(notEmptyHosts).subscribe(
status => { status => {
// TODO: extractdatastatus // TODO: extractdatastatus
// if (status === 409) { // if (status === 409) {
@ -91,18 +91,18 @@ export class FriendAddComponent implements OnInit {
); );
} }
private getNotEmptyUrls() { private getNotEmptyHosts() {
const notEmptyUrls = []; const notEmptyHosts = [];
Object.keys(this.form.value).forEach((urlKey) => { Object.keys(this.form.value).forEach((hostKey) => {
const url = this.form.value[urlKey]; const host = this.form.value[hostKey];
if (url !== '') notEmptyUrls.push(url); if (host !== '') notEmptyHosts.push(host);
}); });
return notEmptyUrls; return notEmptyHosts;
} }
private isUrlsUnique(urls: string[]) { private isHostsUnique(hosts: string[]) {
return urls.every(url => urls.indexOf(url) === urls.lastIndexOf(url)); return hosts.every(host => hosts.indexOf(host) === hosts.lastIndexOf(host));
} }
} }

View File

@ -4,7 +4,7 @@
<thead> <thead>
<tr> <tr>
<th class="table-column-id">ID</th> <th class="table-column-id">ID</th>
<th>Url</th> <th>Host</th>
<th>Score</th> <th>Score</th>
<th>Created Date</th> <th>Created Date</th>
</tr> </tr>
@ -13,7 +13,7 @@
<tbody> <tbody>
<tr *ngFor="let friend of friends"> <tr *ngFor="let friend of friends">
<td>{{ friend.id }}</td> <td>{{ friend.id }}</td>
<td>{{ friend.url }}</td> <td>{{ friend.host }}</td>
<td>{{ friend.score }}</td> <td>{{ friend.score }}</td>
<td>{{ friend.createdDate | date: 'medium' }}</td> <td>{{ friend.createdDate | date: 'medium' }}</td>
</tr> </tr>

View File

@ -1,6 +1,6 @@
export interface Friend { export interface Friend {
id: string; id: string;
url: string; host: string;
score: number; score: number;
createdDate: Date; createdDate: Date;
} }

View File

@ -21,9 +21,9 @@ export class FriendService {
.catch((res) => this.restExtractor.handleError(res)); .catch((res) => this.restExtractor.handleError(res));
} }
makeFriends(notEmptyUrls) { makeFriends(notEmptyHosts) {
const body = { const body = {
urls: notEmptyUrls hosts: notEmptyHosts
}; };
return this.authHttp.post(FriendService.BASE_FRIEND_URL + 'makefriends', body) return this.authHttp.post(FriendService.BASE_FRIEND_URL + 'makefriends', body)

View File

@ -0,0 +1,11 @@
import { FormControl } from '@angular/forms';
export function validateHost(c: FormControl) {
let HOST_REGEXP = new RegExp('^(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$');
return HOST_REGEXP.test(c.value) ? null : {
validateHost: {
valid: false
}
};
}

View File

@ -1,3 +1,3 @@
export * from './url.validator'; export * from './host.validator';
export * from './user'; export * from './user';
export * from './video'; export * from './video';

View File

@ -1,11 +0,0 @@
import { FormControl } from '@angular/forms';
export function validateUrl(c: FormControl) {
let URL_REGEXP = new RegExp('^https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$');
return URL_REGEXP.test(c.value) ? null : {
validateUrl: {
valid: false
}
};
}

View File

@ -8,21 +8,12 @@ export class Video {
isLocal: boolean; isLocal: boolean;
magnetUri: string; magnetUri: string;
name: string; name: string;
podUrl: string; podHost: string;
tags: string[]; tags: string[];
thumbnailPath: string; thumbnailPath: string;
private static createByString(author: string, podUrl: string) { private static createByString(author: string, podHost: string) {
let [ host, port ] = podUrl.replace(/^https?:\/\//, '').split(':'); return author + '@' + podHost;
if (port === '80' || port === '443') {
port = '';
} else {
port = ':' + port;
}
return author + '@' + host + port;
} }
private static createDurationString(duration: number) { private static createDurationString(duration: number) {
@ -43,7 +34,7 @@ export class Video {
isLocal: boolean, isLocal: boolean,
magnetUri: string, magnetUri: string,
name: string, name: string,
podUrl: string, podHost: string,
tags: string[], tags: string[],
thumbnailPath: string thumbnailPath: string
}) { }) {
@ -55,11 +46,11 @@ export class Video {
this.isLocal = hash.isLocal; this.isLocal = hash.isLocal;
this.magnetUri = hash.magnetUri; this.magnetUri = hash.magnetUri;
this.name = hash.name; this.name = hash.name;
this.podUrl = hash.podUrl; this.podHost = hash.podHost;
this.tags = hash.tags; this.tags = hash.tags;
this.thumbnailPath = hash.thumbnailPath; this.thumbnailPath = hash.thumbnailPath;
this.by = Video.createByString(hash.author, hash.podUrl); this.by = Video.createByString(hash.author, hash.podHost);
} }
isRemovableBy(user) { isRemovableBy(user) {

View File

@ -2,7 +2,7 @@
<div class="alert alert-danger"> <div class="alert alert-danger">
The video load seems to be abnormally long. The video load seems to be abnormally long.
<ul> <ul>
<li>Maybe the server {{ video.podUrl }} is down :(</li> <li>Maybe the server {{ video.podHost }} is down :(</li>
<li> <li>
If not, you can report an issue on If not, you can report an issue on
<a href="https://github.com/Chocobozzz/PeerTube/issues" title="Report an issue"> <a href="https://github.com/Chocobozzz/PeerTube/issues" title="Report an issue">

View File

@ -20,14 +20,14 @@ const Pod = mongoose.model('Pod')
router.get('/', listPods) router.get('/', listPods)
router.post('/', router.post('/',
validators.podsAdd, validators.podsAdd,
podsMiddleware.setBodyUrlPort, podsMiddleware.setBodyHostPort,
addPods addPods
) )
router.post('/makefriends', router.post('/makefriends',
oAuth.authenticate, oAuth.authenticate,
admin.ensureIsAdmin, admin.ensureIsAdmin,
validators.makeFriends, validators.makeFriends,
podsMiddleware.setBodyUrlsPort, podsMiddleware.setBodyHostsPort,
makeFriends makeFriends
) )
router.get('/quitfriends', router.get('/quitfriends',
@ -84,17 +84,17 @@ function addPods (req, res, next) {
} }
function listPods (req, res, next) { function listPods (req, res, next) {
Pod.list(function (err, podsUrlList) { Pod.list(function (err, podsList) {
if (err) return next(err) if (err) return next(err)
res.json(getFormatedPods(podsUrlList)) res.json(getFormatedPods(podsList))
}) })
} }
function makeFriends (req, res, next) { function makeFriends (req, res, next) {
const urls = req.body.urls const hosts = req.body.hosts
friends.makeFriends(urls, function (err) { friends.makeFriends(hosts, function (err) {
if (err) { if (err) {
logger.error('Could not make friends.', { error: err }) logger.error('Could not make friends.', { error: err })
return return
@ -107,11 +107,11 @@ function makeFriends (req, res, next) {
} }
function removePods (req, res, next) { function removePods (req, res, next) {
const url = req.body.signature.url const host = req.body.signature.host
waterfall([ waterfall([
function loadPod (callback) { function loadPod (callback) {
Pod.loadByUrl(url, callback) Pod.loadByHost(host, callback)
}, },
function removePod (pod, callback) { function removePod (pod, callback) {

View File

@ -30,7 +30,7 @@ module.exports = router
function remoteVideos (req, res, next) { function remoteVideos (req, res, next) {
const requests = req.body.data const requests = req.body.data
const fromUrl = req.body.signature.url const fromHost = req.body.signature.host
// We need to process in the same order to keep consistency // We need to process in the same order to keep consistency
// TODO: optimization // TODO: optimization
@ -40,7 +40,7 @@ function remoteVideos (req, res, next) {
if (request.type === 'add') { if (request.type === 'add') {
addRemoteVideo(videoData, callbackEach) addRemoteVideo(videoData, callbackEach)
} else if (request.type === 'remove') { } else if (request.type === 'remove') {
removeRemoteVideo(videoData, fromUrl, callbackEach) removeRemoteVideo(videoData, fromHost, callbackEach)
} else { } else {
logger.error('Unkown remote request type %s.', request.type) logger.error('Unkown remote request type %s.', request.type)
} }
@ -62,16 +62,16 @@ function addRemoteVideo (videoToCreateData, callback) {
video.save(callback) video.save(callback)
} }
function removeRemoteVideo (videoToRemoveData, fromUrl, callback) { function removeRemoteVideo (videoToRemoveData, fromHost, callback) {
// We need the list because we have to remove some other stuffs (thumbnail etc) // We need the list because we have to remove some other stuffs (thumbnail etc)
Video.listByUrlAndRemoteId(fromUrl, videoToRemoveData.remoteId, function (err, videosList) { Video.listByHostAndRemoteId(fromHost, videoToRemoveData.remoteId, function (err, videosList) {
if (err) { if (err) {
logger.error('Cannot list videos from url and magnets.', { error: err }) logger.error('Cannot list videos from host and magnets.', { error: err })
return callback(err) return callback(err)
} }
if (videosList.length === 0) { if (videosList.length === 0) {
logger.error('No remote video was found for this pod.', { magnetUri: videoToRemoveData.magnetUri, podUrl: fromUrl }) logger.error('No remote video was found for this pod.', { magnetUri: videoToRemoveData.magnetUri, podHost: fromHost })
} }
each(videosList, function (video, callbackEach) { each(videosList, function (video, callbackEach) {

View File

@ -5,14 +5,14 @@ const validator = require('express-validator').validator
const miscValidators = require('./misc') const miscValidators = require('./misc')
const podsValidators = { const podsValidators = {
isEachUniqueUrlValid isEachUniqueHostValid
} }
function isEachUniqueUrlValid (urls) { function isEachUniqueHostValid (hosts) {
return miscValidators.isArray(urls) && return miscValidators.isArray(hosts) &&
urls.length !== 0 && hosts.length !== 0 &&
urls.every(function (url) { hosts.every(function (host) {
return validator.isURL(url) && urls.indexOf(url) === urls.lastIndexOf(url) return validator.isURL(host) && host.split('://').length === 1 && hosts.indexOf(host) === hosts.lastIndexOf(host)
}) })
} }

View File

@ -15,7 +15,7 @@ const videosValidators = {
isVideoDurationValid, isVideoDurationValid,
isVideoMagnetValid, isVideoMagnetValid,
isVideoNameValid, isVideoNameValid,
isVideoPodUrlValid, isVideoPodHostValid,
isVideoTagsValid, isVideoTagsValid,
isVideoThumbnailValid, isVideoThumbnailValid,
isVideoThumbnail64Valid isVideoThumbnail64Valid
@ -33,7 +33,7 @@ function isEachRemoteVideosValid (requests) {
isVideoDurationValid(video.duration) && isVideoDurationValid(video.duration) &&
isVideoMagnetValid(video.magnet) && isVideoMagnetValid(video.magnet) &&
isVideoNameValid(video.name) && isVideoNameValid(video.name) &&
isVideoPodUrlValid(video.podUrl) && isVideoPodHostValid(video.podHost) &&
isVideoTagsValid(video.tags) && isVideoTagsValid(video.tags) &&
isVideoThumbnail64Valid(video.thumbnailBase64) && isVideoThumbnail64Valid(video.thumbnailBase64) &&
isVideoRemoteIdValid(video.remoteId) isVideoRemoteIdValid(video.remoteId)
@ -70,7 +70,7 @@ function isVideoNameValid (value) {
return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME) return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
} }
function isVideoPodUrlValid (value) { function isVideoPodHostValid (value) {
// TODO: set options (TLD...) // TODO: set options (TLD...)
return validator.isURL(value) return validator.isURL(value)
} }

View File

@ -25,7 +25,7 @@ function makeRetryRequest (params, callback) {
function makeSecureRequest (params, callback) { function makeSecureRequest (params, callback) {
const requestParams = { const requestParams = {
url: params.toPod.url + params.path url: constants.REMOTE_SCHEME.HTTP + '://' + params.toPod.host + params.path
} }
// Add data with POST requst ? // Add data with POST requst ?
@ -34,9 +34,11 @@ function makeSecureRequest (params, callback) {
// Add signature if it is specified in the params // Add signature if it is specified in the params
if (params.sign === true) { if (params.sign === true) {
const host = constants.CONFIG.WEBSERVER.HOST
requestParams.json.signature = { requestParams.json.signature = {
url: constants.CONFIG.WEBSERVER.URL, host,
signature: peertubeCrypto.sign(constants.CONFIG.WEBSERVER.URL) signature: peertubeCrypto.sign(host)
} }
} }

View File

@ -14,7 +14,7 @@ const PAGINATION_COUNT_DEFAULT = 15
// Sortable columns per schema // Sortable columns per schema
const SEARCHABLE_COLUMNS = { const SEARCHABLE_COLUMNS = {
VIDEOS: [ 'name', 'magnetUri', 'podUrl', 'author', 'tags' ] VIDEOS: [ 'name', 'magnetUri', 'podHost', 'author', 'tags' ]
} }
// Sortable columns per schema // Sortable columns per schema
@ -55,6 +55,7 @@ const CONFIG = {
} }
} }
CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -6,7 +6,6 @@ const eachSeries = require('async/eachSeries')
const fs = require('fs') const fs = require('fs')
const mongoose = require('mongoose') const mongoose = require('mongoose')
const request = require('request') const request = require('request')
const urlUtil = require('url')
const waterfall = require('async/waterfall') const waterfall = require('async/waterfall')
const constants = require('../initializers/constants') const constants = require('../initializers/constants')
@ -44,7 +43,7 @@ function getMyCertificate (callback) {
fs.readFile(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.pub', 'utf8', callback) fs.readFile(constants.CONFIG.STORAGE.CERT_DIR + 'peertube.pub', 'utf8', callback)
} }
function makeFriends (urls, callback) { function makeFriends (hosts, callback) {
const podsScore = {} const podsScore = {}
logger.info('Make friends!') logger.info('Make friends!')
@ -54,13 +53,13 @@ function makeFriends (urls, callback) {
return callback(err) return callback(err)
} }
eachSeries(urls, function (url, callbackEach) { eachSeries(hosts, function (host, callbackEach) {
computeForeignPodsList(url, podsScore, callbackEach) computeForeignPodsList(host, podsScore, callbackEach)
}, function (err) { }, function (err) {
if (err) return callback(err) if (err) return callback(err)
logger.debug('Pods scores computed.', { podsScore: podsScore }) logger.debug('Pods scores computed.', { podsScore: podsScore })
const podsList = computeWinningPods(urls, podsScore) const podsList = computeWinningPods(hosts, podsScore)
logger.debug('Pods that we keep.', { podsToKeep: podsList }) logger.debug('Pods that we keep.', { podsToKeep: podsList })
makeRequestsToWinningPods(cert, podsList, callback) makeRequestsToWinningPods(cert, podsList, callback)
@ -149,45 +148,45 @@ module.exports = friends
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function computeForeignPodsList (url, podsScore, callback) { function computeForeignPodsList (host, podsScore, callback) {
getForeignPodsList(url, function (err, foreignPodsList) { getForeignPodsList(host, function (err, foreignPodsList) {
if (err) return callback(err) if (err) return callback(err)
if (!foreignPodsList) foreignPodsList = [] if (!foreignPodsList) foreignPodsList = []
// Let's give 1 point to the pod we ask the friends list // Let's give 1 point to the pod we ask the friends list
foreignPodsList.push({ url: url }) foreignPodsList.push({ host })
foreignPodsList.forEach(function (foreignPod) { foreignPodsList.forEach(function (foreignPod) {
const foreignPodUrl = foreignPod.url const foreignPodHost = foreignPod.host
if (podsScore[foreignPodUrl]) podsScore[foreignPodUrl]++ if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++
else podsScore[foreignPodUrl] = 1 else podsScore[foreignPodHost] = 1
}) })
callback() callback()
}) })
} }
function computeWinningPods (urls, podsScore) { function computeWinningPods (hosts, podsScore) {
// Build the list of pods to add // Build the list of pods to add
// Only add a pod if it exists in more than a half base pods // Only add a pod if it exists in more than a half base pods
const podsList = [] const podsList = []
const baseScore = urls.length / 2 const baseScore = hosts.length / 2
Object.keys(podsScore).forEach(function (podUrl) { Object.keys(podsScore).forEach(function (podHost) {
// If the pod is not me and with a good score we add it // If the pod is not me and with a good score we add it
if (isMe(podUrl) === false && podsScore[podUrl] > baseScore) { if (isMe(podHost) === false && podsScore[podHost] > baseScore) {
podsList.push({ url: podUrl }) podsList.push({ host: podHost })
} }
}) })
return podsList return podsList
} }
function getForeignPodsList (url, callback) { function getForeignPodsList (host, callback) {
const path = '/api/' + constants.API_VERSION + '/pods' const path = '/api/' + constants.API_VERSION + '/pods'
request.get(url + path, function (err, response, body) { request.get(constants.REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) {
if (err) return callback(err) if (err) return callback(err)
try { try {
@ -207,26 +206,26 @@ function makeRequestsToWinningPods (cert, podsList, callback) {
eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) { eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) {
const params = { const params = {
url: pod.url + '/api/' + constants.API_VERSION + '/pods/', url: constants.REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + constants.API_VERSION + '/pods/',
method: 'POST', method: 'POST',
json: { json: {
url: constants.CONFIG.WEBSERVER.URL, host: constants.CONFIG.WEBSERVER.HOST,
publicKey: cert publicKey: cert
} }
} }
requests.makeRetryRequest(params, function (err, res, body) { requests.makeRetryRequest(params, function (err, res, body) {
if (err) { if (err) {
logger.error('Error with adding %s pod.', pod.url, { error: err }) logger.error('Error with adding %s pod.', pod.host, { error: err })
// Don't break the process // Don't break the process
return callbackEach() return callbackEach()
} }
if (res.statusCode === 200) { if (res.statusCode === 200) {
const podObj = new Pod({ url: pod.url, publicKey: body.cert }) const podObj = new Pod({ host: pod.host, publicKey: body.cert })
podObj.save(function (err, podCreated) { podObj.save(function (err, podCreated) {
if (err) { if (err) {
logger.error('Cannot add friend %s pod.', pod.url, { error: err }) logger.error('Cannot add friend %s pod.', pod.host, { error: err })
return callbackEach() return callbackEach()
} }
@ -236,7 +235,7 @@ function makeRequestsToWinningPods (cert, podsList, callback) {
return callbackEach() return callbackEach()
}) })
} else { } else {
logger.error('Status not 200 for %s pod.', pod.url) logger.error('Status not 200 for %s pod.', pod.host)
return callbackEach() return callbackEach()
} }
}) })
@ -268,14 +267,6 @@ function createRequest (type, endpoint, data, to) {
}) })
} }
function isMe (url) { function isMe (host) {
const parsedUrl = urlUtil.parse(url) return host === constants.CONFIG.WEBSERVER.HOST
const hostname = parsedUrl.hostname
const port = parseInt(parsedUrl.port)
const myHostname = constants.CONFIG.WEBSERVER.HOSTNAME
const myPort = constants.CONFIG.WEBSERVER.PORT
return hostname === myHostname && port === myPort
} }

View File

@ -1,38 +1,36 @@
'use strict' 'use strict'
const urlModule = require('url') const constants = require('../initializers/constants')
const logger = require('../helpers/logger')
const podsMiddleware = { const podsMiddleware = {
setBodyUrlsPort, setBodyHostsPort,
setBodyUrlPort setBodyHostPort
} }
function setBodyUrlsPort (req, res, next) { function setBodyHostsPort (req, res, next) {
for (let i = 0; i < req.body.urls.length; i++) { for (let i = 0; i < req.body.hosts.length; i++) {
const urlWithPort = getUrlWithPort(req.body.urls[i]) const hostWithPort = getHostWithPort(req.body.hosts[i])
// Problem with the url parsing? // Problem with the url parsing?
if (urlWithPort === null) { if (hostWithPort === null) {
return res.sendStatus(500) return res.sendStatus(500)
} }
req.body.urls[i] = urlWithPort req.body.hosts[i] = hostWithPort
} }
return next() return next()
} }
function setBodyUrlPort (req, res, next) { function setBodyHostPort (req, res, next) {
const urlWithPort = getUrlWithPort(req.body.url) const hostWithPort = getHostWithPort(req.body.host)
// Problem with the url parsing? // Problem with the url parsing?
if (urlWithPort === null) { if (hostWithPort === null) {
return res.sendStatus(500) return res.sendStatus(500)
} }
req.body.url = urlWithPort req.body.host = hostWithPort
return next() return next()
} }
@ -43,20 +41,16 @@ module.exports = podsMiddleware
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function getUrlWithPort (url) { function getHostWithPort (host) {
const urlObj = urlModule.parse(url) const splitted = host.split(':')
// Add the port if it is not specified console.log(splitted)
if (urlObj.port === null) { // The port was not specified
if (urlObj.protocol === 'http:') { if (splitted.length === 1) {
return url + ':80' if (constants.REMOTE_SCHEME.HTTP === 'https') return host + ':443'
} else if (urlObj.protocol === 'https:') {
return url + ':443' return host + ':80'
} else {
logger.error('Unknown url protocol: ' + urlObj.protocol)
return null
}
} }
return url return host
} }

View File

@ -12,27 +12,27 @@ const secureMiddleware = {
} }
function checkSignature (req, res, next) { function checkSignature (req, res, next) {
const url = req.body.signature.url const host = req.body.signature.host
Pod.loadByUrl(url, function (err, pod) { Pod.loadByHost(host, function (err, pod) {
if (err) { if (err) {
logger.error('Cannot get signed url in decryptBody.', { error: err }) logger.error('Cannot get signed host in decryptBody.', { error: err })
return res.sendStatus(500) return res.sendStatus(500)
} }
if (pod === null) { if (pod === null) {
logger.error('Unknown pod %s.', url) logger.error('Unknown pod %s.', host)
return res.sendStatus(403) return res.sendStatus(403)
} }
logger.debug('Decrypting body from %s.', url) logger.debug('Decrypting body from %s.', host)
const signatureOk = peertubeCrypto.checkSignature(pod.publicKey, url, req.body.signature.signature) const signatureOk = peertubeCrypto.checkSignature(pod.publicKey, host, req.body.signature.signature)
if (signatureOk === true) { if (signatureOk === true) {
return next() return next()
} }
logger.error('Signature is not okay in decryptBody for %s.', req.body.signature.url) logger.error('Signature is not okay in decryptBody for %s.', req.body.signature.host)
return res.sendStatus(403) return res.sendStatus(403)
}) })
} }

View File

@ -10,7 +10,7 @@ const validatorsPod = {
} }
function makeFriends (req, res, next) { function makeFriends (req, res, next) {
req.checkBody('urls', 'Should have an array of unique urls').isEachUniqueUrlValid() req.checkBody('hosts', 'Should have an array of unique hosts').isEachUniqueHostValid()
logger.debug('Checking makeFriends parameters', { parameters: req.body }) logger.debug('Checking makeFriends parameters', { parameters: req.body })
@ -32,7 +32,7 @@ function makeFriends (req, res, next) {
} }
function podsAdd (req, res, next) { function podsAdd (req, res, next) {
req.checkBody('url', 'Should have an url').notEmpty().isURL({ require_protocol: true }) req.checkBody('host', 'Should have an host').notEmpty().isURL()
req.checkBody('publicKey', 'Should have a public key').notEmpty() req.checkBody('publicKey', 'Should have a public key').notEmpty()
// TODO: check we don't have it already // TODO: check we don't have it already

View File

@ -27,10 +27,10 @@ function remoteVideos (req, res, next) {
} }
function signature (req, res, next) { function signature (req, res, next) {
req.checkBody('signature.url', 'Should have a signature url').isURL() req.checkBody('signature.host', 'Should have a signature host').isURL()
req.checkBody('signature.signature', 'Should have a signature').notEmpty() req.checkBody('signature.signature', 'Should have a signature').notEmpty()
logger.debug('Checking signature parameters', { parameters: { signatureUrl: req.body.signature.url } }) logger.debug('Checking signature parameters', { parameters: { signatureHost: req.body.signature.host } })
checkErrors(req, res, next) checkErrors(req, res, next)
} }

View File

@ -12,7 +12,7 @@ const Video = mongoose.model('Video')
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const PodSchema = mongoose.Schema({ const PodSchema = mongoose.Schema({
url: String, host: String,
publicKey: String, publicKey: String,
score: { type: Number, max: constants.FRIEND_SCORE.MAX }, score: { type: Number, max: constants.FRIEND_SCORE.MAX },
createdDate: { createdDate: {
@ -21,8 +21,7 @@ const PodSchema = mongoose.Schema({
} }
}) })
// TODO: set options (TLD...) PodSchema.path('host').validate(validator.isURL)
PodSchema.path('url').validate(validator.isURL)
PodSchema.path('publicKey').required(true) PodSchema.path('publicKey').required(true)
PodSchema.path('score').validate(function (value) { return !isNaN(value) }) PodSchema.path('score').validate(function (value) { return !isNaN(value) })
@ -37,14 +36,14 @@ PodSchema.statics = {
listAllIds, listAllIds,
listBadPods, listBadPods,
load, load,
loadByUrl, loadByHost,
removeAll removeAll
} }
PodSchema.pre('save', function (next) { PodSchema.pre('save', function (next) {
const self = this const self = this
Pod.loadByUrl(this.url, function (err, pod) { Pod.loadByHost(this.host, function (err, pod) {
if (err) return next(err) if (err) return next(err)
if (pod) return next(new Error('Pod already exists.')) if (pod) return next(new Error('Pod already exists.'))
@ -56,7 +55,7 @@ PodSchema.pre('save', function (next) {
PodSchema.pre('remove', function (next) { PodSchema.pre('remove', function (next) {
// Remove the videos owned by this pod too // Remove the videos owned by this pod too
Video.listByUrl(this.url, function (err, videos) { Video.listByHost(this.host, function (err, videos) {
if (err) return next(err) if (err) return next(err)
each(videos, function (video, callbackEach) { each(videos, function (video, callbackEach) {
@ -72,7 +71,7 @@ const Pod = mongoose.model('Pod', PodSchema)
function toFormatedJSON () { function toFormatedJSON () {
const json = { const json = {
id: this._id, id: this._id,
url: this.url, host: this.host,
score: this.score, score: this.score,
createdDate: this.createdDate createdDate: this.createdDate
} }
@ -111,8 +110,8 @@ function load (id, callback) {
return this.findById(id, callback) return this.findById(id, callback)
} }
function loadByUrl (url, callback) { function loadByHost (host, callback) {
return this.findOne({ url: url }, callback) return this.findOne({ host }, callback)
} }
function removeAll (callback) { function removeAll (callback) {

View File

@ -121,7 +121,7 @@ function makeRequest (toPod, requestEndpoint, requestsToMake, callback) {
if (err || (res.statusCode !== 200 && res.statusCode !== 201 && res.statusCode !== 204)) { if (err || (res.statusCode !== 200 && res.statusCode !== 201 && res.statusCode !== 204)) {
logger.error( logger.error(
'Error sending secure request to %s pod.', 'Error sending secure request to %s pod.',
toPod.url, toPod.host,
{ {
error: err || new Error('Status code not 20x : ' + res.statusCode) error: err || new Error('Status code not 20x : ' + res.statusCode)
} }

View File

@ -28,10 +28,9 @@ const VideoSchema = mongoose.Schema({
magnet: { magnet: {
infoHash: String infoHash: String
}, },
podUrl: String, podHost: String,
author: String, author: String,
duration: Number, duration: Number,
thumbnail: String,
tags: [ String ], tags: [ String ],
createdDate: { createdDate: {
type: Date, type: Date,
@ -41,14 +40,9 @@ const VideoSchema = mongoose.Schema({
VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid) VideoSchema.path('name').validate(customVideosValidators.isVideoNameValid)
VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid) VideoSchema.path('description').validate(customVideosValidators.isVideoDescriptionValid)
VideoSchema.path('podUrl').validate(customVideosValidators.isVideoPodUrlValid) VideoSchema.path('podHost').validate(customVideosValidators.isVideoPodHostValid)
VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid) VideoSchema.path('author').validate(customVideosValidators.isVideoAuthorValid)
VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid) VideoSchema.path('duration').validate(customVideosValidators.isVideoDurationValid)
// The tumbnail can be the path or the data in base 64
// The pre save hook will convert the base 64 data in a file on disk and replace the thumbnail key by the filename
VideoSchema.path('thumbnail').validate(function (value) {
return customVideosValidators.isVideoThumbnailValid(value) || customVideosValidators.isVideoThumbnail64Valid(value)
})
VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid) VideoSchema.path('tags').validate(customVideosValidators.isVideoTagsValid)
VideoSchema.methods = { VideoSchema.methods = {
@ -65,8 +59,8 @@ VideoSchema.methods = {
VideoSchema.statics = { VideoSchema.statics = {
getDurationFromFile, getDurationFromFile,
listForApi, listForApi,
listByUrlAndRemoteId, listByHostAndRemoteId,
listByUrl, listByHost,
listOwned, listOwned,
listOwnedByAuthor, listOwnedByAuthor,
listRemotes, listRemotes,
@ -107,7 +101,7 @@ VideoSchema.pre('save', function (next) {
if (video.isOwned()) { if (video.isOwned()) {
const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename()) const videoPath = pathUtils.join(constants.CONFIG.STORAGE.VIDEOS_DIR, video.getVideoFilename())
this.podUrl = constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT this.podHost = constants.CONFIG.WEBSERVER.HOST
tasks.push( tasks.push(
// TODO: refractoring // TODO: refractoring
@ -160,8 +154,8 @@ function generateMagnetUri () {
baseUrlHttp = constants.CONFIG.WEBSERVER.URL baseUrlHttp = constants.CONFIG.WEBSERVER.URL
baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT baseUrlWs = constants.CONFIG.WEBSERVER.WS + '://' + constants.CONFIG.WEBSERVER.HOSTNAME + ':' + constants.CONFIG.WEBSERVER.PORT
} else { } else {
baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podUrl baseUrlHttp = constants.REMOTE_SCHEME.HTTP + '://' + this.podHost
baseUrlWs = constants.REMOTE_SCHEME.WS + this.podUrl baseUrlWs = constants.REMOTE_SCHEME.WS + this.podHost
} }
const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName() const xs = baseUrlHttp + constants.STATIC_PATHS.TORRENTS + this.getTorrentName()
@ -215,7 +209,7 @@ function toFormatedJSON () {
id: this._id, id: this._id,
name: this.name, name: this.name,
description: this.description, description: this.description,
podUrl: this.podUrl, podHost: this.podHost,
isLocal: this.isOwned(), isLocal: this.isOwned(),
magnetUri: this.generateMagnetUri(), magnetUri: this.generateMagnetUri(),
author: this.author, author: this.author,
@ -249,7 +243,7 @@ function toRemoteJSON (callback) {
thumbnailBase64: new Buffer(thumbnailData).toString('base64'), thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
tags: self.tags, tags: self.tags,
createdDate: self.createdDate, createdDate: self.createdDate,
podUrl: self.podUrl podHost: self.podHost
} }
return callback(null, remoteVideo) return callback(null, remoteVideo)
@ -271,12 +265,12 @@ function listForApi (start, count, sort, callback) {
return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback) return modelUtils.listForApiWithCount.call(this, query, start, count, sort, callback)
} }
function listByUrlAndRemoteId (fromUrl, remoteId, callback) { function listByHostAndRemoteId (fromHost, remoteId, callback) {
this.find({ podUrl: fromUrl, remoteId: remoteId }, callback) this.find({ podHost: fromHost, remoteId: remoteId }, callback)
} }
function listByUrl (fromUrl, callback) { function listByHost (fromHost, callback) {
this.find({ podUrl: fromUrl }, callback) this.find({ podHost: fromHost }, callback)
} }
function listOwned (callback) { function listOwned (callback) {