mirror of
https://github.com/syncthing/syncthing.git
synced 2025-12-23 22:18:14 -05:00
Merge branch 'main' into v2
* main: chore(fs): speed up case normalization (#10013) chore(config): remove discontinued secondary STUN servers (fixes #10011) (#10012) chore(gui, man, authors): update docs, translations, and contributors fix(stun): better error handling (ref #10008) (#10010) fix(config): remove discontinued primary STUN server (fixes #10008) (#10009) fix(gui): validate device ID in canonical form (fixes #7291) (#10006)
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -223,6 +223,7 @@ Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
|
||||
Marc Pujol (kilburn) <kilburn@la3.org>
|
||||
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
|
||||
marco-m <marco.molteni@laposte.net>
|
||||
Marcus B Spencer <marcus@marcusspencer.xyz>
|
||||
Marcus Legendre <marcus.legendre@gmail.com>
|
||||
Mario Majila <mariustshipichik@gmail.com>
|
||||
Mark Pulford (mpx) <mark@kyne.com.au>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Разрешени мрежи",
|
||||
"Alphabetic": "Азбучен ред",
|
||||
"Altered by ignoring deletes.": "Промяна чрез пренебрегване на премахваните елементи.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Винаги включено, когато вида на папката е „{{foldertype}}“.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Външна команда управлява версиите. Тя трябва да премахне файла от синхронизираната папка. Ако в пътя до приложението има интервали, то той трябва да бъде поставен в кавички.",
|
||||
"Anonymous Usage Reporting": "Анонимно отчитане на употреба",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Форматът на данните за анонимно отчитане на употреба е променен. Желаете ли да използвате него вместо стария?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Съдържание:",
|
||||
"Bugs": "Дефекти",
|
||||
"Cancel": "Отказ",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Не мож да бъде включено, ако вида на папката е „{{foldertype}}“.",
|
||||
"Changelog": "Дневник на промените",
|
||||
"Clean out after": "Почистване след",
|
||||
"Cleaning Versions": "Почистване на версии",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Erlaubte Netzwerke",
|
||||
"Alphabetic": "Alphabetisch",
|
||||
"Altered by ignoring deletes.": "Weicht ab, weil Löschungen ignoriert werden.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Immer eingeschaltet, wenn der Ordnertyp „{{foldertype}}“ ist.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Die Versionierung erfolgt über einen externen Befehl. Er muss die Datei aus dem geteilten Ordner entfernen. Wenn der Pfad zur Anwendung Leerzeichen enthält, sollte er in Anführungszeichen gesetzt werden.",
|
||||
"Anonymous Usage Reporting": "Anonymer Nutzungsbericht",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Nachrichtentext:",
|
||||
"Bugs": "Fehler",
|
||||
"Cancel": "Abbrechen",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kann nicht aktiviert werden, wenn der Ordnertyp „{{foldertype}}“ ist.",
|
||||
"Changelog": "Änderungsprotokoll",
|
||||
"Clean out after": "Löschen nach",
|
||||
"Cleaning Versions": "Versionen bereinigen",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Réseaux autorisés",
|
||||
"Alphabetic": "Alphabétique",
|
||||
"Altered by ignoring deletes.": "Protégé par \"Ignore Delete\".",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Toujours activé pour le type de partage \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.",
|
||||
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Corps du message :",
|
||||
"Bugs": "Bogues",
|
||||
"Cancel": "Annuler",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Ne peut être activé pour le type de partage \"{{foldertype}}\".",
|
||||
"Changelog": "Historique des versions",
|
||||
"Clean out after": "Conserver pendant",
|
||||
"Cleaning Versions": "Purge des versions",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Reti Consentite",
|
||||
"Alphabetic": "Alfabetico",
|
||||
"Altered by ignoring deletes.": "Modificato ignorando le eliminazioni.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre attivato quando il tipo di cartella è \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.",
|
||||
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Corpo:",
|
||||
"Bugs": "Bug",
|
||||
"Cancel": "Annulla",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Non può essere abilitato se il tipo di cartella è \"{{foldertype}}\".",
|
||||
"Changelog": "Registro modifiche",
|
||||
"Clean out after": "Svuota dopo",
|
||||
"Cleaning Versions": "Pulizia Versioni",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Dozwolone sieci",
|
||||
"Alphabetic": "Alfabetycznie",
|
||||
"Altered by ignoring deletes.": "Zmieniono przez ignorowanie usuniętych",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Zawsze włączone, gdy typ folderu to \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Zewnętrzne polecenie odpowiedzialne jest za wersjonowanie. Musi ono usunąć plik ze współdzielonego folderu. Jeżeli ścieżka do aplikacji zawiera spacje, to powinna ona być zamknięta w cudzysłowie.",
|
||||
"Anonymous Usage Reporting": "Anonimowe statystyki użycia",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Format anonimowych statystyk użycia uległ zmianie. Czy chcesz przejść na nowy format?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Treść:",
|
||||
"Bugs": "Błędy",
|
||||
"Cancel": "Anuluj",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Nie można włączyć, jeśli typem folderu jest \"{{foldertype}}\".",
|
||||
"Changelog": "Historia zmian",
|
||||
"Clean out after": "Opróżnij po",
|
||||
"Cleaning Versions": "Czyszczenie wersji",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabética",
|
||||
"Altered by ignoring deletes.": "Alterado ignorando exclusões.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre ativado quando o tipo de pasta for \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Um comando externo controla o controle de versão. Tem que remover o arquivo da pasta compartilhada. Se o caminho para o aplicativo contiver espaços, ele deve ser colocado entre aspas.",
|
||||
"Anonymous Usage Reporting": "Relatórios anônimos de uso",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anônimo de uso mudou. Gostaria de usar o formato novo?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Corpo:",
|
||||
"Bugs": "Erros",
|
||||
"Cancel": "Cancelar",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Não pode ser ativado se o tipo de pasta for \"{{foldertype}}\".",
|
||||
"Changelog": "Registro de alterações",
|
||||
"Clean out after": "Limpar depois de",
|
||||
"Cleaning Versions": "Limpando Versões",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Redes permitidas",
|
||||
"Alphabetic": "Alfabética",
|
||||
"Altered by ignoring deletes.": "Alterada por terem sido ignoradas as eliminações.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre ligado quando o tipo de pasta é \"{{foldertype}}\".",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Um comando externo gere as versões. Esse comando tem que remover o ficheiro da pasta partilhada. Se o caminho para a aplicação contiver espaços, então terá de o escrever entre aspas.",
|
||||
"Anonymous Usage Reporting": "Enviar relatórios anónimos de utilização",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anónimo de utilização foi alterado. Gostaria de mudar para o novo formato?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Corpo:",
|
||||
"Bugs": "Erros",
|
||||
"Cancel": "Cancelar",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Não pode ser habilitado quando o tipo de pasta é \"{{foldertype}}\".",
|
||||
"Changelog": "Registo de alterações",
|
||||
"Clean out after": "Esvaziar ao fim de",
|
||||
"Cleaning Versions": "Limpando versões",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "İzin Verilen Ağlar",
|
||||
"Alphabetic": "Alfabetik",
|
||||
"Altered by ignoring deletes.": "Silmeler yoksayılarak değiştirildi.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Klasör türü \"{{foldertype}}\" olduğunda her zaman açıktır.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Harici bir komut sürümlendirmeyi gerçekleştirir. Dosyayı paylaşılan klasörden kaldırmak zorundadır. Eğer uygulama yolu boşluklar içeriyorsa, tırnak içine alınmalıdır.",
|
||||
"Anonymous Usage Reporting": "İsimsiz Kullanım Bildirme",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "İsimsiz kullanım raporu biçimi değişti. Yeni biçime geçmek ister misiniz?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Gövde:",
|
||||
"Bugs": "Hatalar",
|
||||
"Cancel": "İptal",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Klasör türü \"{{foldertype}}\" olduğunda etkinleştirilemez.",
|
||||
"Changelog": "Değişiklik Günlüğü",
|
||||
"Clean out after": "Şundan sonra temizle",
|
||||
"Cleaning Versions": "Sürümleri Temizleme",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "允许的网络",
|
||||
"Alphabetic": "字母顺序",
|
||||
"Altered by ignoring deletes.": "通过忽略删除进行更改。",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "当文件夹类型为“{{foldertype}}”时始终启用。",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "外部命令处理版本控制。必须从共享文件夹中移除文件。如果应用程序的路径包含空格,应用半角引号括起来。",
|
||||
"Anonymous Usage Reporting": "匿名使用报告",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用报告格式已更改。是否要切换到新格式?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "正文:",
|
||||
"Bugs": "问题反馈",
|
||||
"Cancel": "取消",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "当文件夹类型为“{{foldertype}}”时无法启用。",
|
||||
"Changelog": "更新日志",
|
||||
"Clean out after": "在该时间后清除",
|
||||
"Cleaning Versions": "清理版本中",
|
||||
@@ -228,12 +230,12 @@
|
||||
"Listener Failures": "监听程序失败",
|
||||
"Listener Status": "监听程序状态",
|
||||
"Listeners": "监听程序",
|
||||
"Loading data...": "正在载入数据…",
|
||||
"Loading...": "正在载入…",
|
||||
"Loading data...": "正在加载数据…",
|
||||
"Loading...": "正在加载…",
|
||||
"Local Additions": "本地添加",
|
||||
"Local Discovery": "本地发现",
|
||||
"Local State": "本地状态",
|
||||
"Local State (Total)": "本地状态汇总",
|
||||
"Local State (Total)": "本地状态(总计)",
|
||||
"Locally Changed Items": "本地更改的项目",
|
||||
"Log": "日志",
|
||||
"Log File": "日志文件",
|
||||
@@ -481,11 +483,11 @@
|
||||
"Untrusted": "不受信任",
|
||||
"Up to Date": "最新",
|
||||
"Updated {%file%}": "已更新 {{file}}",
|
||||
"Upgrade": "更新",
|
||||
"Upgrade To {%version%}": "升级至版本 {{version}}",
|
||||
"Upgrade": "升级",
|
||||
"Upgrade To {%version%}": "升级到 {{version}}",
|
||||
"Upgrading": "升级中",
|
||||
"Upload Rate": "上传速率",
|
||||
"Uptime": "启动时间",
|
||||
"Uptime": "运行时间",
|
||||
"Usage reporting is always enabled for candidate releases.": "发布候选版始终启用使用报告。",
|
||||
"Use HTTPS for GUI": "使用 HTTPS 连接到 GUI",
|
||||
"Use notifications from the filesystem to detect changed items.": "使用来自文件系统的通知来检测更改的项目。",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<h4 class="text-center" translate>The Syncthing Authors</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Anatoli Babenia, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, DerRockWolf, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Gusted, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Luke Hamburg, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Taylor Khan, Terrance, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tim Nordenfur, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, WangXi, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cjc7373, cui fliter, d-volution, dashangcun, derekriemer, desbma, diemade, digital, entity0xfe, georgespatton, ghjklw, guangwu, gudvinr, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, kylosus, luchenhan, luzpaz, marco-m, mathias4833, maxice8, mclang, mv1005, nf, orangekame3, otbutz, overkill, perewa, polyfloyd, red_led, rubenbe, sec65, vapatel2, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
|
||||
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ross Smith II, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Anatoli Babenia, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, DerRockWolf, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Gusted, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Luke Hamburg, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Taylor Khan, Terrance, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tim Nordenfur, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, WangXi, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cjc7373, cui fliter, d-volution, dashangcun, derekriemer, desbma, diemade, digital, entity0xfe, georgespatton, ghjklw, guangwu, gudvinr, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, kylosus, luchenhan, luzpaz, marco-m, mathias4833, maxice8, mclang, mv1005, nf, orangekame3, otbutz, overkill, perewa, polyfloyd, red_led, rubenbe, sec65, vapatel2, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,18 +5,12 @@ angular.module('syncthing.core')
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
$http.get(urlbase + '/svc/deviceid?id=' + viewValue).success(function (resp) {
|
||||
if (resp.error) {
|
||||
ctrl.$setValidity('validDeviceid', false);
|
||||
} else {
|
||||
ctrl.$setValidity('validDeviceid', true);
|
||||
}
|
||||
let isValid = !resp.error;
|
||||
let isUnique = !isValid || !scope.devices.hasOwnProperty(resp.id);
|
||||
|
||||
ctrl.$setValidity('validDeviceid', isValid);
|
||||
ctrl.$setValidity('unique', isUnique);
|
||||
});
|
||||
//Prevents user from adding a duplicate ID
|
||||
if (scope.devices.hasOwnProperty(viewValue)) {
|
||||
ctrl.$setValidity('unique', false);
|
||||
} else {
|
||||
ctrl.$setValidity('unique', true);
|
||||
}
|
||||
return viewValue;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,22 +71,19 @@ var (
|
||||
|
||||
// DefaultPrimaryStunServers are servers provided by us (to avoid causing the public servers burden)
|
||||
DefaultPrimaryStunServers = []string{
|
||||
"stun.syncthing.net:3478",
|
||||
// Discontinued because of misuse. See https://forum.syncthing.net/t/stun-server-misuse/23319
|
||||
//"stun.syncthing.net:3478",
|
||||
}
|
||||
DefaultSecondaryStunServers = []string{
|
||||
"stun.callwithus.com:3478",
|
||||
"stun.counterpath.com:3478",
|
||||
"stun.counterpath.net:3478",
|
||||
"stun.ekiga.net:3478",
|
||||
"stun.hitv.com:3478",
|
||||
"stun.ideasip.com:3478",
|
||||
"stun.internetcalls.com:3478",
|
||||
"stun.miwifi.com:3478",
|
||||
"stun.schlund.de:3478",
|
||||
"stun.sipgate.net:10000",
|
||||
"stun.sipgate.net:3478",
|
||||
"stun.voip.aebc.com:3478",
|
||||
"stun.voiparound.com:3478",
|
||||
"stun.voipbuster.com:3478",
|
||||
"stun.voipstunt.com:3478",
|
||||
"stun.xten.com:3478",
|
||||
|
||||
@@ -17,6 +17,52 @@ import (
|
||||
// UnicodeLowercaseNormalized returns the Unicode lower case variant of s,
|
||||
// having also normalized it to normalization form C.
|
||||
func UnicodeLowercaseNormalized(s string) string {
|
||||
if isASCII, isLower := isASCII(s); isASCII {
|
||||
if isLower {
|
||||
return s
|
||||
}
|
||||
return toLowerASCII(s)
|
||||
}
|
||||
|
||||
return toLowerUnicode(s)
|
||||
}
|
||||
|
||||
func isASCII(s string) (bool, bool) {
|
||||
isLower := true
|
||||
for _, b := range []byte(s) {
|
||||
if b > unicode.MaxASCII {
|
||||
return false, isLower
|
||||
}
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
isLower = false
|
||||
}
|
||||
}
|
||||
return true, isLower
|
||||
}
|
||||
|
||||
func toLowerASCII(s string) string {
|
||||
var (
|
||||
b strings.Builder
|
||||
pos int
|
||||
)
|
||||
b.Grow(len(s))
|
||||
for i, c := range []byte(s) {
|
||||
if c < 'A' || 'Z' < c {
|
||||
continue
|
||||
}
|
||||
if pos < i {
|
||||
b.WriteString(s[pos:i])
|
||||
}
|
||||
pos = i + 1
|
||||
b.WriteByte(c + 'a' - 'A')
|
||||
}
|
||||
if pos != len(s) {
|
||||
b.WriteString(s[pos:])
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func toLowerUnicode(s string) string {
|
||||
i := firstCaseChange(s)
|
||||
if i == -1 {
|
||||
return norm.NFC.String(s)
|
||||
@@ -30,7 +76,11 @@ func UnicodeLowercaseNormalized(s string) string {
|
||||
rs.WriteString(s[:i])
|
||||
|
||||
for _, r := range s[i:] {
|
||||
rs.WriteRune(unicode.ToLower(unicode.ToUpper(r)))
|
||||
if r <= unicode.MaxLatin1 && r != 'µ' {
|
||||
rs.WriteRune(unicode.ToLower(r))
|
||||
} else {
|
||||
rs.WriteRune(unicode.To(unicode.LowerCase, unicode.To(unicode.UpperCase, r)))
|
||||
}
|
||||
}
|
||||
return norm.NFC.String(rs.String())
|
||||
}
|
||||
@@ -38,10 +88,13 @@ func UnicodeLowercaseNormalized(s string) string {
|
||||
// Byte index of the first rune r s.t. lower(upper(r)) != r.
|
||||
func firstCaseChange(s string) int {
|
||||
for i, r := range s {
|
||||
if r <= unicode.MaxASCII && (r < 'A' || r > 'Z') {
|
||||
continue
|
||||
if r <= unicode.MaxASCII {
|
||||
if r < 'A' || r > 'Z' {
|
||||
continue
|
||||
}
|
||||
return i
|
||||
}
|
||||
if unicode.ToLower(unicode.ToUpper(r)) != r {
|
||||
if unicode.To(unicode.LowerCase, unicode.To(unicode.UpperCase, r)) != r {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,18 @@ var caseCases = [][2]string{
|
||||
{"a\xCC\x88", "\xC3\xA4"}, // ä
|
||||
}
|
||||
|
||||
var benchmarkCases = [][2]string{
|
||||
{"img_202401241010.jpg", "ASCII lowercase"},
|
||||
{"IMG_202401241010.jpg", "ASCII mixedcase start"},
|
||||
{"img_202401241010.JPG", "ASCII mixedcase end"},
|
||||
{"wir_kinder_aus_bullerbü.epub", "Latin1 lowercase"},
|
||||
{"Wir_Kinder_aus_Bullerbü.epub", "Latin1 mixedcase start"},
|
||||
{"wir_kinder_aus_bullerbü.EPUB", "Latin1 mixedcase end"},
|
||||
{"translated_ウェブの国際化.html", "Unicode lowercase"},
|
||||
{"Translated_ウェブの国際化.html", "Unicode mixedcase start"},
|
||||
{"translated_ウェブの国際化.HTML", "Unicode mixedcase end"},
|
||||
}
|
||||
|
||||
func TestUnicodeLowercaseNormalized(t *testing.T) {
|
||||
for _, tc := range caseCases {
|
||||
res := UnicodeLowercaseNormalized(tc[0])
|
||||
@@ -58,22 +70,13 @@ func TestUnicodeLowercaseNormalized(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnicodeLowercaseMaybeChange(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range caseCases {
|
||||
UnicodeLowercaseNormalized(s[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnicodeLowercaseNoChange(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range caseCases {
|
||||
UnicodeLowercaseNormalized(s[1])
|
||||
}
|
||||
func BenchmarkUnicodeLowercase(b *testing.B) {
|
||||
for _, c := range benchmarkCases {
|
||||
b.Run(c[1], func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
UnicodeLowercaseNormalized(c[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ package stun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@@ -38,6 +40,8 @@ const (
|
||||
NATSymmetricUDPFirewall = stun.NATSymmetricUDPFirewall
|
||||
)
|
||||
|
||||
var errNotPunchable = errors.New("not punchable")
|
||||
|
||||
type Subscriber interface {
|
||||
OnNATTypeChanged(natType NATType)
|
||||
OnExternalAddressChanged(address *Host, via string)
|
||||
@@ -110,10 +114,11 @@ func (s *Service) Serve(ctx context.Context) error {
|
||||
l.Debugf("Starting stun for %s", s)
|
||||
|
||||
for _, addr := range s.cfg.Options().StunServers() {
|
||||
// This blocks until we hit an exit condition or there are issues with the STUN server.
|
||||
// This returns a boolean signifying if a different STUN server should be tried (oppose to the whole thing
|
||||
// shutting down and this winding itself down.
|
||||
s.runStunForServer(ctx, addr)
|
||||
// This blocks until we hit an exit condition or there are
|
||||
// issues with the STUN server.
|
||||
if err := s.runStunForServer(ctx, addr); errors.Is(err, errNotPunchable) {
|
||||
break // we will sleep for a while
|
||||
}
|
||||
|
||||
// Have we been asked to stop?
|
||||
select {
|
||||
@@ -129,11 +134,6 @@ func (s *Service) Serve(ctx context.Context) error {
|
||||
s.setExternalAddress(nil, "")
|
||||
goto disabled
|
||||
}
|
||||
|
||||
// Unpunchable NAT? Chillout for some time.
|
||||
if !s.isCurrentNATTypePunchable() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// We failed to contact all provided stun servers or the nat is not punchable.
|
||||
@@ -142,7 +142,7 @@ func (s *Service) Serve(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) runStunForServer(ctx context.Context, addr string) {
|
||||
func (s *Service) runStunForServer(ctx context.Context, addr string) error {
|
||||
l.Debugf("Running stun for %s via %s", s, addr)
|
||||
|
||||
// Resolve the address, so that in case the server advertises two
|
||||
@@ -153,7 +153,7 @@ func (s *Service) runStunForServer(ctx context.Context, addr string) {
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
l.Debugf("%s stun addr resolution on %s: %s", s, addr, err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
s.client.SetServerAddr(udpAddr.String())
|
||||
|
||||
@@ -163,15 +163,18 @@ func (s *Service) runStunForServer(ctx context.Context, addr string) {
|
||||
natType, extAddr, err = s.client.Discover()
|
||||
return err
|
||||
})
|
||||
if err != nil || extAddr == nil {
|
||||
l.Debugf("%s stun discovery on %s: %s", s, addr, err)
|
||||
return
|
||||
if err != nil {
|
||||
l.Debugf("%s stun discovery on %s: %v", s, addr, err)
|
||||
return err
|
||||
} else if extAddr == nil {
|
||||
l.Debugf("%s stun discovery on %s resulted in no address", s, addr)
|
||||
return fmt.Errorf("%s: no address", addr)
|
||||
}
|
||||
|
||||
// The stun server is most likely borked, try another one.
|
||||
if natType == NATError || natType == NATUnknown || natType == NATBlocked {
|
||||
l.Debugf("%s stun discovery on %s resolved to %s", s, addr, natType)
|
||||
return
|
||||
return fmt.Errorf("%s: bad result: %v", addr, natType)
|
||||
}
|
||||
|
||||
s.setNATType(natType)
|
||||
@@ -181,15 +184,14 @@ func (s *Service) runStunForServer(ctx context.Context, addr string) {
|
||||
// and such, just let the caller check the nat type and work it out themselves.
|
||||
if !s.isCurrentNATTypePunchable() {
|
||||
l.Debugf("%s cannot punch %s, skipping", s, natType)
|
||||
return
|
||||
return errNotPunchable
|
||||
}
|
||||
|
||||
s.setExternalAddress(extAddr, addr)
|
||||
|
||||
s.stunKeepAlive(ctx, addr, extAddr)
|
||||
return s.stunKeepAlive(ctx, addr, extAddr)
|
||||
}
|
||||
|
||||
func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host) {
|
||||
func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host) error {
|
||||
var err error
|
||||
nextSleep := time.Duration(s.cfg.Options().StunKeepaliveStartS) * time.Second
|
||||
|
||||
@@ -211,7 +213,7 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host)
|
||||
minSleep := time.Duration(s.cfg.Options().StunKeepaliveMinS) * time.Second
|
||||
if nextSleep < minSleep {
|
||||
l.Debugf("%s keepalive aborting, sleep below min: %s < %s", s, nextSleep, minSleep)
|
||||
return
|
||||
return fmt.Errorf("unreasonably low keepalive: %v", minSleep)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,13 +240,13 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host)
|
||||
case <-time.After(sleepFor):
|
||||
case <-ctx.Done():
|
||||
l.Debugf("%s stopping, aborting stun", s)
|
||||
return
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
if s.cfg.Options().IsStunDisabled() {
|
||||
// Disabled, give up
|
||||
l.Debugf("%s disabled, aborting stun ", s)
|
||||
return
|
||||
return errors.New("disabled")
|
||||
}
|
||||
|
||||
// Check if any writes happened while we were sleeping, if they did, sleep again
|
||||
@@ -259,7 +261,7 @@ func (s *Service) stunKeepAlive(ctx context.Context, addr string, extAddr *Host)
|
||||
extAddr, err = s.client.Keepalive()
|
||||
if err != nil {
|
||||
l.Debugf("%s stun keepalive on %s: %s (%v)", s, addr, err, extAddr)
|
||||
return
|
||||
return err
|
||||
}
|
||||
ourLastWrite = time.Now()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user