mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-12 15:59:02 -05:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad8c266f76 | ||
|
|
807c3bdcc7 | ||
|
|
62efbd17de | ||
|
|
d99350fd61 | ||
|
|
40c70a9a2a | ||
|
|
8fb7f40a6a | ||
|
|
2f12d41d9d | ||
|
|
9fbdb6b305 | ||
|
|
73285cadb6 | ||
|
|
56f1c295b6 | ||
|
|
7f76ed8413 | ||
|
|
f7edd36931 | ||
|
|
c39f2b7c05 | ||
|
|
342036408e | ||
|
|
f4904fce17 | ||
|
|
2abb2de753 | ||
|
|
ef0a0db07e | ||
|
|
a45795efec | ||
|
|
88ae353aef | ||
|
|
97b9690711 | ||
|
|
48ce356d5c | ||
|
|
92451f94bc | ||
|
|
593632045d | ||
|
|
e3c55ef307 | ||
|
|
edf1730fd2 | ||
|
|
34cd8e3f95 | ||
|
|
242cce022a |
1
AUTHORS
1
AUTHORS
@@ -66,6 +66,7 @@ Tim Abell <tim@timwise.co.uk>
|
||||
Tobias Nygren <tnn@nygren.pp.se>
|
||||
Tomas Cerveny <kozec@kozec.com>
|
||||
Tully Robinson <tully@tojr.org>
|
||||
Tyler Brazier <tyler@tylerbrazier.com>
|
||||
Veeti Paananen <veeti.paananen@rojekti.fi>
|
||||
Vil Brekin <vilbrekin@gmail.com>
|
||||
Yannic A. <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>
|
||||
|
||||
1
NICKS
1
NICKS
@@ -65,6 +65,7 @@ simplypeachy <aD@simplypeachy.co.uk> <simplypeachy@users.noreply.github.com>
|
||||
timabell <tim@timwise.co.uk>
|
||||
tnn2 <tnn@nygren.pp.se>
|
||||
tojrobinson <tully@tojr.org>
|
||||
tylerbrazier <tyler@tylerbrazier.com>
|
||||
uok <ueomkail@gmail.com> <uok@users.noreply.github.com>
|
||||
veeti <veeti.paananen@rojekti.fi>
|
||||
wsgcsysadmin <e.meitner@willystreet.coo>
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
"github.com/syncthing/syncthing/lib/events"
|
||||
"github.com/syncthing/syncthing/lib/sync"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@@ -24,6 +25,13 @@ var (
|
||||
sessionsMut = sync.NewMutex()
|
||||
)
|
||||
|
||||
func emitLoginAttempt(success bool, username string) {
|
||||
events.Default.Log(events.LoginAttempt, map[string]interface{}{
|
||||
"success": success,
|
||||
"username": username,
|
||||
})
|
||||
}
|
||||
|
||||
func basicAuthAndSessionMiddleware(cookieName string, cfg config.GUIConfiguration, next http.Handler) http.Handler {
|
||||
apiKey := cfg.APIKey()
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -70,12 +78,15 @@ func basicAuthAndSessionMiddleware(cookieName string, cfg config.GUIConfiguratio
|
||||
return
|
||||
}
|
||||
|
||||
if string(fields[0]) != cfg.User {
|
||||
username := string(fields[0])
|
||||
if username != cfg.User {
|
||||
emitLoginAttempt(false, username)
|
||||
error()
|
||||
return
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(cfg.Password), fields[1]); err != nil {
|
||||
emitLoginAttempt(false, username)
|
||||
error()
|
||||
return
|
||||
}
|
||||
@@ -90,6 +101,7 @@ func basicAuthAndSessionMiddleware(cookieName string, cfg config.GUIConfiguratio
|
||||
MaxAge: 0,
|
||||
})
|
||||
|
||||
emitLoginAttempt(true, username)
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func reportData(cfg *config.Wrapper, m *model.Model) map[string]interface{} {
|
||||
|
||||
defaultAnnounceServersDNS, defaultAnnounceServersIP, otherAnnounceServers := 0, 0, 0
|
||||
for _, addr := range cfg.Options().GlobalAnnServers {
|
||||
if addr == "default" {
|
||||
if addr == "default" || addr == "default-v4" || addr == "default-v6" {
|
||||
defaultAnnounceServersDNS++
|
||||
} else if stringIn(addr, config.DefaultDiscoveryServersIP) {
|
||||
defaultAnnounceServersIP++
|
||||
|
||||
@@ -149,6 +149,16 @@ func (s *verboseSvc) formatEvent(ev events.Event) string {
|
||||
data := ev.Data.(map[string][]string)
|
||||
newRelays := data["new"]
|
||||
return fmt.Sprintf("Relay state changed; connected relay(s) are %s.", strings.Join(newRelays, ", "))
|
||||
case events.LoginAttempt:
|
||||
data := ev.Data.(map[string]interface{})
|
||||
username := data["username"].(string)
|
||||
var success string
|
||||
if data["success"].(bool) {
|
||||
success = "successful"
|
||||
} else {
|
||||
success = "failed"
|
||||
}
|
||||
return fmt.Sprintf("Login %s for username %s.", success, username)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"Add new folder?": "¿Añadir una nueva carpeta?",
|
||||
"Address": "Dirección",
|
||||
"Addresses": "Direcciones",
|
||||
"Advanced": "Advanced",
|
||||
"Advanced Configuration": "Advanced Configuration",
|
||||
"Advanced": "Avanzado",
|
||||
"Advanced Configuration": "Configuración Avanzada",
|
||||
"All Data": "Todos los datos",
|
||||
"Allow Anonymous Usage Reporting?": "¿Deseas permitir el envío anónimo de informes de uso?",
|
||||
"Alphabetic": "Alfabético",
|
||||
@@ -19,7 +19,7 @@
|
||||
"Anonymous Usage Reporting": "Informe anónimo de uso",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Cualquier dispositivo configurado en un dispositivo de introducción será añadido también.",
|
||||
"Automatic upgrades": "Actualizaciones automáticas",
|
||||
"Be careful!": "Be careful!",
|
||||
"Be careful!": "¡Ten cuidado!",
|
||||
"Bugs": "Errores (bugs)",
|
||||
"CPU Utilization": "Uso de CPU",
|
||||
"Changelog": "Informe de cambios",
|
||||
@@ -40,7 +40,7 @@
|
||||
"Device {%device%} ({%address%}) wants to connect. Add new device?": "El dispositivo {{device}} ({{address}}) quiere conectarse. ¿Añadir nuevo dispositivo?",
|
||||
"Devices": "Dispositivos",
|
||||
"Disconnected": "Desconectado",
|
||||
"Discovery": "Discovery",
|
||||
"Discovery": "Descubrimiento",
|
||||
"Documentation": "Documentación",
|
||||
"Download Rate": "Velocidad de descarga",
|
||||
"Downloaded": "Descargado",
|
||||
@@ -50,18 +50,18 @@
|
||||
"Edit Folder": "Editar repositorio",
|
||||
"Editing": "Editando",
|
||||
"Enable UPnP": "Habilitar UPnP",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Introduzca separados por comas (\"tcp://ip:port\", \"tcp://host:port\") direcciones o \"dynamic\" para llevar a cabo la detección automática de la dirección.",
|
||||
"Enter ignore patterns, one per line.": "Introducir patrones a ignorar, uno por línea.",
|
||||
"Error": "Error",
|
||||
"External File Versioning": "Versionado externo de fichero",
|
||||
"Failed Items": "Failed Items",
|
||||
"Failed Items": "Elementos fallidos",
|
||||
"File Pull Order": "Orden de ficheros del pull",
|
||||
"File Versioning": "Versionado de ficheros",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Los bits de permiso de ficheros son ignorados cuando se buscan cambios. Utilizar en sistemas de ficheros FAT.",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Los archivos serán movidos a la carpeta .stversions cuando sean reemplazados o borrados por Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Los ficheros son cambiados a versiones con indicación de fecha en una carpeta \".stversions\" cuando son reemplazados o borrados por Syncthing.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Los ficheros son protegidos por los cambios hechos en otros dispositivos, pero los cambios hechos en este dispositivo serán enviados al resto del grupo (cluster).",
|
||||
"Folder": "Folder",
|
||||
"Folder": "Carpeta",
|
||||
"Folder ID": "ID de carpeta",
|
||||
"Folder Master": "Carpeta principal",
|
||||
"Folder Path": "Ruta de la carpeta",
|
||||
@@ -75,12 +75,12 @@
|
||||
"Global Discovery Server": "Servidor de descubrimiento global",
|
||||
"Global State": "Estado global",
|
||||
"Help": "Ayuda",
|
||||
"Home page": "Home page",
|
||||
"Home page": "Página de inicio",
|
||||
"Ignore": "Ignorar",
|
||||
"Ignore Patterns": "Patrones a ignorar",
|
||||
"Ignore Permissions": "Permisos a ignorar",
|
||||
"Incoming Rate Limit (KiB/s)": "Límite de descarga (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Incorrect configuration may damage your folder contents and render Syncthing inoperable.",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Una configuración incorrecta puede dañar los contenidos de la carpeta y hacer que Syncthing no funcione.",
|
||||
"Introducer": "Presentador",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Inversión de la condición dada (por ejemplo, \"no excluir\")",
|
||||
"Keep Versions": "Mantener versiones",
|
||||
@@ -94,7 +94,7 @@
|
||||
"Major Upgrade": "Actualización importante",
|
||||
"Maximum Age": "Edad máxima",
|
||||
"Metadata Only": "Sólo metadatos",
|
||||
"Minimum Free Disk Space": "Minimum Free Disk Space",
|
||||
"Minimum Free Disk Space": "Espacio mínimo libre en disco",
|
||||
"Move to top of queue": "Mover al principio de la cola",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Comodín multinivel (coincide con múltiples niveles de directorio)",
|
||||
"Never": "Nunca",
|
||||
@@ -107,15 +107,15 @@
|
||||
"OK": "OK",
|
||||
"Off": "Desconectar",
|
||||
"Oldest First": "El más antiguo primero",
|
||||
"Options": "Options",
|
||||
"Out of Sync": "Out of Sync",
|
||||
"Options": "Opciones",
|
||||
"Out of Sync": "No sincronizado",
|
||||
"Out of Sync Items": "Elementos no sincronizados",
|
||||
"Outgoing Rate Limit (KiB/s)": "Límite de subida (KiB/s)",
|
||||
"Override Changes": "Anular cambios",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Ruta a la carpeta en la máquina local. Se creará si no existe. El carácter de la tilde (~) puede usarse como atajo.",
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Ruta donde se almacenarán las versiones (dejar vacío para usar la carpeta por defecto \".stversions\").",
|
||||
"Pause": "Pause",
|
||||
"Paused": "Paused",
|
||||
"Pause": "Pausar",
|
||||
"Paused": "Pausado",
|
||||
"Please consult the release notes before performing a major upgrade.": "Por favor, consultar las notas de la versión antes de realizar una actualización importante.",
|
||||
"Please wait": "Por favor, espere",
|
||||
"Preview": "Vista previa",
|
||||
@@ -123,17 +123,17 @@
|
||||
"Quick guide to supported patterns": "Guía rápida de patrones soportados",
|
||||
"RAM Utilization": "Uso de RAM",
|
||||
"Random": "Aleatorio",
|
||||
"Relayed via": "Relayed via",
|
||||
"Relays": "Relays",
|
||||
"Relayed via": "Respaldada a través",
|
||||
"Relays": "Respaldos",
|
||||
"Release Notes": "Notas de la versión",
|
||||
"Remove": "Remove",
|
||||
"Remove": "Eliminar",
|
||||
"Rescan": "Volver a buscar",
|
||||
"Rescan All": "Volver a buscar todo",
|
||||
"Rescan Interval": "Intervalo de nueva búsqueda",
|
||||
"Restart": "Reiniciar",
|
||||
"Restart Needed": "Reinicio necesario",
|
||||
"Restarting": "Reiniciando",
|
||||
"Resume": "Resume",
|
||||
"Resume": "Continuar",
|
||||
"Reused": "Reutilizado",
|
||||
"Save": "Guardar",
|
||||
"Scanning": "Rastreando",
|
||||
@@ -158,7 +158,7 @@
|
||||
"Source Code": "Código fuente",
|
||||
"Staggered File Versioning": "Versionado escalonado de fichero",
|
||||
"Start Browser": "Iniciar el navegador",
|
||||
"Statistics": "Statistics",
|
||||
"Statistics": "Estadísticas",
|
||||
"Stopped": "Detenido",
|
||||
"Support": "Soporte",
|
||||
"Sync Protocol Listen Addresses": "Direcciones de escucha del protocolo de sincronización",
|
||||
@@ -181,18 +181,18 @@
|
||||
"The folder ID must be unique.": "La ID de la carpeta debe ser única.",
|
||||
"The folder path cannot be blank.": "La ruta de la carpeta no puede estar en blanco.",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "Se utilizan los siguientes intervalos: para la primera hora se mantiene una versión cada 30 segundos, para el primer día se mantiene una versión cada hora, para los primeros 30 días se mantiene una versión diaria hasta la edad máxima de una semana.",
|
||||
"The following items could not be synchronized.": "The following items could not be synchronized.",
|
||||
"The following items could not be synchronized.": "Los siguientes artículos no pueden ser sincronizados.",
|
||||
"The maximum age must be a number and cannot be blank.": "La edad máxima debe ser un número y no puede estar vacía.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "El tiempo máximo para mantener una versión en días (introducir 0 para mantener las versiones indefinidamente).",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "El porcentaje de espacio libre mínimo debe ser un número no negativo entre 0 y 100 (ambos inclusive).",
|
||||
"The number of days must be a number and cannot be blank.": "El número de días debe ser un número y no puede estar en blanco.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "El número de días para mantener los archivos en la papelera. Cero significa \"para siempre\".",
|
||||
"The number of old versions to keep, per file.": "El número de versiones a antiguas a mantener para cada fichero.",
|
||||
"The number of versions must be a number and cannot be blank.": "El número de versiones debe ser un número y no puede estar vacío.",
|
||||
"The path cannot be blank.": "La ruta no puede estar vacía.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "The rate limit must be a non-negative number (0: no limit)",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "El límite de velocidad debe ser un número no negativo (0: sin límite)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "El intervalo de actualización debe ser un número positivo de segundos.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Se reintentarán de forma automática y se sincronizarán cuando se resuelva el error.",
|
||||
"This is a major version upgrade.": "Hay una actualización importante.",
|
||||
"Trash Can File Versioning": "Versionado de archivos de la papelera",
|
||||
"Unknown": "Desconocido",
|
||||
@@ -213,7 +213,7 @@
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Cuando añada una nueva carpeta, tenga en cuenta que su ID se usa para unir carpetas entre dispositivos. Son sensibles a las mayúsculas y deben coincidir exactamente entre todos los dispositivos.",
|
||||
"Yes": "Si",
|
||||
"You must keep at least one version.": "Debes mantener al menos una versión.",
|
||||
"days": "days",
|
||||
"days": "días",
|
||||
"full documentation": "Documentación completa",
|
||||
"items": "Elementos",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} quiere compartir la carpeta \"{{folder}}\"."
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"Device {%device%} ({%address%}) wants to connect. Add new device?": "El dispositivo {{device}} ({{address}}) se quiere conectar. ¿Agregar nuevo dispositivo?",
|
||||
"Devices": "Dispositivos",
|
||||
"Disconnected": "Desconectado",
|
||||
"Discovery": "Discovery",
|
||||
"Discovery": "Búsqueda",
|
||||
"Documentation": "Documentación",
|
||||
"Download Rate": "Tasa de descarga",
|
||||
"Downloaded": "Descargado",
|
||||
@@ -108,7 +108,7 @@
|
||||
"Off": "Apagado",
|
||||
"Oldest First": "Antiguo primero",
|
||||
"Options": "Opciones",
|
||||
"Out of Sync": "Out of Sync",
|
||||
"Out of Sync": "Fuera de sincronización",
|
||||
"Out of Sync Items": "Ítems no sincronizados",
|
||||
"Outgoing Rate Limit (KiB/s)": "Tasa máxima de envío (KiB/s)",
|
||||
"Override Changes": "Reemplazar los cambios",
|
||||
@@ -124,7 +124,7 @@
|
||||
"RAM Utilization": "Utilización de RAM",
|
||||
"Random": "Aleatorio",
|
||||
"Relayed via": "retransmitida vía",
|
||||
"Relays": "Relés",
|
||||
"Relays": "Retransmisores",
|
||||
"Release Notes": "Notas de lanzamiento",
|
||||
"Remove": "Eliminar",
|
||||
"Rescan": "Reescanear",
|
||||
@@ -133,7 +133,7 @@
|
||||
"Restart": "Reiniciar",
|
||||
"Restart Needed": "Es necesario reiniciar",
|
||||
"Restarting": "Reiniciando",
|
||||
"Resume": "Resume",
|
||||
"Resume": "Reanudar",
|
||||
"Reused": "Reutilizado",
|
||||
"Save": "Guardar",
|
||||
"Scanning": "Actualización",
|
||||
|
||||
@@ -38,9 +38,9 @@
|
||||
"Device Identification": "Identification de l'appareil",
|
||||
"Device Name": "Nom du périphérique",
|
||||
"Device {%device%} ({%address%}) wants to connect. Add new device?": "L'appareil {{device}} ({{address}}) veut se connecter. Voulez-vous ajouter cette appareil ?",
|
||||
"Devices": "Appareil",
|
||||
"Devices": "Appareils",
|
||||
"Disconnected": "Déconnecté",
|
||||
"Discovery": "Discovery",
|
||||
"Discovery": "Découverte",
|
||||
"Documentation": "Documentation",
|
||||
"Download Rate": "Débit de réception",
|
||||
"Downloaded": "Téléchargé",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"A negative number of days doesn't make sense.": "Et negativt antall dager gir ikke mening.",
|
||||
"A new major version may not be compatible with previous versions.": "En ny hovedversjon kan bli ikke-kompatibel med en eldre versjon.",
|
||||
"A new major version may not be compatible with previous versions.": "En ny hovedversjon er ikke nødvendigvis kompatibel med eldre versjoner.",
|
||||
"API Key": "API-nøkkel",
|
||||
"About": "Om",
|
||||
"Actions": "Handlinger",
|
||||
@@ -57,8 +57,8 @@
|
||||
"Failed Items": "Elementsynkronisering som har feilet",
|
||||
"File Pull Order": "Filenes Henterekkefølge",
|
||||
"File Versioning": "Versjonskontroll",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Fil bit-rettigheter ignoreres når forandringer oppdages. Bruk FAT filsystem. ",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Filer som slettes eller erstattes av Syncthing flyttes til katalogen .stversions",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Informasjon om filrettigheter ignoreres når det letes etter endringer. Bruk på FAT filsystem. ",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Filer som slettes eller erstattes av Syncthing flyttes til katalogen .stversions.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Filer flyttes til en datostemplet versjon i .stversions-katalogen når den oppdateres eller slettes av Syncthing.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer er beskyttet mot endringer som er gjort på andre enheter, men endringer som er gjort på denne enheten blir sendt til resten av gruppen.",
|
||||
"Folder": "Katalog",
|
||||
@@ -80,7 +80,7 @@
|
||||
"Ignore Patterns": "Utelatelsesmønster",
|
||||
"Ignore Permissions": "Ignorer Tilgangsbit",
|
||||
"Incoming Rate Limit (KiB/s)": "Innkommende Hastighetsbegrensning (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Feilaktige innstillinger kan skade innholdet i dine delte kataloger og hindre Syncthing i å fungere. ",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Feilaktige innstillinger kan skade innholdet i dine delte kataloger og hindre Syncthing i å fungere.",
|
||||
"Introducer": "Introduktør",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Invers av den gitte tilstanden (t.d. ikke ekskluder)",
|
||||
"Keep Versions": "Behold Versjoner",
|
||||
@@ -194,7 +194,7 @@
|
||||
"The rescan interval must be a non-negative number of seconds.": "Antall sekund i skanneintervallet kan ikke være negativt.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Disse hentes automatisk og vil synkroniseres når feilen er blitt utbedret.",
|
||||
"This is a major version upgrade.": "Dette er en hovedoppgradering",
|
||||
"Trash Can File Versioning": "Fil Versjonskontroll i papirkurven",
|
||||
"Trash Can File Versioning": "Papirkurv Versjonskontroll",
|
||||
"Unknown": "Ukjent",
|
||||
"Unshared": "Ikke delt",
|
||||
"Unused": "Ikke i bruk",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"A negative number of days doesn't make sense.": "A negative number of days doesn't make sense.",
|
||||
"A new major version may not be compatible with previous versions.": "A new major version may not be compatible with previous versions.",
|
||||
"A negative number of days doesn't make sense.": "Eit negativt tal dagar har ikkje meining.",
|
||||
"A new major version may not be compatible with previous versions.": "Ein ny hovudversjon er ikkje nødvendigvis kompatibel med eldre versjonar. ",
|
||||
"API Key": "API-nøkkel",
|
||||
"About": "Om",
|
||||
"Actions": "Handlingar",
|
||||
@@ -15,7 +15,7 @@
|
||||
"All Data": "Alle data",
|
||||
"Allow Anonymous Usage Reporting?": "Tillata anonymisert bruksrapportering?",
|
||||
"Alphabetic": "Alfabetisk",
|
||||
"An external command handles the versioning. It has to remove the file from the synced folder.": "An external command handles the versioning. It has to remove the file from the synced folder.",
|
||||
"An external command handles the versioning. It has to remove the file from the synced folder.": "Ein ekstern kommando håndterer filutgåver. Den må sørge for at fila blir fjerna frå den synkroniserte mappa.",
|
||||
"Anonymous Usage Reporting": "Anonymisert bruksrapportering",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Einingar konfigurert på ei introduksjonseining vil òg verta lagt til denne eininga.",
|
||||
"Automatic upgrades": "Automatiske oppdateringar",
|
||||
@@ -23,7 +23,7 @@
|
||||
"Bugs": "Programfeil",
|
||||
"CPU Utilization": "CPU-utnytting",
|
||||
"Changelog": "Endringslogg",
|
||||
"Clean out after": "Clean out after",
|
||||
"Clean out after": "Tøm etter",
|
||||
"Close": "Lukk",
|
||||
"Command": "Kommando",
|
||||
"Comment, when used at the start of a line": "Kommentar, når brukt i starten av linja",
|
||||
@@ -40,7 +40,7 @@
|
||||
"Device {%device%} ({%address%}) wants to connect. Add new device?": "Eininga {{device}} ({{address}}) vil kopla seg til. Vil du leggja ho til?",
|
||||
"Devices": "Einingar",
|
||||
"Disconnected": "Fråkopla",
|
||||
"Discovery": "Discovery",
|
||||
"Discovery": "Oppdaging",
|
||||
"Documentation": "Dokumentasjon",
|
||||
"Download Rate": "Nedlastingsfart",
|
||||
"Downloaded": "Lasta ned",
|
||||
@@ -54,12 +54,12 @@
|
||||
"Enter ignore patterns, one per line.": "Skriv inn mønster som skal utelatast, eitt per linje.",
|
||||
"Error": "Feilmelding",
|
||||
"External File Versioning": "Ekstern filutgåvehandtering",
|
||||
"Failed Items": "Failed Items",
|
||||
"File Pull Order": "File Pull Order",
|
||||
"Failed Items": "Feilande element",
|
||||
"File Pull Order": "Henterekkefølge for filer",
|
||||
"File Versioning": "Filutgåvekontroll",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "File permission bits are ignored when looking for changes. Use on FAT file systems.",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Files are moved to .stversions folder when replaced or deleted by Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Informasjon om filrettar vert ignorert når det blir leita etter endringar. Bruk på FAT filsystem. ",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Filer som Syncthing slettar eller skriv over vert flytta til katalogen .stversions.",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Filer som Syncthing oppdaterar eller sletter vert flytta til ein datostempla versjon i .stversions-katalogen.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Filer er beskytta mot endringar gjort på andre einingar, men endringar gjort på denne eininga vert sende til resten av klyngja.",
|
||||
"Folder": "Mappe",
|
||||
"Folder ID": "Mappe ID",
|
||||
@@ -80,7 +80,7 @@
|
||||
"Ignore Patterns": "Utelatingsmønster",
|
||||
"Ignore Permissions": "Ignorer tilgangar",
|
||||
"Incoming Rate Limit (KiB/s)": "Innkomande hastigheitsgrense (KiB/s)",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Incorrect configuration may damage your folder contents and render Syncthing inoperable.",
|
||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Feil innstillingar kan skade innhaldet i dine delte katalogar og hindre Syncthing i å fungere.",
|
||||
"Introducer": "Introduktør",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "Det motsette av den gitte tilstanden (dvs. ekskluder ikkje)",
|
||||
"Keep Versions": "Behald Versjonar",
|
||||
@@ -90,11 +90,11 @@
|
||||
"Later": "Seinare",
|
||||
"Local Discovery": "Lokal oppdaging",
|
||||
"Local State": "Lokal Tilstand",
|
||||
"Local State (Total)": "Local State (Total)",
|
||||
"Major Upgrade": "Major Upgrade",
|
||||
"Local State (Total)": "Lokal tilstand (total)",
|
||||
"Major Upgrade": "Hovudoppgradering",
|
||||
"Maximum Age": "Maksimal Levetid",
|
||||
"Metadata Only": "Berre metadata",
|
||||
"Minimum Free Disk Space": "Minimum Free Disk Space",
|
||||
"Minimum Free Disk Space": "Naudsynt ledig diskplass",
|
||||
"Move to top of queue": "Flytt øvst i køen",
|
||||
"Multi level wildcard (matches multiple directory levels)": "Fleirnivå-jokerteikn (søkjer på fleire mappenivå)",
|
||||
"Never": "Aldri",
|
||||
@@ -116,15 +116,15 @@
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Plasseringa for lagra versjonar (la denne vera tom for å bruka standard .stversions-mappa i mappa).",
|
||||
"Pause": "Stans",
|
||||
"Paused": "Stansa",
|
||||
"Please consult the release notes before performing a major upgrade.": "Please consult the release notes before performing a major upgrade.",
|
||||
"Please consult the release notes before performing a major upgrade.": "Sjå \"release notes\" før ei hovudoppgradering vert utført.",
|
||||
"Please wait": "Gjer vel og vent",
|
||||
"Preview": "Førehandsvisning",
|
||||
"Preview Usage Report": "Førehandsvis bruksrapporten",
|
||||
"Quick guide to supported patterns": "Kjapp innføring i godkjente mønster",
|
||||
"RAM Utilization": "Minnebruk",
|
||||
"Random": "Tilfeldig",
|
||||
"Relayed via": "Relayed via",
|
||||
"Relays": "Relays",
|
||||
"Relayed via": "Relé via",
|
||||
"Relays": "Reléer",
|
||||
"Release Notes": "Utgivingsnotat",
|
||||
"Remove": "Fjern",
|
||||
"Rescan": "Skann På Ny",
|
||||
@@ -168,7 +168,7 @@
|
||||
"Syncthing is restarting.": "Syncthing startar på ny.",
|
||||
"Syncthing is upgrading.": "Syncthing oppgraderer.",
|
||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing ser ut til å vera nede, eller så er det eit problem med nettilkoplinga di. Prøvar på ny …",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.",
|
||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ser ut til å ha støtt på eit problem under behandling av din førespurnad. Vær vennleg å oppfrisk nettlesaren eller start Syncthing på nytt om problemet vedvarer.",
|
||||
"The aggregated statistics are publicly available at {%url%}.": "Samla statistikk er opent tilgjengeleg på {{url}}.",
|
||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Instillingane har blitt lagra men ikkje aktivert. Syncthing må starta på ny for å aktivera dei nye instillingane.",
|
||||
"The device ID cannot be blank.": "Eining ID kan ikkje vera tom.",
|
||||
@@ -184,17 +184,17 @@
|
||||
"The following items could not be synchronized.": "Fyljande filer kunne ikkje synkroniserast.",
|
||||
"The maximum age must be a number and cannot be blank.": "Maksimal levetid må vera eit tal og kan ikkje vera tomt.",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Høgaste tidsrom å behalda ei utgåve (i dagar, set til 0 for å behalda versjonane for alltid).",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "Nødvendig ledig diskplass må vere eit tal mellom 0 og 100.",
|
||||
"The number of days must be a number and cannot be blank.": "Tal på dagar må ver eit tal og kan ikkje vera tomt.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "The number of days to keep files in the trash can. Zero means forever.",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "Antall dagar å behalda filer i søppelkorga. Null betyr for alltid.",
|
||||
"The number of old versions to keep, per file.": "Tal på gamle versjonar ein skal behalda, per fil.",
|
||||
"The number of versions must be a number and cannot be blank.": "Tal på versjonar må vera eit tal og kan ikkje vera tomt.",
|
||||
"The path cannot be blank.": "Bana kan ikkje vera tom.",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "Hastigheitsgrensa må ver eit positivt tall (0: ingen grensa)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "Talet på sekund i skanneintervallet kan ikkje vera negativt.",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "They are retried automatically and will be synced when the error is resolved.",
|
||||
"This is a major version upgrade.": "This is a major version upgrade.",
|
||||
"Trash Can File Versioning": "Trash Can File Versioning",
|
||||
"They are retried automatically and will be synced when the error is resolved.": "Desse vil bli prøvd på nytt automatisk og vil bli synkronisert når feilen har blitt utbetra.",
|
||||
"This is a major version upgrade.": "Dette er ei hovudoppgradering",
|
||||
"Trash Can File Versioning": "Papirkorg filutgåvehandtering",
|
||||
"Unknown": "Ukjent",
|
||||
"Unshared": "Ikkje delt",
|
||||
"Unused": "Ubrukt",
|
||||
@@ -213,7 +213,7 @@
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Hugs at når ei ny mappe vert lagt til, vert mappe-ID-en brukt til å binda saman mappene mellom einingane. Det er skilnad på store og små bokstavar, så ID-ane må vera identiske på alle einingane.",
|
||||
"Yes": "Ja",
|
||||
"You must keep at least one version.": "Du må behalda minst ein versjon.",
|
||||
"days": "days",
|
||||
"days": "dagar",
|
||||
"full documentation": "all dokumentasjon",
|
||||
"items": "element",
|
||||
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} ønskjer å dela mappa \"{{folder}}\"."
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"Oldest First": "Mais antigo primeiro",
|
||||
"Options": "Opções",
|
||||
"Out of Sync": "Fora de sincronia",
|
||||
"Out of Sync Items": "Itens fora de sincronia",
|
||||
"Out of Sync Items": "Fora de sincronia",
|
||||
"Outgoing Rate Limit (KiB/s)": "Limite de velocidade de envio (KiB/s)",
|
||||
"Override Changes": "Sobrescrever mudanças",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Caminho para a pasta na máquina local. Será criado caso não exista. O caractere til (~) pode ser usado como um atalho para",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
<li class="auto-generated">Tobias Nygren</li>
|
||||
<li class="auto-generated">Tomas Cerveny</li>
|
||||
<li class="auto-generated">Tully Robinson</li>
|
||||
<li class="auto-generated">Tyler Brazier</li>
|
||||
<li class="auto-generated">Veeti Paananen</li>
|
||||
<li class="auto-generated">Vil Brekin</li>
|
||||
<li class="auto-generated">Yannic A.</li>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -29,18 +29,25 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultDiscoveryServers should be substituted when the configuration
|
||||
// contains <globalAnnounceServer>default</globalAnnounceServer>. This is
|
||||
// DefaultDiscoveryServersV4 should be substituted when the configuration
|
||||
// contains <globalAnnounceServer>default-v4</globalAnnounceServer>. This is
|
||||
// done by the "consumer" of the configuration, as we don't want these
|
||||
// saved to the config.
|
||||
DefaultDiscoveryServers = []string{
|
||||
DefaultDiscoveryServersV4 = []string{
|
||||
"https://discovery-v4-1.syncthing.net/?id=SR7AARM-TCBUZ5O-VFAXY4D-CECGSDE-3Q6IZ4G-XG7AH75-OBIXJQV-QJ6NLQA", // 194.126.249.5, Sweden
|
||||
"https://discovery-v4-2.syncthing.net/?id=DVU36WY-H3LVZHW-E6LLFRE-YAFN5EL-HILWRYP-OC2M47J-Z4PE62Y-ADIBDQC", // 45.55.230.38, USA
|
||||
"https://discovery-v4-3.syncthing.net/?id=7WT2BVR-FX62ZOW-TNVVW25-6AHFJGD-XEXQSBW-VO3MPL2-JBTLL4T-P4572Q4", // 128.199.95.124, Singapore
|
||||
}
|
||||
// DefaultDiscoveryServersV6 should be substituted when the configuration
|
||||
// contains <globalAnnounceServer>default-v6</globalAnnounceServer>.
|
||||
DefaultDiscoveryServersV6 = []string{
|
||||
"https://discovery-v6-1.syncthing.net/?id=SR7AARM-TCBUZ5O-VFAXY4D-CECGSDE-3Q6IZ4G-XG7AH75-OBIXJQV-QJ6NLQA", // 2001:470:28:4d6::5, Sweden
|
||||
"https://discovery-v6-2.syncthing.net/?id=DVU36WY-H3LVZHW-E6LLFRE-YAFN5EL-HILWRYP-OC2M47J-Z4PE62Y-ADIBDQC", // 2604:a880:800:10::182:a001, USA
|
||||
"https://discovery-v6-3.syncthing.net/?id=7WT2BVR-FX62ZOW-TNVVW25-6AHFJGD-XEXQSBW-VO3MPL2-JBTLL4T-P4572Q4", // 2400:6180:0:d0::d9:d001, Singapore
|
||||
}
|
||||
// DefaultDiscoveryServers should be substituted when the configuration
|
||||
// contains <globalAnnounceServer>default</globalAnnounceServer>.
|
||||
DefaultDiscoveryServers = append(DefaultDiscoveryServersV4, DefaultDiscoveryServersV6...)
|
||||
|
||||
// DefaultDiscoveryServersIP is used by the usage reporting.
|
||||
// XXX: Detect Android, and use this is we still don't have working DNS?
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
// If all verification calls returns nil, CommitConfiguration() is called for
|
||||
// each subscribing object. The callee returns true if the new configuration
|
||||
// has been successfully applied, otherwise false. Any Commit() call returning
|
||||
// false will result in a "restart needed" respone to the API/user. Note that
|
||||
// false will result in a "restart needed" response to the API/user. Note that
|
||||
// the new configuration will still have been applied by those who were
|
||||
// capable of doing so.
|
||||
type Committer interface {
|
||||
@@ -310,9 +310,14 @@ func (w *Wrapper) Save() error {
|
||||
func (w *Wrapper) GlobalDiscoveryServers() []string {
|
||||
var servers []string
|
||||
for _, srv := range w.cfg.Options.GlobalAnnServers {
|
||||
if srv == "default" {
|
||||
switch srv {
|
||||
case "default":
|
||||
servers = append(servers, DefaultDiscoveryServers...)
|
||||
} else {
|
||||
case "default-v4":
|
||||
servers = append(servers, DefaultDiscoveryServersV4...)
|
||||
case "default-v6":
|
||||
servers = append(servers, DefaultDiscoveryServersV6...)
|
||||
default:
|
||||
servers = append(servers, srv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,49 +280,18 @@ func (s *connectionSvc) connect() {
|
||||
continue
|
||||
}
|
||||
|
||||
var addrs []string
|
||||
var relays []discover.Relay
|
||||
for _, addr := range deviceCfg.Addresses {
|
||||
if addr == "dynamic" {
|
||||
if s.discoverer != nil {
|
||||
if t, r, err := s.discoverer.Lookup(deviceID); err == nil {
|
||||
addrs = append(addrs, t...)
|
||||
relays = append(relays, r...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
}
|
||||
addrs, relays := s.resolveAddresses(deviceID, deviceCfg.Addresses)
|
||||
|
||||
for _, addr := range addrs {
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
l.Infoln("Failed to parse connection url:", addr, err)
|
||||
continue
|
||||
if conn := s.connectDirect(deviceID, addr); conn != nil {
|
||||
if connected {
|
||||
s.model.Close(deviceID, fmt.Errorf("switching connections"))
|
||||
}
|
||||
s.conns <- model.IntermediateConnection{
|
||||
conn, model.ConnectionTypeDirectDial,
|
||||
}
|
||||
continue nextDevice
|
||||
}
|
||||
|
||||
dialer, ok := dialers[uri.Scheme]
|
||||
if !ok {
|
||||
l.Infoln("Unknown address schema", uri)
|
||||
continue
|
||||
}
|
||||
|
||||
l.Debugln("dial", deviceCfg.DeviceID, uri)
|
||||
conn, err := dialer(uri, s.tlsCfg)
|
||||
if err != nil {
|
||||
l.Debugln("dial failed", deviceCfg.DeviceID, uri, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if connected {
|
||||
s.model.Close(deviceID, fmt.Errorf("switching connections"))
|
||||
}
|
||||
|
||||
s.conns <- model.IntermediateConnection{
|
||||
conn, model.ConnectionTypeDirectDial,
|
||||
}
|
||||
continue nextDevice
|
||||
}
|
||||
|
||||
// Only connect via relays if not already connected
|
||||
@@ -345,45 +314,12 @@ func (s *connectionSvc) connect() {
|
||||
s.lastRelayCheck[deviceID] = time.Now()
|
||||
|
||||
for _, addr := range relays {
|
||||
uri, err := url.Parse(addr.URL)
|
||||
if err != nil {
|
||||
l.Infoln("Failed to parse relay connection url:", addr, err)
|
||||
continue
|
||||
if conn := s.connectViaRelay(deviceID, addr); conn != nil {
|
||||
s.conns <- model.IntermediateConnection{
|
||||
conn, model.ConnectionTypeRelayDial,
|
||||
}
|
||||
continue nextDevice
|
||||
}
|
||||
|
||||
inv, err := client.GetInvitationFromRelay(uri, deviceID, s.tlsCfg.Certificates)
|
||||
if err != nil {
|
||||
l.Debugf("Failed to get invitation for %s from %s: %v", deviceID, uri, err)
|
||||
continue
|
||||
} else {
|
||||
l.Debugln("Succesfully retrieved relay invitation", inv, "from", uri)
|
||||
}
|
||||
|
||||
conn, err := client.JoinSession(inv)
|
||||
if err != nil {
|
||||
l.Debugf("Failed to join relay session %s: %v", inv, err)
|
||||
continue
|
||||
} else {
|
||||
l.Debugln("Sucessfully joined relay session", inv)
|
||||
}
|
||||
|
||||
var tc *tls.Conn
|
||||
|
||||
if inv.ServerSocket {
|
||||
tc = tls.Server(conn, s.tlsCfg)
|
||||
} else {
|
||||
tc = tls.Client(conn, s.tlsCfg)
|
||||
}
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
l.Infof("TLS handshake (BEP/relay %s): %v", inv, err)
|
||||
tc.Close()
|
||||
continue
|
||||
}
|
||||
s.conns <- model.IntermediateConnection{
|
||||
tc, model.ConnectionTypeRelayDial,
|
||||
}
|
||||
continue nextDevice
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +331,84 @@ func (s *connectionSvc) connect() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *connectionSvc) resolveAddresses(deviceID protocol.DeviceID, inAddrs []string) (addrs []string, relays []discover.Relay) {
|
||||
for _, addr := range inAddrs {
|
||||
if addr == "dynamic" {
|
||||
if s.discoverer != nil {
|
||||
if t, r, err := s.discoverer.Lookup(deviceID); err == nil {
|
||||
addrs = append(addrs, t...)
|
||||
relays = append(relays, r...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *connectionSvc) connectDirect(deviceID protocol.DeviceID, addr string) *tls.Conn {
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
l.Infoln("Failed to parse connection url:", addr, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
dialer, ok := dialers[uri.Scheme]
|
||||
if !ok {
|
||||
l.Infoln("Unknown address schema", uri)
|
||||
return nil
|
||||
}
|
||||
|
||||
l.Debugln("dial", deviceID, uri)
|
||||
conn, err := dialer(uri, s.tlsCfg)
|
||||
if err != nil {
|
||||
l.Debugln("dial failed", deviceID, uri, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func (s *connectionSvc) connectViaRelay(deviceID protocol.DeviceID, addr discover.Relay) *tls.Conn {
|
||||
uri, err := url.Parse(addr.URL)
|
||||
if err != nil {
|
||||
l.Infoln("Failed to parse relay connection url:", addr, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
inv, err := client.GetInvitationFromRelay(uri, deviceID, s.tlsCfg.Certificates)
|
||||
if err != nil {
|
||||
l.Debugf("Failed to get invitation for %s from %s: %v", deviceID, uri, err)
|
||||
return nil
|
||||
}
|
||||
l.Debugln("Succesfully retrieved relay invitation", inv, "from", uri)
|
||||
|
||||
conn, err := client.JoinSession(inv)
|
||||
if err != nil {
|
||||
l.Debugf("Failed to join relay session %s: %v", inv, err)
|
||||
return nil
|
||||
}
|
||||
l.Debugln("Successfully joined relay session", inv)
|
||||
|
||||
var tc *tls.Conn
|
||||
|
||||
if inv.ServerSocket {
|
||||
tc = tls.Server(conn, s.tlsCfg)
|
||||
} else {
|
||||
tc = tls.Client(conn, s.tlsCfg)
|
||||
}
|
||||
|
||||
err = tc.Handshake()
|
||||
if err != nil {
|
||||
l.Infof("TLS handshake (BEP/relay %s): %v", inv, err)
|
||||
tc.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (s *connectionSvc) acceptRelayConns() {
|
||||
for {
|
||||
conn := s.relaySvc.Accept()
|
||||
|
||||
@@ -214,6 +214,9 @@ func blockKeyInto(o, hash []byte, folder, file string) []byte {
|
||||
}
|
||||
o[0] = KeyTypeBlock
|
||||
copy(o[1:], []byte(folder))
|
||||
for i := len(folder); i < 64; i++ {
|
||||
o[1+i] = 0
|
||||
}
|
||||
copy(o[1+64:], []byte(hash))
|
||||
copy(o[1+64+32:], []byte(file))
|
||||
return o
|
||||
|
||||
@@ -45,7 +45,7 @@ func (r *VirtualMtimeRepo) UpdateMtime(path string, diskMtime, actualMtime time.
|
||||
func (r *VirtualMtimeRepo) GetMtime(path string, diskMtime time.Time) time.Time {
|
||||
data, exists := r.ns.Bytes(path)
|
||||
if !exists {
|
||||
// Absense of debug print is significant enough in itself here
|
||||
// Absence of debug print is significant enough in itself here
|
||||
return diskMtime
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ connection addresses (if any) and relay addresses (if any).
|
||||
}
|
||||
|
||||
It's OK for either of the "direct" or "relays" fields to be either the empty
|
||||
list ([]), null, or missing entirely. An announcment with both fields missing
|
||||
list ([]), null, or missing entirely. An announcement with both fields missing
|
||||
or empty is however not useful...
|
||||
|
||||
Any empty or unspecified IP addresses (i.e. addresses like tcp://:22000,
|
||||
@@ -39,7 +39,7 @@ The server response is empty, with code 200 (OK) on success. If no certificate
|
||||
was presented, status 403 (Forbidden) is returned. If the posted data doesn't
|
||||
conform to the expected format, 400 (Bad Request) is returned.
|
||||
|
||||
In successfull responses, the server may return a "Reannounce-After" header
|
||||
In successful responses, the server may return a "Reannounce-After" header
|
||||
containing the number of seconds after which the client should perform a new
|
||||
announcement.
|
||||
|
||||
@@ -58,7 +58,7 @@ Queries are performed as HTTPS GET requests to the announce server URL. The
|
||||
requested device ID is passed as the query parameter "device", in canonical
|
||||
string form, i.e. https://announce.syncthing.net/?device=ABC12345-....
|
||||
|
||||
Successfull responses will have status code 200 (OK) and carry a JSON payload
|
||||
Successful responses will have status code 200 (OK) and carry a JSON payload
|
||||
of the same format as the announcement above. The response will not contain
|
||||
empty or unspecified addresses.
|
||||
|
||||
|
||||
@@ -196,6 +196,7 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
|
||||
// Any empty or unspecified addresses should be set to the source address
|
||||
// of the announcement. We also skip any addresses we can't parse.
|
||||
|
||||
l.Debugln("discover: Registering addresses for", id)
|
||||
var validAddresses []string
|
||||
for _, addr := range device.Addresses {
|
||||
u, err := url.Parse(addr.URL)
|
||||
@@ -214,9 +215,12 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
|
||||
continue
|
||||
}
|
||||
u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
|
||||
l.Debugf("discover: Reconstructed URL is %#v", u)
|
||||
validAddresses = append(validAddresses, u.String())
|
||||
l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr.URL, u.String())
|
||||
} else {
|
||||
validAddresses = append(validAddresses, addr.URL)
|
||||
l.Debugf("discover: Accepted address %s verbatim", addr.URL)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +234,7 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
|
||||
if isNewDevice {
|
||||
events.Default.Log(events.DeviceDiscovered, map[string]interface{}{
|
||||
"device": id.String(),
|
||||
"addrs": device.Addresses,
|
||||
"addrs": validAddresses,
|
||||
"relays": device.Relays,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ const (
|
||||
FolderScanProgress
|
||||
ExternalPortMappingChanged
|
||||
RelayStateChanged
|
||||
LoginAttempt
|
||||
|
||||
AllEvents = (1 << iota) - 1
|
||||
)
|
||||
@@ -93,6 +94,8 @@ func (t EventType) String() string {
|
||||
return "ExternalPortMappingChanged"
|
||||
case RelayStateChanged:
|
||||
return "RelayStateChanged"
|
||||
case LoginAttempt:
|
||||
return "LoginAttempt"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
@@ -281,7 +284,7 @@ func (s *BufferedSubscription) Since(id int, into []Event) []Event {
|
||||
}
|
||||
|
||||
// Error returns a string pointer suitable for JSON marshalling errors. It
|
||||
// retains the "null on sucess" semantics, but ensures the error result is a
|
||||
// retains the "null on success" semantics, but ensures the error result is a
|
||||
// string regardless of the underlying concrete error type.
|
||||
func Error(err error) *string {
|
||||
if err == nil {
|
||||
|
||||
@@ -78,7 +78,7 @@ func TestFacilityDebugging(t *testing.T) {
|
||||
f1.Debugln("Debug line from f1")
|
||||
|
||||
if msgs != 1 {
|
||||
t.Fatalf("Incorrent number of messages, %d != 1", msgs)
|
||||
t.Fatalf("Incorrect number of messages, %d != 1", msgs)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,15 +75,16 @@ type Model struct {
|
||||
clientName string
|
||||
clientVersion string
|
||||
|
||||
folderCfgs map[string]config.FolderConfiguration // folder -> cfg
|
||||
folderFiles map[string]*db.FileSet // folder -> files
|
||||
folderDevices map[string][]protocol.DeviceID // folder -> deviceIDs
|
||||
deviceFolders map[protocol.DeviceID][]string // deviceID -> folders
|
||||
deviceStatRefs map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef
|
||||
folderIgnores map[string]*ignore.Matcher // folder -> matcher object
|
||||
folderRunners map[string]service // folder -> puller or scanner
|
||||
folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef
|
||||
fmut sync.RWMutex // protects the above
|
||||
folderCfgs map[string]config.FolderConfiguration // folder -> cfg
|
||||
folderFiles map[string]*db.FileSet // folder -> files
|
||||
folderDevices map[string][]protocol.DeviceID // folder -> deviceIDs
|
||||
deviceFolders map[protocol.DeviceID][]string // deviceID -> folders
|
||||
deviceStatRefs map[protocol.DeviceID]*stats.DeviceStatisticsReference // deviceID -> statsRef
|
||||
folderIgnores map[string]*ignore.Matcher // folder -> matcher object
|
||||
folderRunners map[string]service // folder -> puller or scanner
|
||||
folderRunnerTokens map[string][]suture.ServiceToken // folder -> tokens for puller or scanner
|
||||
folderStatRefs map[string]*stats.FolderStatisticsReference // folder -> statsRef
|
||||
fmut sync.RWMutex // protects the above
|
||||
|
||||
conn map[protocol.DeviceID]Connection
|
||||
deviceVer map[protocol.DeviceID]string
|
||||
@@ -105,28 +106,29 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
|
||||
l.Debugln(line)
|
||||
},
|
||||
}),
|
||||
cfg: cfg,
|
||||
db: ldb,
|
||||
finder: db.NewBlockFinder(ldb),
|
||||
progressEmitter: NewProgressEmitter(cfg),
|
||||
id: id,
|
||||
shortID: id.Short(),
|
||||
cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles,
|
||||
protectedFiles: protectedFiles,
|
||||
deviceName: deviceName,
|
||||
clientName: clientName,
|
||||
clientVersion: clientVersion,
|
||||
folderCfgs: make(map[string]config.FolderConfiguration),
|
||||
folderFiles: make(map[string]*db.FileSet),
|
||||
folderDevices: make(map[string][]protocol.DeviceID),
|
||||
deviceFolders: make(map[protocol.DeviceID][]string),
|
||||
deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
|
||||
folderIgnores: make(map[string]*ignore.Matcher),
|
||||
folderRunners: make(map[string]service),
|
||||
folderStatRefs: make(map[string]*stats.FolderStatisticsReference),
|
||||
conn: make(map[protocol.DeviceID]Connection),
|
||||
deviceVer: make(map[protocol.DeviceID]string),
|
||||
devicePaused: make(map[protocol.DeviceID]bool),
|
||||
cfg: cfg,
|
||||
db: ldb,
|
||||
finder: db.NewBlockFinder(ldb),
|
||||
progressEmitter: NewProgressEmitter(cfg),
|
||||
id: id,
|
||||
shortID: id.Short(),
|
||||
cacheIgnoredFiles: cfg.Options().CacheIgnoredFiles,
|
||||
protectedFiles: protectedFiles,
|
||||
deviceName: deviceName,
|
||||
clientName: clientName,
|
||||
clientVersion: clientVersion,
|
||||
folderCfgs: make(map[string]config.FolderConfiguration),
|
||||
folderFiles: make(map[string]*db.FileSet),
|
||||
folderDevices: make(map[string][]protocol.DeviceID),
|
||||
deviceFolders: make(map[protocol.DeviceID][]string),
|
||||
deviceStatRefs: make(map[protocol.DeviceID]*stats.DeviceStatisticsReference),
|
||||
folderIgnores: make(map[string]*ignore.Matcher),
|
||||
folderRunners: make(map[string]service),
|
||||
folderRunnerTokens: make(map[string][]suture.ServiceToken),
|
||||
folderStatRefs: make(map[string]*stats.FolderStatisticsReference),
|
||||
conn: make(map[protocol.DeviceID]Connection),
|
||||
deviceVer: make(map[protocol.DeviceID]string),
|
||||
devicePaused: make(map[protocol.DeviceID]bool),
|
||||
|
||||
fmut: sync.NewRWMutex(),
|
||||
pmut: sync.NewRWMutex(),
|
||||
@@ -163,7 +165,6 @@ func (m *Model) StartFolderRW(folder string) {
|
||||
}
|
||||
p := newRWFolder(m, m.shortID, cfg)
|
||||
m.folderRunners[folder] = p
|
||||
m.fmut.Unlock()
|
||||
|
||||
if len(cfg.Versioning.Type) > 0 {
|
||||
factory, ok := versioner.Factories[cfg.Versioning.Type]
|
||||
@@ -176,14 +177,17 @@ func (m *Model) StartFolderRW(folder string) {
|
||||
// The versioner implements the suture.Service interface, so
|
||||
// expects to be run in the background in addition to being called
|
||||
// when files are going to be archived.
|
||||
m.Add(service)
|
||||
token := m.Add(service)
|
||||
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
|
||||
}
|
||||
p.versioner = versioner
|
||||
}
|
||||
|
||||
m.warnAboutOverwritingProtectedFiles(folder)
|
||||
|
||||
m.Add(p)
|
||||
token := m.Add(p)
|
||||
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
|
||||
m.fmut.Unlock()
|
||||
|
||||
l.Okln("Ready to synchronize", folder, "(read-write)")
|
||||
}
|
||||
@@ -232,13 +236,49 @@ func (m *Model) StartFolderRO(folder string) {
|
||||
}
|
||||
s := newROFolder(m, folder, time.Duration(cfg.RescanIntervalS)*time.Second)
|
||||
m.folderRunners[folder] = s
|
||||
|
||||
token := m.Add(s)
|
||||
m.folderRunnerTokens[folder] = append(m.folderRunnerTokens[folder], token)
|
||||
m.fmut.Unlock()
|
||||
|
||||
m.Add(s)
|
||||
|
||||
l.Okln("Ready to synchronize", folder, "(read only; no external updates accepted)")
|
||||
}
|
||||
|
||||
func (m *Model) RemoveFolder(folder string) {
|
||||
m.fmut.Lock()
|
||||
m.pmut.Lock()
|
||||
|
||||
// Stop the services running for this folder
|
||||
for _, id := range m.folderRunnerTokens[folder] {
|
||||
m.Remove(id)
|
||||
}
|
||||
|
||||
// Close connections to affected devices
|
||||
for _, dev := range m.folderDevices[folder] {
|
||||
if conn, ok := m.conn[dev]; ok {
|
||||
closeRawConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up our config maps
|
||||
delete(m.folderCfgs, folder)
|
||||
delete(m.folderFiles, folder)
|
||||
delete(m.folderDevices, folder)
|
||||
delete(m.folderIgnores, folder)
|
||||
delete(m.folderRunners, folder)
|
||||
delete(m.folderRunnerTokens, folder)
|
||||
delete(m.folderStatRefs, folder)
|
||||
for dev, folders := range m.deviceFolders {
|
||||
m.deviceFolders[dev] = stringSliceWithout(folders, folder)
|
||||
}
|
||||
|
||||
// Remove it from the database
|
||||
db.DropFolder(m.db, folder)
|
||||
|
||||
m.pmut.Unlock()
|
||||
m.fmut.Unlock()
|
||||
}
|
||||
|
||||
type ConnectionInfo struct {
|
||||
protocol.Statistics
|
||||
Connected bool
|
||||
@@ -261,7 +301,7 @@ func (info ConnectionInfo) MarshalJSON() ([]byte, error) {
|
||||
})
|
||||
}
|
||||
|
||||
// ConnectionStats returns a map with connection statistics for each connected device.
|
||||
// ConnectionStats returns a map with connection statistics for each device.
|
||||
func (m *Model) ConnectionStats() map[string]interface{} {
|
||||
type remoteAddrer interface {
|
||||
RemoteAddr() net.Addr
|
||||
@@ -1014,7 +1054,7 @@ func sendIndexes(conn protocol.Connection, folder string, fs *db.FileSet, ignore
|
||||
minLocalVer, err = sendIndexTo(false, minLocalVer, conn, folder, fs, ignores)
|
||||
|
||||
// Wait a short amount of time before entering the next loop. If there
|
||||
// are continous changes happening to the local index, this gives us
|
||||
// are continuous changes happening to the local index, this gives us
|
||||
// time to batch them up a little.
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
@@ -1250,6 +1290,11 @@ nextSub:
|
||||
}
|
||||
subs = unifySubs
|
||||
|
||||
// The cancel channel is closed whenever we return (such as from an error),
|
||||
// to signal the potentially still running walker to stop.
|
||||
cancel := make(chan struct{})
|
||||
defer close(cancel)
|
||||
|
||||
w := &scanner.Walker{
|
||||
Folder: folderCfg.ID,
|
||||
Dir: folderCfg.Path(),
|
||||
@@ -1265,6 +1310,7 @@ nextSub:
|
||||
Hashers: m.numHashers(folder),
|
||||
ShortID: m.shortID,
|
||||
ProgressTickIntervalS: folderCfg.ScanProgressIntervalS,
|
||||
Cancel: cancel,
|
||||
}
|
||||
|
||||
runner.setState(FolderScanning)
|
||||
@@ -1674,17 +1720,17 @@ func (m *Model) BringToFront(folder, file string) {
|
||||
// CheckFolderHealth checks the folder for common errors and returns the
|
||||
// current folder error, or nil if the folder is healthy.
|
||||
func (m *Model) CheckFolderHealth(id string) error {
|
||||
folder, ok := m.cfg.Folders()[id]
|
||||
if !ok {
|
||||
return errors.New("folder does not exist")
|
||||
}
|
||||
|
||||
if minFree := m.cfg.Options().MinHomeDiskFreePct; minFree > 0 {
|
||||
if free, err := osutil.DiskFreePercentage(m.cfg.ConfigPath()); err == nil && free < minFree {
|
||||
return errors.New("home disk has insufficient free space")
|
||||
}
|
||||
}
|
||||
|
||||
folder, ok := m.cfg.Folders()[id]
|
||||
if !ok {
|
||||
return errors.New("folder does not exist")
|
||||
}
|
||||
|
||||
fi, err := os.Stat(folder.Path())
|
||||
|
||||
v, ok := m.CurrentLocalVersion(id)
|
||||
@@ -1797,9 +1843,9 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
for folderID, fromCfg := range fromFolders {
|
||||
toCfg, ok := toFolders[folderID]
|
||||
if !ok {
|
||||
// A folder was removed. Requires restart.
|
||||
l.Debugln(m, "requires restart, removing folder", folderID)
|
||||
return false
|
||||
// The folder was removed.
|
||||
m.RemoveFolder(folderID)
|
||||
continue
|
||||
}
|
||||
|
||||
// This folder exists on both sides. Compare the device lists, as we
|
||||
@@ -1848,7 +1894,7 @@ func (m *Model) CommitConfiguration(from, to config.Configuration) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Removing a device requres restart
|
||||
// Removing a device requires restart
|
||||
toDevs := mapDeviceCfgs(from.Devices)
|
||||
for _, dev := range from.Devices {
|
||||
if _, ok := toDevs[dev.DeviceID]; !ok {
|
||||
@@ -1961,3 +2007,14 @@ func closeRawConn(conn io.Closer) error {
|
||||
}
|
||||
return conn.Close()
|
||||
}
|
||||
|
||||
func stringSliceWithout(ss []string, s string) []string {
|
||||
for i := range ss {
|
||||
if ss[i] == s {
|
||||
copy(ss[i:], ss[i+1:])
|
||||
ss = ss[:len(ss)-1]
|
||||
return ss
|
||||
}
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
@@ -948,7 +948,8 @@ func (p *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocks
|
||||
// changes that we don't know about yet and we should scan before
|
||||
// touching the file. If we can't stat the file we'll just pull it.
|
||||
if info, err := osutil.Lstat(realName); err == nil {
|
||||
if info.ModTime().Unix() != curFile.Modified || info.Size() != curFile.Size() {
|
||||
mtime := p.virtualMtimeRepo.GetMtime(file.Name, info.ModTime())
|
||||
if mtime.Unix() != curFile.Modified || info.Size() != curFile.Size() {
|
||||
l.Debugln("file modified but not rescanned; not pulling:", realName)
|
||||
// Scan() is synchronous (i.e. blocks until the scan is
|
||||
// completed and returns an error), but a scan can't happen
|
||||
|
||||
@@ -514,7 +514,7 @@ func TestDeregisterOnFailInPull(t *testing.T) {
|
||||
|
||||
p.handleFile(file, copyChan, finisherChan)
|
||||
|
||||
// Receove at finisher, we shoud error out as puller has nowhere to pull
|
||||
// Receive at finisher, we should error out as puller has nowhere to pull
|
||||
// from.
|
||||
select {
|
||||
case state := <-finisherBufferChan:
|
||||
|
||||
@@ -19,13 +19,13 @@ import (
|
||||
// workers are used in parallel. The outbox will become closed when the inbox
|
||||
// is closed and all items handled.
|
||||
|
||||
func newParallelHasher(dir string, blockSize, workers int, outbox, inbox chan protocol.FileInfo, counter *int64, done chan struct{}) {
|
||||
func newParallelHasher(dir string, blockSize, workers int, outbox, inbox chan protocol.FileInfo, counter *int64, done, cancel chan struct{}) {
|
||||
wg := sync.NewWaitGroup()
|
||||
wg.Add(workers)
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
go func() {
|
||||
hashFiles(dir, blockSize, outbox, inbox, counter)
|
||||
hashFiles(dir, blockSize, outbox, inbox, counter, cancel)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
@@ -59,19 +59,33 @@ func HashFile(path string, blockSize int, sizeHint int64, counter *int64) ([]pro
|
||||
return Blocks(fd, blockSize, sizeHint, counter)
|
||||
}
|
||||
|
||||
func hashFiles(dir string, blockSize int, outbox, inbox chan protocol.FileInfo, counter *int64) {
|
||||
for f := range inbox {
|
||||
if f.IsDirectory() || f.IsDeleted() {
|
||||
panic("Bug. Asked to hash a directory or a deleted file.")
|
||||
}
|
||||
func hashFiles(dir string, blockSize int, outbox, inbox chan protocol.FileInfo, counter *int64, cancel chan struct{}) {
|
||||
for {
|
||||
select {
|
||||
case f, ok := <-inbox:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
blocks, err := HashFile(filepath.Join(dir, f.Name), blockSize, f.CachedSize, counter)
|
||||
if err != nil {
|
||||
l.Debugln("hash error:", f.Name, err)
|
||||
continue
|
||||
}
|
||||
if f.IsDirectory() || f.IsDeleted() {
|
||||
panic("Bug. Asked to hash a directory or a deleted file.")
|
||||
}
|
||||
|
||||
f.Blocks = blocks
|
||||
outbox <- f
|
||||
blocks, err := HashFile(filepath.Join(dir, f.Name), blockSize, f.CachedSize, counter)
|
||||
if err != nil {
|
||||
l.Debugln("hash error:", f.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
f.Blocks = blocks
|
||||
select {
|
||||
case outbox <- f:
|
||||
case <-cancel:
|
||||
return
|
||||
}
|
||||
|
||||
case <-cancel:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ type Walker struct {
|
||||
// Optional progress tick interval which defines how often FolderScanProgress
|
||||
// events are emitted. Negative number means disabled.
|
||||
ProgressTickIntervalS int
|
||||
// Signals cancel from the outside - when closed, we should stop walking.
|
||||
Cancel chan struct{}
|
||||
}
|
||||
|
||||
type TempNamer interface {
|
||||
@@ -121,7 +123,7 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) {
|
||||
// We're not required to emit scan progress events, just kick off hashers,
|
||||
// and feed inputs directly from the walker.
|
||||
if w.ProgressTickIntervalS < 0 {
|
||||
newParallelHasher(w.Dir, w.BlockSize, w.Hashers, finishedChan, toHashChan, nil, nil)
|
||||
newParallelHasher(w.Dir, w.BlockSize, w.Hashers, finishedChan, toHashChan, nil, nil, w.Cancel)
|
||||
return finishedChan, nil
|
||||
}
|
||||
|
||||
@@ -149,7 +151,7 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) {
|
||||
|
||||
realToHashChan := make(chan protocol.FileInfo)
|
||||
done := make(chan struct{})
|
||||
newParallelHasher(w.Dir, w.BlockSize, w.Hashers, finishedChan, realToHashChan, &progress, done)
|
||||
newParallelHasher(w.Dir, w.BlockSize, w.Hashers, finishedChan, realToHashChan, &progress, done, w.Cancel)
|
||||
|
||||
// A routine which actually emits the FolderScanProgress events
|
||||
// every w.ProgressTicker ticks, until the hasher routines terminate.
|
||||
@@ -168,13 +170,21 @@ func (w *Walker) Walk() (chan protocol.FileInfo, error) {
|
||||
"current": current,
|
||||
"total": total,
|
||||
})
|
||||
case <-w.Cancel:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
loop:
|
||||
for _, file := range filesToHash {
|
||||
l.Debugln("real to hash:", file.Name)
|
||||
realToHashChan <- file
|
||||
select {
|
||||
case realToHashChan <- file:
|
||||
case <-w.Cancel:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
close(realToHashChan)
|
||||
}()
|
||||
@@ -329,7 +339,11 @@ func (w *Walker) walkAndHashFiles(fchan, dchan chan protocol.FileInfo) filepath.
|
||||
|
||||
l.Debugln("symlink changedb:", p, f)
|
||||
|
||||
dchan <- f
|
||||
select {
|
||||
case dchan <- f:
|
||||
case <-w.Cancel:
|
||||
return errors.New("cancelled")
|
||||
}
|
||||
|
||||
return skip
|
||||
}
|
||||
@@ -363,7 +377,13 @@ func (w *Walker) walkAndHashFiles(fchan, dchan chan protocol.FileInfo) filepath.
|
||||
Modified: mtime.Unix(),
|
||||
}
|
||||
l.Debugln("dir:", p, f)
|
||||
dchan <- f
|
||||
|
||||
select {
|
||||
case dchan <- f:
|
||||
case <-w.Cancel:
|
||||
return errors.New("cancelled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -406,7 +426,12 @@ func (w *Walker) walkAndHashFiles(fchan, dchan chan protocol.FileInfo) filepath.
|
||||
CachedSize: info.Size(),
|
||||
}
|
||||
l.Debugln("to hash:", p, f)
|
||||
fchan <- f
|
||||
|
||||
select {
|
||||
case fchan <- f:
|
||||
case <-w.Cancel:
|
||||
return errors.New("cancelled")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -190,11 +190,7 @@ func readTarGz(dir string, r io.Reader) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
shortName := path.Base(hdr.Name)
|
||||
|
||||
l.Debugf("considering file %q", shortName)
|
||||
|
||||
err = archiveFileVisitor(dir, &tempName, &sig, shortName, tr)
|
||||
err = archiveFileVisitor(dir, &tempName, &sig, hdr.Name, tr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -227,16 +223,12 @@ func readZip(dir string, r io.Reader) (string, error) {
|
||||
|
||||
// Iterate through the files in the archive.
|
||||
for _, file := range archive.File {
|
||||
shortName := path.Base(file.Name)
|
||||
|
||||
l.Debugf("considering file %q", shortName)
|
||||
|
||||
inFile, err := file.Open()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = archiveFileVisitor(dir, &tempName, &sig, shortName, inFile)
|
||||
err = archiveFileVisitor(dir, &tempName, &sig, file.Name, inFile)
|
||||
inFile.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -256,18 +248,26 @@ func readZip(dir string, r io.Reader) (string, error) {
|
||||
|
||||
// archiveFileVisitor is called for each file in an archive. It may set
|
||||
// tempFile and signature.
|
||||
func archiveFileVisitor(dir string, tempFile *string, signature *[]byte, filename string, filedata io.Reader) error {
|
||||
func archiveFileVisitor(dir string, tempFile *string, signature *[]byte, archivePath string, filedata io.Reader) error {
|
||||
var err error
|
||||
filename := path.Base(archivePath)
|
||||
archiveDir := path.Dir(archivePath)
|
||||
archiveDirs := strings.Split(archiveDir, "/")
|
||||
if len(archiveDirs) > 1 {
|
||||
//don't consider files in subfolders
|
||||
return nil
|
||||
}
|
||||
l.Debugf("considering file %s", archivePath)
|
||||
switch filename {
|
||||
case "syncthing", "syncthing.exe":
|
||||
l.Debugln("reading binary")
|
||||
l.Debugf("found upgrade binary %s", archivePath)
|
||||
*tempFile, err = writeBinary(dir, filedata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "syncthing.sig", "syncthing.exe.sig":
|
||||
l.Debugln("reading signature")
|
||||
l.Debugf("found signature %s", archivePath)
|
||||
*signature, err = ioutil.ReadAll(filedata)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-BEP" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-CONFIG" "5" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-EVENT-API" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-FAQ" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v3
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-NETWORKING" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-NETWORKING" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-REST-API" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-REST-API" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.
|
||||
@@ -602,6 +602,13 @@ needed by this device in order for it to become in sync.
|
||||
.fi
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.sp
|
||||
\fBNOTE:\fP
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
This is an expensive call, increasing CPU and RAM usage on the device. Use sparingly.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SS POST /rest/db/prio
|
||||
.sp
|
||||
Moves the file to the top of the download queue.
|
||||
@@ -776,19 +783,55 @@ Returns the data sent in the anonymous usage report.
|
||||
.nf
|
||||
.ft C
|
||||
{
|
||||
"folderMaxFiles": 42106,
|
||||
"folderMaxMiB": 12563,
|
||||
"longVersion": "syncthing v0.10.27+5\-g36c93b7 (go1.4 darwin\-amd64 default) jb@syno 2015\-03\-16 20:43:34 UTC",
|
||||
"memorySize": 16384,
|
||||
"memoryUsageMiB": 41,
|
||||
"numDevices": 10,
|
||||
"numFolders": 4,
|
||||
"platform": "darwin\-amd64",
|
||||
"sha256Perf": 122.38,
|
||||
"totFiles": 45180,
|
||||
"totMiB": 18151,
|
||||
"uniqueID": "6vulmdGw",
|
||||
"version": "v0.10.27+5\-g36c93b7"
|
||||
"folderMaxMiB" : 0,
|
||||
"platform" : "linux\-amd64",
|
||||
"totMiB" : 0,
|
||||
"longVersion" : "syncthing v0.12.2 \e"Beryllium Bedbug\e" (go1.4.3 linux\-amd64 default) unknown\-user@build2.syncthing.net 2015\-11\-09 13:23:26 UTC",
|
||||
"upgradeAllowedManual" : true,
|
||||
"totFiles" : 3,
|
||||
"folderUses" : {
|
||||
"ignorePerms" : 0,
|
||||
"autoNormalize" : 0,
|
||||
"readonly" : 0,
|
||||
"ignoreDelete" : 0
|
||||
},
|
||||
"memoryUsageMiB" : 13,
|
||||
"version" : "v0.12.2",
|
||||
"sha256Perf" : 27.28,
|
||||
"numFolders" : 2,
|
||||
"memorySize" : 1992,
|
||||
"announce" : {
|
||||
"defaultServersIP" : 0,
|
||||
"otherServers" : 0,
|
||||
"globalEnabled" : false,
|
||||
"defaultServersDNS" : 1,
|
||||
"localEnabled" : false
|
||||
},
|
||||
"usesRateLimit" : false,
|
||||
"numCPU" : 2,
|
||||
"uniqueID" : "",
|
||||
"urVersion" : 2,
|
||||
"rescanIntvs" : [
|
||||
60,
|
||||
60
|
||||
],
|
||||
"numDevices" : 2,
|
||||
"folderMaxFiles" : 3,
|
||||
"relays" : {
|
||||
"defaultServers" : 1,
|
||||
"enabled" : true,
|
||||
"otherServers" : 0
|
||||
},
|
||||
"deviceUses" : {
|
||||
"compressMetadata" : 1,
|
||||
"customCertName" : 0,
|
||||
"staticAddr" : 1,
|
||||
"compressAlways" : 0,
|
||||
"compressNever" : 1,
|
||||
"introducer" : 0,
|
||||
"dynamicAddr" : 1
|
||||
},
|
||||
"upgradeAllowedAuto" : false
|
||||
}
|
||||
.ft P
|
||||
.fi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-SECURITY" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-SECURITY" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-STIGNORE" "5" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING-STIGNORE" "5" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "TODO" "7" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "TODO" "7" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
Todo \- Keep automatic backups of deleted files by other nodes
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING" "1" "November 09, 2015" "v0.12" "Syncthing"
|
||||
.TH "SYNCTHING" "1" "November 14, 2015" "v0.12" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.
|
||||
|
||||
@@ -32,6 +32,7 @@ var excludeCommits = stringSetFromStrings([]string{
|
||||
"4b76ec40c07078beaa2c5e250ed7d9bd6276a718",
|
||||
"32a76901a91ff0f663db6f0830e0aedec946e4d0",
|
||||
"3626003f680bad3e63677982576d3a05421e88e9",
|
||||
"342036408e65bd25bb6afbcc705e2e2c013bb01f",
|
||||
})
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestGetIndex(t *testing.T) {
|
||||
p := startInstance(t, 2)
|
||||
defer checkedStop(t, p)
|
||||
|
||||
// Check for explicint index.html
|
||||
// Check for explicit index.html
|
||||
|
||||
res, err := http.Get("http://localhost:8082/index.html")
|
||||
if err != nil {
|
||||
|
||||
@@ -220,7 +220,7 @@ func alterFiles(dir string) error {
|
||||
/*
|
||||
This fails. Bug?
|
||||
|
||||
// Rename the file, while potentially moving it up in the directory hiearachy
|
||||
// Rename the file, while potentially moving it up in the directory hierarchy
|
||||
case r == 4 && comps > 2 && (info.Mode().IsRegular() || rand.Float64() < 0.2):
|
||||
rpath := filepath.Dir(path)
|
||||
if rand.Float64() < 0.2 {
|
||||
|
||||
Reference in New Issue
Block a user